Solved

Resize a Repeating Section After Hiding Panels inside the Section


Userlevel 4
Badge +10

Hi Folks,

I am building a form with a repeating section that has two panels (aligned vertically). The first panel is visible by default. The second panel should only appear later in the approval process. My problem is that there is a great deal of whitespace visible when the second panel is hidden. I have seen some old posts where the request was made in the uservoice forum to add a resize feature.

 

Does anyone know of any way to accomplish this yet?

 

I have seen you, @MegaJerk, popping up in many of the repeating section posts and was hoping that you had some guidance for me. I will say at the outset that I am not strong in JS but with a little guidance, I can usually get posted code to work if JS is needed for this.

 

Thanks and Regards,

Patrick

icon

Best answer by MegaJerk 12 August 2019, 17:16

View original

15 replies

Userlevel 5
Badge +14

A few questions. 

1. Is this a Classic or Responsive Form? 

2. Can you provide a Screenshot of the Form in Design Mode and one in Edit Mode (usage mode) so that I can see explicitly how it's laid out? 



Warning. It will indeed require a lot of JavaScript, but because I have so much customer Repeating Section stuff happening in my world, it shouldn't be that difficult for me to find some easy to drop in code that will accomplish what you're looking to do (assuming it's similar enough to the things that I'm already doing! ;)) 

Let's get cookin! 

Userlevel 4
Badge +10

Hi @MegaJerk!


 


 Clasic Foms.


 


Here is the forms section in question in Design Mode:



...and here is a screenshot of the form in Edit mode qwith both panels visible:



...and here is the edit mode with the second panel hidden.



 


Thanks so much for assisting with this!


Best Regards,


Patrick


 


 

Userlevel 5
Badge +14

I realized that I had one more question that needs to be answered. 

Is the condition that would make the Panel Visible / Invisible something that happens in real time, or is it something that is set outside of the form (say, via a Workflow)?

In otherwords, when the Form is loaded, do I need to worry about only Showing or Hiding it there, or should I worry about Showing it and Hiding because someone selected something specific on a triggering Control in the same Form? 


 


 

Userlevel 4
Badge +10

Hi @MegaJerk


 


The condition would be a vlaue in a column of the list. We call it WorkflowState. It wiould be a value set by default on a new form (NEW_STATE), or by a workflow (PM_STATE, PURCHASE_STATE, HOLD_STATE, END_STATE, etc.). This is a supply/purchase request process and in the case of our form, we want the second panel that pertains to "Post Receipt" information to show up only after the request has been approved and the items are ready to be or have been ordered by the supply team.


 


Thanks!


Patrick

Userlevel 5
Badge +14

This should be pretty straight forward, though I am unsure of how your process fully works, if you have information that you'd like to show on the form only AFTER a certain state has been reached, then the below solution should do the trick. 


 


The Setup 


 



 


My form has a Repeating Section, inside of which are Two Panels (named Panel 1 and Panel 2 respectively).


 


Panel 2 has been given a CSS Class of "ReceiptPanel": 



