Javascript throwing unsaved changes error when no changes have been made.

  • 1
  • Problem
  • Updated 4 years ago
  • Solved
I'm getting an uncaught model (unsaved changes) when I run the following script more than once. I tried to implement a model check and save if there were changes, but to no avail. I still get hte uncaught changes error on the 'Open' model.

What am I missing?

Here's the code:
var mO = skuid.$M('Open'),    model = skuid.$M('ApptInteractions'),
    condition = model.getConditionByName('Date'),
    curDate = condition.value;
var jsDate = (curDate == 'TODAY') ? new Date() : skuid.time.parseSFDate(curDate);
jsDate.setDate(jsDate.getDate()-1);
var prevDate = skuid.time.getSFDate(jsDate);
//Set Conditions
model.setCondition(condition, prevDate);
mO.setCondition(mO.getConditionByName('OpenDate'), prevDate);
//Requery models (if unsaved, save first)
if (!model.hasChanged) {
    model.updateData();
} else {
    model.save({
    callback: function(results){
        if(results.totalsuccess) {
            model.updateData();
        } else {
            console.log('Appointments model save failed.');
        }
    }});
}
if (!mO.hasChanged) {
    mO.updateData();
} else {
    mO.save({
    callback: function(results){
        if(results.totalsuccess) {
            mO.updateData();
        } else {
            console.log('Appointments model save failed.');
        }
    }});
}

//Refresh template
skuid.component.getById('DateTemplate').render();
Thanks!
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb

Posted 4 years ago

  • 1
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
It looks like every time I activate and set the value of a condition on a model, and then try to query that model, it's throwing an "uncaught model 'Model' has unsaved changes." error.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Under what circumstances does this script get run?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
I'm in the mobile page builder. User clicks a button (to display open appointments for the previous day). 

I get the same error when the user clicks a different button and activates this action framework set:
  1. Activate and set value of model condition.
  2. Query Model
  3. Set Main Panel
It throws the error before the query, even though I've made no changes to the model (as far as I can tell).
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
What happens if you run a "Cancel Models" action / run model.cancel() and m0.cancel() in JavaScript before everything else?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
No dice.

Fascinatingly, the error is getting triggered from the updateData() within the callback of the save() function: 


How could there be unsaved changes in the model? It was just saved!
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Better check to see what changes there are --- there must be something. Before the m0.updateData in the callback, do:

console.log('Does m0 still have changes: ' + m0.hasChanged);
console.log(m0.changes);
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
slick. I didn't know model.changes was a thing.

What am I looking at?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Looks like something is populating a value for the Name field on all of the rows in your Model, immediately after save --- do you have any After-Save Actions set up on a Deck in your page? Or any other JavaScript that might be bound to the save event on this Model?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Zach,

I searched through the XML, and couldn't find anything. I don't have any after-save actions on the panel in question.

Mobile doesn't have any model actions, so that's out (also checked the XML just in case).

The only thing I can think that might impact Name is a field renderer we have on Name on other panels... but the field isn't even rendered on the page, so I don't think that should make any difference?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Aha! I bet that's it. Is there an updateRow() somewhere in the Field Renderer javascript?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Matt, checked your renderer.fullName snippet, found this bit:

value = CC_SCH.getFullNameFromField(field);
skuid.ui.fieldRenderers[field.metadata.displaytype].read( field, value );
// If our value is not equal to the current value of the "Name" field,
// do an updateRow
if (field.Name !== value) {
model.updateRow(row,'Name',value,{ initiatorId: field._GUID });
}

I'm posting this to the Community for posterity to help others who run into a similar conundrum. One problem with the original was that field.Name was meaningless (i think you meant field.row.Name), but the bigger problem is that you are running the update check after the field renderer. Here's how I would do this:

var combinedValue = CC_SCH.getFullNameFromField(field);
// If our combined value is not equal to the current value of the field,
// do an update, and change value appropriately
if (combinedValue !== value) {
   value = combinedValue;
   model.updateRow(row,'Name',value,{ initiatorId: field._GUID });
}
skuid.ui.fieldRenderers[field.metadata.displaytype].read( field, value );
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Yep...

/*** Name Renderer:  registers Name Fields with the Model
* and concatenates the names as they are being changed.
**/
var field = arguments[0],
value = skuid.utils.decodeHTML(arguments[1]),
model = field.model,
row = field.row;
//field.metadata.displaytype
//register related fields.
field.model.registerField(field, "First_Name__c");
field.model.registerField(field, "Middle_Name__c");
field.model.registerField(field, "Last_Name__c");
value = CC_SCH.getFullNameFromField(field);
skuid.ui.fieldRenderers[field.metadata.displaytype].read( field, value );
// If our value is not equal to the current value of the "Name" field,
// do an updateRow
if (field.Name !== value) {
model.updateRow(row,'Name',value,{ initiatorId: field._GUID });
}
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Nice... thanks, Zach!
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Zach... I'm still getting the error after updating the renderer as you indicated.

It's still changing the value of Name after every save, apparently.

What's the best way around this?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Matt, ideally what I would do is to remove the Field Renderer altogether --- and use Model Actions. I would setup a single Model action on your "Open" Model listening for changes on the First Name, Middle Name, or Last Name fields, whose action is to combine those together into the desired concatenated Name format, and put this value into the Name field on the row in question. However, Model Actions are only available via XML in the Mobile Composer --- they work fine, but you have to define them in XML. I could suggest how to do this if you are open to it, or we could continue to debug your renderer code.
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Smart. I'll work on that now. We have a desktop version of the page as well, so I'll create the model actions there and paste them into the mobile XML.
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Zach (et al.),

i'm using model actions to set Name to the correct value, instead of a custom renderer. However, I'm struggling to get the field to render again when the field is updated.

skuid.ui.Field('Name').render() ?

Will that render every field on every model called Name?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
All affected Name fields should automatically re-render if you update the value of that field on any rows in the Model. Did you remove the custom field renderer snippet from all of the Name fields?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
There are no custom renderers on Name. 

This is the model action set for when First Name, Middle Name, or Last Name are updated:


However, this is the result after typing in those three fields:


Help?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
If I set render conditions on the Name (Full Name) field so that it will only render when First_Name__c is != null, then I get Name = First.

But it does not rerender when typing middle or last.
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
Is the Name field an Autonumber in Salesforce / is it editable? I don't think that the update will appear unless the field is editable.

Also, try using triple-braces instead of double. (e.g. {{{FirstName}}} instead of {{FirstName}} ) 
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Ah.. that was it. Apparently it was set as an Autonumber instead of Text. Thanks!