This rely was originally created as a Private Message, but since many of the concepts may prove helpful to anyone browsing the forum or looking to implement something similar, I asked that the messages be made public so that I could post this reply for the community.
Beyond that context, it just answers the question @kelliganp posed above in regards to setting a Control's value just before the form is Saved and Submitted to SharePoint proper.
Enjoy!
-----------------------
Good to see that you are getting down to your own code! It's always exciting when things start to fall into place. Below I will provide an example to you of how I would go about accomplishing what it is you're trying to do.
Note: In this example I do not put any code at the end of the Custom Javascript as you have described because it can really be self contained. While you didn't ask about that specifically I thought I would address it here before we dig in!
Moving on...
Form Setup
Here is an example form that I have created:
Here are the settings for each Control from top to bottom excluding "Title" (which is default)
ReportDate_DTE:
Frequency_CHO:
CCRName_SLT:
I have create (1) Rule for the CCRName control which automatically sets the control to be Disabled no matter what:
Last but not least, while it looks like a normal Save and Submit button I have actually created a new one, and customized a few of the advanced options:
The Client Click Formula will be the code that we use to set the CCRName control, and is as follows:
(function(event) {
if (NWF$("#" + jsvar_CCRName_SLT).val()) {
return;
}
function formatDatePart(myNum) {
myNum = String(myNum);
if (myNum.length > 1) {
return myNum
} else {
return "0" + myNum;
}
}
var reportDate = new Date(NWF$("#" + jsvar_ReportDate_DTE).val());
var reportDateParts = [reportDate.getFullYear(), reportDate.getMonth() + 1, reportDate.getDate()];
/* var reportDateParts = [jsvar_ReportDate_DTE.getFullYear(), jsvar_ReportDate_DTE.getMonth() + 1, jsvar_ReportDate_DTE.getDate()]; */
var reportDate = reportDateParts.map(formatDatePart).join("");
var curDate = new Date();
var dateParts = [curDate.getFullYear(), curDate.getMonth() + 1, curDate.getDate()];
var timeParts = [curDate.getHours(), curDate.getMinutes(), curDate.getSeconds()];
var dateTime = dateParts.map(formatDatePart).join("-") + timeParts.map(formatDatePart).join("-");
var frequencyCHO = NWF$("#" + jsvar_Frequency_CHO).val();
var ccrName = reportDate + " - Consolidated CDRL Report (" + frequencyCHO + ") - " + dateTime;
/* var ccrName = reportDate + " - Consolidated CDRL Report (" + jsvar_Frequency_CHO + ") - " + dateTime; */
if (!Page_ClientValidate()) {
NWF$("#s4-workspace").scrollTop(0);
event.preventDefault();
} else {
NWF$("#" + jsvar_CCRName_SLT).val(ccrName);
}
}(event));
Form Tests
In a new Form, I see the following:
If I try to submit the Form using the Save and Submit button:
I get a validation error because there is no Title. Likewise this would also appear if I had any other Controls that did not pass validation (whether by inherent validation or by way of custom Validation Rules).
Note that our CCRName Control has not been populated. Likely we do not want that control to populate unless the form has been correctly completed. If that's not the case, then we can change this behavior, but for now I will assume that's the correct way to do it.
Let's fix the control in question that is out of validation (Title being blank) and see what happens:
Too fast to capture! I'm back at the List Screen!
Let's click on the Item and see what it holds:
Alright, we have our name set!
What happens though if we edit the form and change a few of the dependencies:
Will it produce a different value? Clicking on Save and Submit and opening the form again we see:
Though the other controls were changed, CCRName did not!
Explaining The Code
I've taken your code and made a few modifications, all of this is expanded on in the comments of the code itself, so instead of explaining it twice, please see the following to get a better understanding of what's going on:
(function(event) {
/*
Before we proceed, because you only want this to be set once,
if the control already has a value, then we just stop running
our function now. No need to continue!
*/
if (NWF$("#" + jsvar_CCRName_SLT).val()) {
return;
}
/* your formatting function */
function formatDatePart(myNum) {
myNum = String(myNum);
if (myNum.length > 1) {
return myNum
} else {
return "0" + myNum;
}
}
/* making the other vars to build the title */
/*
It should be noted that I'm getting the same end result as
what you typed out, I just put them in arrays to make it slightly tidier for me.
The only other thing I've done is then use .map() to apply
the formatting function to every index of the array,
instead of typing it out each time I'd like to call it.
This will spit out an array where all of the contents
have been formatted correctly.
Example - If the date was 8/20/2022 then:
[jsvar_ReportDate_DTE.getFullYear(), jsvar_ReportDate_DTE.getMonth() + 1, jsvar_ReportDate_DTE.getDate()];
creates an array that looks like
[2022,8,20]
mapping that array with your formatting function:
[2022,8,20].map(formatDatePart)
results in the following new array being spit out:
[2022,08,20]
additionally, adding a .join("") to the end of that:
[2022,08,20].join("")
results in the following string being spit out:
"20220820"
-------------
Please note that the only reason I did this was out of my own
personal preference. The way you had your code setup would have
ultimately resulted in the same outcome which is the real
important thing. There a thousand and one ways to crack a nut
when it comes to programming, so don't get too hung up on trying
to optimize code before it's even working!
If it's clear to you and anyone else who might have to work with
it, is the most important thing. Readability > Small Code!
*/
/*
Because I can't see what other code you have, I am going to assume
that "jsvar_ReportDate_DTE" is a global variable that you've put
into the main form setting's Custom Javascript section, and that
you actually know it's a proper "Date" object.
However, if this is *actually* just a named javascript variable
that contains the ID of the control in question, then you're going
to need to add an extra step because Date controls do not return
Date Objects, they just return a string if you're getting the
jquery.val() from them.
No matter, in my case, because I don't have a global called
"jsvar_ReportDate_DTE" I have to run with the second scenario.
I've made a DateTime Control with a JavaScript Variable for its
ID called "jsvar_ReportDate_DTE". The next two lines represent
that code, but I've left your orignal line in, just commented out.
You can erase my two lines and replace them with your original by
uncommenting it out.
*/
var reportDate = new Date(NWF$("#" + jsvar_ReportDate_DTE).val());
var reportDateParts = [reportDate.getFullYear(), reportDate.getMonth() + 1, reportDate.getDate()];
/* var reportDateParts = [jsvar_ReportDate_DTE.getFullYear(), jsvar_ReportDate_DTE.getMonth() + 1, jsvar_ReportDate_DTE.getDate()]; */
var reportDate = reportDateParts.map(formatDatePart).join("");
var curDate = new Date();
var dateParts = [curDate.getFullYear(), curDate.getMonth() + 1, curDate.getDate()];
var timeParts = [curDate.getHours(), curDate.getMinutes(), curDate.getSeconds()];
/*
I have made a modification to the time portion of your name and
dropped the semicolons from it. The reason being is that those
characters are notoriously not supported as file names and in other
places in both SharePoint and windows (basically all of the Microsoft
ecosystem).
Though I do not know if you ever plan on using this as a List Item Title,
Document Title, File Name, etc. it seems like it *could* be, so better
err on the side of caution. If you're sure that's not the case
then feel free to change the ending:
.join("-");
to
.join(":");
*/
var dateTime = dateParts.map(formatDatePart).join("-") + timeParts.map(formatDatePart).join("-");
/*
As above, I don't know what jsvar_Frequency_CHO is, but will once again assume that it's
a global for you. I however will need to assign it a value so I have created an SLT control
with some random text in it.
Your original code is commented out.
*/
var frequencyCHO = NWF$("#" + jsvar_Frequency_CHO).val();
var ccrName = reportDate + " - Consolidated CDRL Report (" + frequencyCHO + ") - " + dateTime;
/* var ccrName = reportDate + " - Consolidated CDRL Report (" + jsvar_Frequency_CHO + ") - " + dateTime; */
/*
----------------------------------------------------------------------------------------
So if:
reportDate = 8/9/2022
frequencyCHO = "HXHfY12ST0O2qbb1e4VMpQ"
curDate = 8/20/2022
Then:
ccrName = "20220809 - Consolidated CDRL Report (HXHfY12ST0O2qbb1e4VMpQ) - 2022-08-2020-28-41"
----------------------------------------------------------------------------------------
*/
/*
This is going to check the Form to see if all of the controls have
passed validation. If they haven't it returns false. Because I've
put a Logical Not "!" in front of that statement, if the form is
invalid, then the following statement:
!Page_ClientValidate()
returns true, meaning that we then go to the top portion of our
if / else statement.
*/
if (!Page_ClientValidate()) {
/* This scrolls to the top of the page where the error readout is*/
NWF$("#s4-workspace").scrollTop(0);
/*
This STOPS the form from submitting because we do not want to
go any further or make any changes if the form is invalid!
*/
event.preventDefault();
} else {
/*
However, if the form is valid, then we update the CCRName
control with our new string. Then we're done!
*/
NWF$("#" + jsvar_CCRName_SLT).val(ccrName);
}
}(event));
Answering Your Questions
You asked me three specific questions, and I will do my best to answer them now
1. How dry is this code? Have I reached around my elbow to scratch my ear?
The most important thing about coding is consistency and readability. Your code was pretty well readable, but I made some changes for my own personal take on things. You could just as easily modify your original code to include the few changes I made! No matter, it would work and that is what matters. Don't get too caught up about being inefficient. CPUs have become so powerful that your few extra instructions of code won't really put much of a dent in performance. While it's good to go back and clean up house once everything else is done, it's often not as useful to try to do that from the very beginning. At least when you're starting out doing this stuff. As you become more consistent and get to better know a *good* or *better* way of doing something, you can start to be more efficient while you're writing your code in real-time.
2. How do I make the setCCRName() function run at the time the record is saved so the the last part of the string (the current time of the save) is accurate?
This has now been solved by attaching it to the Button's OnClick event (where we placed the code in the advanced section of the button). Essentially that just makes the button execute whatever code has been placed there before it runs the default event that (tries) to submit the form to SharePoint proper.
3. What "gotchas" do I need to be aware of that can come back and bite me when the user dose not act as expected?
Most of the gotchas involve your own organization and users. Is someone going to want to *change* the name after the fact? If so... well, it's not accounted for here! So that could be an issue! It's difficult to say what roadblocks exist for anyone in particular, but from my own experience I advise you to think about how your organization / users tend to approach the work they are already doing. Is there a standard straight forward process for getting from A to B? Do they tend to deviate often? Do they do things completely different than how most other orgs in the same field do the same things? Questioning like that should let you create some sort of list to ask people. "Will you all every want to change this name, and if so, when?" Based on the answer you can make changes. But it's always going to change from process to process, company to company.
More generically, the gotchas I tend to look out for are:
"Should a user be allowed to access this field that is important?"
"Should a user be allowed to override or change this field that is important?"
"Is there a way I can clearly allow for a user to pick one way or the other?"
Avoiding scenarios where "anything could happen" when the data being input into a form is, as far as I'm concerned, of the utmost importance. Things need to be either One Way, or One Way Depending On Very Specific Circumstances That Inform Other Processes. This thinking also tends to prevent me (or anyone else) from what's called "Overloading" a Control's meaning. Each control should have its purpose, and should not be used to serve as some sort of "resource" that might be used / consumed by distinctly different things.
For instance, if I have a Control which is used to provide notes from a Salesperson to a Designer, I do not want to then also use that Control as a way for them to convey something on a Quote that is being generated using those fields. Instead I want a Quote Notes Control and a Designer Notes control! In this way we can have nuance where it's needed because the customer might not need to read things that an internal Designer would need to see, and an internal Designer typically needs more details than what a Customer might want to see on a quote!
Outro
I hope that this helps to better crystalize a few things. While I typically do not talk much philosophy in regards to how one might approach designing something like a Nintex Form, I have done my best to explain some of my own approaches. Though I do not know if they will work for you (because everywhere is different), I hope that some of it is at least a little useful.