(Note: Do not forget to add this CSS rule as it is critical


 


Next to the Repeating Section is a Label Control (reading 'Current State:') and a Choice Control associated to a List Column that I have named WorkflowState. The Workflow State Control is set to Visible: Yes // Enabled: No! It is just to show which state the Column Value currently has in it! 


 


The List Column (WorkflowState) is setup in the following way: 



 


 


The Rules


 


There is only one (1) Rule, and it is a Formatting Rule on Panel 2:


 



 


The Rule should contain the following code: 


(function(workflowState){
switch (workflowState) {
case "State 1":
case "State 2":
case "State 3":
return true;
default:
return false;
}
}(WorkflowState));

(Note: at the BOTTOM of that code, the word "WorkflowState" should be replaced by the actual Reference to the List Column in question. As you can see in the image, it is shown with Red Text as opposed to just being regular typed text!) 


 


All this code does is Hide the panel whenever the value of WorkflowState matches the value "State 1", "State 2", or "State 3" as indicated by the case statements. If it doesn't, then it will default to false, and the Panel will be Shown. 


 


Because you can add new handled cases relatively easily, in the event that I were to add a new Choice for my Column, I by adding a new case to the Rule, it can be accounted for without too much work. 


 


The JavaScript


 


The time has come for the JavaScript! 

How you add this to your form is entirely up to you. You can either drop it into the Custom Javascript portion of the Settings, or you can save it to a file and reference it in the Advanced -> Custom JavaScript Includes URL referencer. Either way, this includes most (but not all) of the Repeating Section Boilerplate that I use to handle all things Repeating Sections for my form in a common Namespace (NCU). 


 


There are plans to eventually release all of the Javascript functions and tools that I have built under this common library name, but I still have a lot of testing to do. Most of everything that is being done in the code is heavily commented so that you should be able to read what it does, but if you need additional explanations, feel free to ask. 


/* global NWF$, NWF, NF, outerDiv, ValidatorOnLoad, _spPageContextInfo */
/* eslint-env browser */
/* eslint no-console: ["error", { allow: ["log", "error"] }] */

/*global
NWF$, NWF, NF, outerDiv, ValidatorOnLoad, _spPageContextInfo
*/

/* Tabbed Form code */


/*
We're going to create a convenient Namespace to store all of our
Form Variables and Functions in, so that we can keep those out of the
Global Scope
*/

var NCU = (function (NCU) {
"use strict";

NCU.FormVariables = (function (obj) {
obj.hiddenRepeaterPanelClasses = ["ReceiptPanel"];
obj.pageIsReady = false;
return obj;
}(NCU.FormVariables || {}));

/*
Now we'll set up our FormFunctions property which will contain
all of our custom Functions.
*/
NCU.FormFunctions = (function (obj) {

/*
This fixes the incorrect way that Nintex Forms increase / decrease the
height of the Form Canvas. By leaving in the FormFiller Events below,
all instances where the height would have a chance to grow out of control
in either direction, should be fixed
*/
obj.setCanvasContainerHeight = function () {
if (outerDiv.data("outerDivHeight") !== outerDiv.height()) {
outerDiv.outerHeight(outerDiv.height());
outerDiv.data("outerDivHeight", outerDiv.height());
}
};

obj.rebuildHiddenRepeaterSections = function (eventRow) {
var thisRepeatingSection = eventRow.closest("[data-controlname]");
var thisFormControlID = thisRepeatingSection.attr("formcontrolID");
var formFillerDivCurrent = NWF.FormFiller.Functions.GetFormFillerDiv();
var siblingRepeatingSection = formFillerDivCurrent.find(".nf-repeater:hidden").closest("[data-controlname]").not("[formcontrolid='" + thisFormControlID + "']");
siblingRepeatingSection.each(function (index, section) {

var thisSiblingSection = NWF$(section);
var thisRepeaterControl = thisSiblingSection.find(".nf-repeater");
var thisRepeaterInnerControl = thisRepeaterControl.parent();
var thisSiblingSectionRows = thisSiblingSection.find(".nf-repeater-row:not(.nf-repeater-row-hidden)");

var totalRowHeight = 0;
thisSiblingSectionRows.each(function (index, row) {
totalRowHeight += NWF$(row).outerHeight();
});

if (totalRowHeight > thisRepeaterControl.height()) {
thisRepeaterControl.height(totalRowHeight);
}

thisRepeaterInnerControl.height(thisRepeaterControl.height());

if (thisSiblingSection[0].className.indexOf('nf-error-highlight') != -1) {
thisSiblingSection.outerHeight(thisRepeaterControl.outerHeight() + 4);
} else {
thisSiblingSection.outerHeight(thisRepeaterControl.outerHeight());
}
});
};

obj.RepositionAndResizeOtherControlsAndFillerContainerHeight = (NWF.FormFiller.Functions.RepositionAndResizeOtherControlsAndFillerContainerHeight || NWF.FormFiller.Resize.RepositionAndResizeOtherControlsAndFillerContainerHeight);

obj.hideRepeatingSectionPanels = function (panelClassArray) {

if (NWF$.isArray(panelClassArray)) {

NWF$.each(panelClassArray, function (index, panelClass) {

if (panelClass) {

if (panelClass.match(/^./) === null) {
panelClass = "." + panelClass;
}

NWF$(panelClass + ".nf-filler-control").each(function (index, targetPanel) {
/* Get the current Panel that we're iterating over */
targetPanel = NWF$(targetPanel);

/* Get the current Row that the Panel is in */
var currentRow = targetPanel.closest(".nf-repeater-row");

/* If the Panel is NOT visible (because it was hidden by a RULE) */
if (!(targetPanel.is(":visible"))) {

/* Get the Outermost Height of the Panel */
var panelHeight = targetPanel.outerHeight();

/* If the CURRENT ROW is not the HIDDEN ROOT ROW, then we need resize our Canvas / Form */
/* Because the Panel was actually taking up space there! */
if (!currentRow.hasClass("nf-repeater-row-hidden")) {
obj.RepositionAndResizeOtherControlsAndFillerContainerHeight(currentRow, -panelHeight, -panelHeight, NWF$("#formFillerDiv"));
}

/* Then we take the Height out of the Row itself (because RS Rows do not auto resize) */
currentRow.outerHeight(currentRow.outerHeight() - panelHeight);
}
});
}
});
}
};

return obj;
}(NCU.FormFunctions || {}));

/* Added Prototypes */
String.prototype.format = function () {
var s = String(this);
var i = arguments.length;

while (i--) {
s = s.replace(new RegExp("\{" + i + "\}", "gm"), arguments[i]);
}
return s;
};



/*
Validator Rules can sometimes become orphaned when a New Row is added.
If the function exists which can reattach them to their associated Controls,
this will make sure that it runs every time so that every row can be validated in real time.
*/
if (typeof ValidatorOnLoad !== "undefined") {
NWF.FormFiller.Events.RegisterRepeaterRowAdded(function () {
ValidatorOnLoad();
});
}

/*
The below FormFiller.Events are in place to handle how the Form will
incorrectly set the Form Height as Controls are Hidden and Shown
via the Rule System and other interactions.
*/
NWF.FormFiller.Events.RegisterRepeaterRowAdded(function (thisRow) {
NCU.FormFunctions.rebuildHiddenRepeaterSections(thisRow);
NCU.FormFunctions.setCanvasContainerHeight();
});

NWF.FormFiller.Events.RegisterRepeaterRowDeleted(function (thisRow) {
NCU.FormFunctions.rebuildHiddenRepeaterSections(thisRow);
NCU.FormFunctions.setCanvasContainerHeight();
});

NWF.FormFiller.Events.RegisterControlShowHidePropagated(function () {
if (arguments[0].data("RepositionControls")) {
NCU.FormFunctions.setCanvasContainerHeight();
}
});

NWF.FormFiller.Events.RegisterControlHeightChangePropagated(function () {
NCU.FormFunctions.setCanvasContainerHeight();
});

NWF.FormFiller.Events.RegisterBeforeReady(function () {
if (NF.BaseDataAccessHelper === undefined) {
try {
console.log("Attempting to load missing js file: 'NF.BaseDataAccessHelper.js'");
NWF$.getScript(_spPageContextInfo.siteAbsoluteUrl.replace(/sites.+/, "") + _spPageContextInfo.layoutsUrl + "/NintexForms/JavaScriptStringHandler.ashx?" + "resourceType=jsfile&" + "fileName=NF.BaseDataAccessHelper.js&" + "culture=" + _spPageContextInfo.currentCultureName);
} catch (e) {
console.log("There was a problem grabbing the BaseDataAccessHelper using the JavaScriptStringHandler!");
console.log(e);
}
}

outerDiv.outerHeight(outerDiv.height());
});

/*
When the Form loads, but before it's visible, we need make sure that the Repeating Section
is correctly sized. Specifically, we need to make sure that the Panel where information *might*
reside on a Task form but not on an Edit Form, has been hidden and that its height has been
subtracted from the rows it is contained in, as well as the canvas.
The following For Each loop will do just that...
*/
NWF.FormFiller.Events.RegisterBeforeFillerVisible(function() {
NCU.FormFunctions.hideRepeatingSectionPanels(NCU.FormVariables.hiddenRepeaterPanelClasses);
});

NWF.FormFiller.Events.RegisterAfterReady(function () {
outerDiv.data("outerDivHeight", outerDiv.height());
NCU.FormVariables.pageIsReady = true;
});

return NCU;
}(NCU || {}));

The ONLY thing you should need to change will be at the top of the function in the FormVariables section where you'll see the following bit of code: 


NCU.FormVariables = (function (obj) {  
obj.hiddenRepeaterPanelClasses = ["ReceiptPanel"];
obj.pageIsReady = false;
return obj;
}(NCU.FormVariables || {}));

Notice how the value of obj.hiddenRepeaterPanelClasses is an Array containing a string with the Value of "ReceiptPanel", the same as the CSS Class we gave to our Panel? If you change the CSS Class name of that panel you will absolutely need to change the code to mirror whatever name  you decide to give the class!!!!

Failure to do so will result in your Repeating Section no longer resizing correctly! 



The Results


 


Now that you have put everything in its place, we can test to see if it works. 

Creating a new Item with a WorkflowState of "State 1" will present the form to us as shown: 



(Note: I have added a few rows so that you can see it's working


 


If we were to change the value of WorkflowState to something like "State 4", "State 5", or "State 6", the form will present to us as: 



 


The Catch


 


There are a few limitations to what I have presented here. One of the biggest being, if you were to put the Form into a state where the second panel was Shown, but you added a new Row, it would still mess up the Resizing. 

While I have some ideas on how to solve that, I am currently too busy to immediately produce anything useful, so it would likely take a little time if it is indeed something you all do. 


 


If it isn't, then you may want to make sure that you're disabling the ability to Add / Delete new Rows once you reach certain states. 


I hope that this helps to put you on the path of solving your problem. 


 

Userlevel 4
Badge +10

Hi @MegaJerk,


 


It worked and was a very simple set of instructions for me to follow! I did have a bit of an issue with our footer (corporate branding package) overlaying the first panel on my first test but corrected when I manually changed the state. I will need to do a more testing and possibly deactivate the branding package on this site. Not a big deal.


 


I will spensd some time looking over the code to see if I have any questions on the other things you included in your code.


YOU ROCK!


 


Thanks and Best Regards,


Patrick

Userlevel 4
Badge +10

Hello again @MegaJerk,


 


My colleague, @gbone02, and I are implementing this solution and we ran across a bit of a glitch. First of all, we have a branding package that the company has deployed across the platform and the footer is somtimes rendering over-top of the form content on the tab that conrtains the repeating section we are controling. There is also a panel at the bottom of the form template that has no rules assigned to it, so it should show up on all tabs at the bottom. It too, is rendering overlaid on the repeating sevion. I have posted a screenshot of the issue. Do you have any thoughts or ideas on how to force the content to render in order?



 


Thanks and Regards,


Patrick

Userlevel 5
Badge +14

I'd have to take a more in-depth look at your form to be of any help at this point.


 


Without an export, my guess is that when you change tabs, there is a brief moment (before my code kicks in) where the Section is calculated in an erroneous way (probably at 0 height because of the aforementioned errors in how Nintex Form calculates RS Height on Hide). Because of this, the height of everything is made smaller than it should be, and the Footer repositions in a way that is minus the Height of the Repeating Section. Immediately afterwards though, my code kicks in and corrects the Height of the Repeating Section and subsequent sibling items. I do not know however if that would cascade out to all of the siblings beyond the Panel the RS is contained inside of (IE: Any Control outside of the Panel). 

One way to approach a solution to this problem might be to just throw the Footer into its own panel (if it isn't already) and set some hard positioning logic on it any time a Tab has been changed and the Tab Panel has had its Height adjusted. This way when the Tab Panel is adjusted appropriately, you can just say something along the lines of: 


NWF$([data-controlnam='FooterPanel']).css({top: TabPanel.position.top + TabPanel.outerHeight() + 4}); 

This is just a Rough Example, but it's mostly what I think will work. 

Userlevel 4
Badge +10

Hi @MegaJerk! Thanks for responding.


 


Is there a way I can send you the list template? I do not want to post it here publicly as there are possibly too many company URL references built in.


 


The footer is part of a farm-deployed branding package that I have no control over so it renders independent of my form layout I think. I can turn off the branding package on my site collection from the Solutions Gallery but I would like to avoid that if I can. The issue still happens for the other panel I mentioned so I will see if I can get your code sample to work with the "Current State" control from the screen shot.


 


Thanks and Regards,


Patrick

Userlevel 4
Badge +10

Hi @MegaJerk,


I added the line of code you suggested but I am not sure I added it in the correct place in the Custom JavaScript window. The footer renders correctly but then the


 


Here is what I pased in below the previous code you posted...


NWF$([data-controlname='FooterPanel']).css({top: TabPanel.position.top + TabPanel.outerHeight() + 4}); 

You may notice I added an "e" at the end of "controlname" as I assumed it was a typo. I put the line "after" all of the orther code you posted. Sorry if this was in error as I am not great with JS. The end of my code block looks like this...


/*  
When the Form loads, but before it's visible, we need make sure that the Repeating Section
is correctly sized. Specifically, we need to make sure that the Panel where information *might*
reside on a Task form but not on an Edit Form, has been hidden and that its height has been
subtracted from the rows it is contained in, as well as the canvas.
The following For Each loop will do just that...
*/
NWF.FormFiller.Events.RegisterBeforeFillerVisible(function() {
NCU.FormFunctions.hideRepeatingSectionPanels(NCU.FormVariables.hiddenRepeaterPanelClasses);
}); NWF.FormFiller.Events.RegisterAfterReady(function () {
outerDiv.data("outerDivHeight", outerDiv.height());
NCU.FormVariables.pageIsReady = true;
}); return NCU;
}(NCU || {}));

NWF$([data-controlname='FooterPanel']).css({top: TabPanel.position.top + TabPanel.outerHeight() + 4});

Additionally, I changed the name of the panel at the bottom of the form with the "Current State" controls and the Update button to "FooterPanel". I also named the CSS Class the same just in case that is what you meant. :)


 


 

