circular reference with 2 listeners

  • 1
  • Question
  • Updated 4 years ago
  • Answered
I seem to have created a circular reference, with 2 listeners which respond to changes in 2 separate models, but unfortunately update each other.

The parent object 'FleetQuote' updates the child object 'Sectors'. The first listener listens for changes in the FleetQuote model, and updates the Sectors if changes occur.

The child object 'Sectors' updates the parent object with e.g. a Final Price (sum of the sectors), and I have a listener which listens for those changes in the sector and updates the quote.

But one obviously updates the other!

Code is below:

//listener for Fleet Quote model
skuid.$(function(){ 
var FleetQuote = skuid.model.getModel('FleetQuote'), 
FleetQuoteSectors = skuid.model.getModel('FleetQuoteSectors'); 

var listener = new skuid.ui.Editor(); 
listener.handleSave = function() { 
if (!FleetQuote.hasChanged) { 
FleetQuoteSectors.updateData(); 

}; 
listener.registerModel(FleetQuote); 
}); 


//listener for sectors Model
skuid.$(function(){ 
var FleetQuoteSectors = skuid.model.getModel('FleetQuoteSectors'), 
FleetQuote = skuid.model.getModel('FleetQuote'); 

var listener = new skuid.ui.Editor(); 
listener.handleSave = function() { 
if (!FleetQuoteSectors.hasChanged) { 
FleetQuote.updateData(); 

}; 
listener.registerModel(FleetQuoteSectors); 
}); 


The only solution I can think of is that I only really need to listen to changes on specific fields in the quote model (rather than the whole model) but how do I implement a listener for 2 or 3 specific fields on a model?

Any help is GREATLY appreciated! Greg

Photo of Greg Jarrett

Greg Jarrett

  • 3,496 Points 3k badge 2x thumb

Posted 4 years ago

  • 1
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,014 Points 20k badge 2x thumb
Hi Greg,

Actually, I don't think that these 2 listeners create a circular loop, because updateData() does not cause handleSave() to be called. Here's the way that the Model API methods work with Editors:

model.save() --> once the operation finishes, handleSave() is called on registered Editors
model.cancel() --> once the operation finishes, handleCancel() is called on all registered Editors
model.updateData() --> once the operation finishes, handleDataRefresh() is called on all registered Editors

So, when your FleetQuote save listener gets called, it will go call updateData() on the FleetSectors Model, which basically requeries for data in that Model. This does not trigger the handleSave() on your FleetSectors listener. 

That being said, perhaps there's some other stuff going on in your page that I don't know about, so to give you the benefit of the doubt, here's how you can test: add console.log() statements in your handleSave() methods to see whether they get continuously called, like this:

listener.handleSave = function() { 
console.log('fleet quote listener handleSave called');
if (!FleetQuote.hasChanged) { 
console.log('requerying Fleet Quote Sectors');
FleetQuoteSectors.updateData(); 

}; 
listener.registerModel(FleetQuote); 
}); 


listener.handleSave = function() { 
console.log('sectors listener handleSave called');
if (!FleetQuoteSectors.hasChanged) { 
console.log('requerying Fleet Quote');
FleetQuote.updateData(); 

}; 
listener.registerModel(FleetQuoteSectors); 
}); 


If you see these statements continuously show up in the JavaScript console, you've got a problem.

All that being said, there are ways to listen for changes on particular fields in a Model -- are you thinking that you'd want this to happen before the Quote model is saved (e.g. real-time) or only after it is saved?

Photo of Greg Jarrett

Greg Jarrett

  • 3,496 Points 3k badge 2x thumb
Hi Zach, as you suggested, it only executes once (console screenshot attached),
but what does happen is the 'Sector' row disappears when I hit save. (this was happening before but I didn't mention it as I thought fixing the listener would resolve this.)

There is a condition on the sector object which says: 

Sector__c records WHERE Fleet_Quote__c
is in the set of values
containing the id field from any row returned by the FleetQuote model
This condition is always on.

(I haven't used the url id parameter as there is a parent object to both of these, kind of like an opportunity object, which contains the id parameter)

It seems as if the FleetQuote model isn't refreshing in order to satisfy the condition on the sector object.

If I refresh the page then all is fine - the data put in is rendered correctly in the page, and I'm able to make changes. Its only with a brand new FleetQuote record that the child sector row is disappearing.

I've attached another screenshot of the disappeared sector page with console statements.

Photo of Zach McElrath

Zach McElrath, Employee

  • 49,014 Points 20k badge 2x thumb
Models with "Field from another Model" conditions ordinarily won't "reevaluate" the "Field from another Model" part of such Conditions behavior unless the Source Model is also requeried at the same time --- i.e. for your scenario, if you are going to requery the Sectors Model, you'll have to requery the Quotes Model as well, using

skuid.model.updateData([FleetQuote,FleetQuoteSectors]);

if you want to rely on the Field from another Model Condition to produce your desired behavior. NOTE: querying for the FleetQuote Model first is also essential.

If there's a way you can write your Conditions so that you don't have to use the Field from another Model IN Condition here, that might be the way to go --- e.g. if you can say Sectors where Fleet_Quote__r.Opportunity__c = (param) Id, that would be an option that would avoid the problem.
Photo of Greg Jarrett

Greg Jarrett

  • 3,496 Points 3k badge 2x thumb
IT WORKS!! I hadn't thought of traversing fields on the condition. The simple fix is often the way to go.

Thank You!!