FYI,
I’ve confirmed that the .createRowIfNoneFound is the problem.
When I remove that property from the model, and then after load call the following, I get the expected result.
//Replacement for .createRowIfNoneFound<br>if (!brochureModel.getRows().length) brochureModel.createRow();
Definitely a hacky work-around. Skuid should be able to load dynamically created models with createRowIfNoneFound = true.
Hey Matt -
Can you be more specific when you say “…the code breaks…”? Is there an exception thrown? Some type of error message?
I know that Skuid contains code that will not allow a model to load when there are changes in the model (that line of code inside the runtime has bitten me in the past). I’m guessing you are probably encountering an unhandled exception on that line of code but just want to confirm.
In your screen shot, there are changes. It’s that Row Id ‘1’ was created - it’s just no field values are set yet which is why you might be thinking there are no changes.
Before determining if there is something in your code that might be wrong or if this potential bug, if you could let me know what you mean by “breaks” and also if you could put together just a simple sample that would be helpful. The fact that it’s inside of a custom component shouldn’t matter so isolating this down to a blank page with a simple dynamic model creation should narrow down surface area.
Hey Barry. Thanks for pursuing the details… sorry for the vagueness.
Yes, an exception is thrown at .load() indicating that the model can’t be updated/queried because there are changes.
Here’s the code for my component… it’s just a field editor with a reference field and a checkbox that creates its own dynamic model with conditions based on its properties.
// Brochure Component skuid.componentType.register('ccoptimize__brochure',function (element,xmlDef,component){
// Get properties and model
var brochureCode = xmlDef.attr('code'),
brochureLabel = 'Literature__' + xmlDef.attr('brochure'),
caseModelId = xmlDef.attr('casemodel'),
caseFieldId = xmlDef.attr('casefield');
// Set variables
var brochureModelId = (brochureLabel) ? brochureLabel.replace(/.^A-Z0-9_]+/ig, "") : 'Literature__' + randomString(4),
caseModel = skuid.$M(caseModelId),
caseValue = caseModel.getFieldValue(caseModel.getFirstRow(),caseFieldId);
// Generate Brochure Model
var brochureModel = new skuid.model.Model();
brochureModel.objectName = 'Literature__c';
brochureModel.id = brochureModelId;
brochureModel.recordsLimit = 1;
<b>// brochureModel.createRowIfNoneFound = true;
</b> brochureModel.fields = .
{ id: 'Brochure_Name__c'},
{ id: 'Brochure_Name__r.Name'},
{ id: 'Brochure_Given__c'}
];
brochureModel.conditions = d
{
type: 'fieldvalue',
field: 'Brochure_Name__r.Brochure_Code__c',
operator: '=',
value: brochureCode,
encloseValueInQuotes: true
},
{
type: 'fieldvalue',
field: 'Patient_Case__c',
operator: '=',
value: caseValue,
encloseValueInQuotes: true
}
];
// Define XML for our child components
var xmlFieldEditor = $xml('<basicfieldeditor showheader="true" showsavecancel="false" showerrorsinline="true" model="'+brochureModelId+'" mode="edit"/>').append(
$xml('<columns/>').append(
$xml('<column width="100%" />').append(
$xml('<sections/>').append(
$xml('<section title="Brochure" collapsible="no" showheader="true"/>').append(
$xml('<fields/>').append(
$xml('<field id="Brochure_Name__c" valuehalign="" type="REFPICK" optionsource="" required="true"/>').append(
$xml('<filters><filter type="fieldvalue" operator="=" enclosevalueinquotes="true" field="Brochure_Code__c" value="'+brochureCode+'"/></filters>'),
$xml('<searchfields/>')
),
$xml('<field id="Brochure_Given__c" valuehalign="" type=""/>')
)
)
)
)
)
);
// Make containers for our components
var brochureContainer = $('<div id='+brochureModelId+'>');
// Load brochure model
$.when(brochureModel.initialize().register().load()).then(function(){
skuid.component.factory({
xmlDefinition: xmlFieldEditor,
element: brochureContainer
});
// Add components to the DOM element
element.append(brochureContainer);
<b>//Replacement for .createRowIfNoneFound
</b><b> if (!brochureModel.getRows().length) brochureModel.createRow();
</b> });
});
Hey Matt -
Thanks for the additional info.
The skuid.model.Model.initialize method is creating the row when createRowIfNone is true. This results in load failing because there are changes in the model.
To be honest, I'm not sure if this is a bug or by design. Unfortunately, neither initialize nor register are documented in the API reference so it's difficult to say if a row should be created in those methods. The dynamic modules tutorial also doesn't really indicate what is required or not required when dynamically creating a model as it just provides a sample of how to dynamically create a model where it's expecting data to come from the server.
I could see a use case where someone would want to create a dynamic model that wouldn't contain server side data (there wouldn't be a need to call load possibly in this case) and they would want a default row so it's possible that initialize is behaving as designed. Again, since there is no documentation around any of these methods, it's hard to say for sure.
Your workaround will do the trick and it's the only viable option at this point from what I can tell. I did put together a small isolated sample (see below). In my approach, I cancel changes after initialize/register before calling load. This allows load to succeed but even when createRowIfNoneFound is true, a new row is not created.
Skuid Team - A few requests/questions:
1) Can the skuid.model.Model documentation be updated to include initialize and register so that we can better understand what their intended purpose is and when/how to use them?
2) Is calling "Load" always required when dynamically creating a model even if there is no data from the server needed?
3) Should initialize be creating a row if none or should that only be the responsibility of load?
4) In my sample below, load doesn't create a new row even though createRowIfNoneFound is true. Is this intended behavior?
5) What is the recommended approach for dynamically creating a model with conditions, loading the data and then creating a default row if none is found?
Isolated Repro:
(function($S, $){
skuid.snippet.registerSnippet('createModel', function(){
var accountModel = new skuid.model.Model();
accountModel.objectName = 'Account';
accountModel.id = 'Account';
accountModel.recordsLimit = 1;
accountModel.createRowIfNoneFound = true;
accountModel.fields =
{ id: 'Id' }
, { id: 'Name' }
]
accountModel.conditions =
{
type: 'fieldvalue'
, field: 'Name'
, operator: '='
, value: 'FiddlyFooBar'
, encloseValueInQuotes: true
}
]
accountModel.initialize().register();
// register creates a row since createRowIfFound is marked to true
// so we must cancel the changes in order for load to complete successfully
accountModel.cancel();
$.when(accountModel.load())
.done(function() {
console.log(accountModel);
})
.fail(function() {
console.log('failed to load!!') ;
});
});
})(skuid, skuid.$);
Thanks, Barry.
I also tried calling .cancel() before .load() to handle the error, but realized that it was eliminating the row creation. I also tried .save(), but ran into some issues with that as well, though I don’t recall exactly what went wrong at the moment.
I’d steer clear of save unless you really want data to be saved. It’s possible your saved failed because of missing required data on the record.
Hopefully the Skuid Team can shed some light on my questions and provide direction on this. In the meantime, your workaround should keep you going.
Hello Barry,
I am working on creating dynamic model for one of my project and took your code sample as a start. the problem I am facing is getting the error “Cannot read property ‘getAdapter’ of undefined”
when it hit this line:
accountModel.initialize().register();
Do you have any idea what I am missing?
Thanks,
Tanzir
Tanzir,
Try adding
accountModel.adapterName = "salesforce";
into your model definition.
Thanks Matt,
But I am still getting this error. I am attaching the code. if you can tell what I am missing that will be a great help:
var element = arguments[0], $ = skuid.$;
alert(‘starting work’);
var accountModel = new skuid.model.Model();
accountModel.objectName = ‘Account’;
accountModel.adapterName = “Salesforce”;
accountModel.id = ‘Account’;
accountModel.recordsLimit = 1;
accountModel.createRowIfNoneFound = true;
accountModel.fields = ;
{ id: ‘Id’ }
, { id: ‘Name’ }
];
accountModel.conditions = b
{
type: ‘fieldvalue’
, field: ‘Name’
, operator: ‘=’
, value: ‘FiddlyFooBar’
, encloseValueInQuotes: true
}
];
accountModel.initialize().register();
alert(‘registered model’);
// register creates a row since createRowIfFound is marked to true
// so we must cancel the changes in order for load to complete successfully
accountModel.cancel();
$.when(accountModel.load())
.done(function() {
console.log(accountModel);
})
.fail(function() {
console.log(‘failed to load!!’) ;
});
I made it working. I had add an empty model in skuid. It seems little confusing on why I have to have an empty model
Hello Tanzir -
You have encountered a Skuid bug. I just created another post to track the issue - see https://community.skuid.com/t/js-exception-encountered-on-dynamic-model-initialize-when-n… for details.
The workaround, as you found, is to create a declarative model on the page making sure to have load data and create row if none both set to False (not checked) to avoid any unnecessary additional performance impact. The model will result service adapter getting registered which is what you need to make your dynamic model work properly.
Matt - In this case, the adapterName shouldn’t be needed as Skuid will default “saleforce” to the adapter name if the property is empty. Just FYI.
Skuid Team -
In the comments above, I had asked some questions to help better shape Matt’s issue but there hasn’t been a response to them.
Can someone please provide some insight to the questions at https://community.skuid.com/t/dynamic-model-createrowifnonefound-prevents-load?topic-repl…?
Thanks!
Skuid Team - Any insight on this?