Userlevel 5
Badge +14

I mean. I just posted that as an 'off the top of my head' idea about what would need to be done, and it was not something that I had implemented or tested (I posted it before we had our more indepth conversation). 

Ultimately it would need to be placed inside of *some* event that fires every time the form Controls get repositioned because, I'd imagine, the Footer would need to move based off of the current size of the Form Canvas (which is dynamic because it's constantly resizing to fit the contents which are being shown / hidden). 

Long story short: that's why it had a typo 😉 It was completely just an idea! 

Badge +2

Hi @MegaJerk thanks for the contribution, I tried this solution with O365 but I only get the page not to be rendered at all. Is there is a way to make this resize work on O365? Any help is appreciated.

Userlevel 5
Badge +14

I have never used O365 in any capacity unfortunately, so I have no idea of what could help you to solve your problem. I'm not entirely even sure what 'type' of Nintex Form is available for that platform. Do they have classic with javascript customization?

Additionally this is a topic that I will need to come back to at a later date (discussed privately already with OP), as I am currently too busy with work to create a solution that is more dynamic. 

For now I would recommend starting a new topic, but at some point I'll be able to come back to this and cook something up that might be useful. 

Badge +2

Hi @MegaJerk  thanks for the quick response.


 


O365 does provide classic form design with javascript, in fact some solutions posted at this board have help me with O365 issues. Thanks anyway I'll post my question on the proper board, goodbye and take care.

Badge +1
Hi @MegaJerk, I hope you are still around. I'm having this exact same issue but my show/hide condition is based on a real time action inside the form (A checkbox being ticked), is the solution different to what you explain in this thread? Is there an easier implementation for this dynamic resizing?

Reply