I'm trying to set an array value in a backbone.js model initialize function. In the line that starts with 'this.set...' I get a 'unexpected string' error. Is it not possible to set array values this way?
Thanks!
var bGenericItem = Backbone.Model.extend({
defaults: {
attrArray: new Array({'item_id': '', 'type': '', 'name':''})
},
initialize: function(){
// Set the id to cid for now
this.set({ attrArray["item_id"]: this.cid });
}
});
What you're trying to do doesn't make any sense. Your defaults is an array which holds a single object:
defaults: {
attrArray: [
{ item_id: '', type: '', name: '' }
]
},
You'd use an array if you wanted to hold a list of attribute objects. But, if you had a list of attribute objects, which one's item_id would you expect attrArray['item_id'] to refer to? Are you assuming that attrArray will always be initialized to the default value and that no one would ever send an attrArray in as part of your model's initial data? If so, you'd want something more like this:
// Use a function so that each instance gets its own array,
// otherwise the default array will be attached to the prototype
// and shared by all instances.
defaults: function() {
return {
attrArray: [
{ item_id: '', type: '', name: '' }
]
};
},
initialize: function() {
// get will return a reference to the array (not a copy!) so
// we can modify it in-place.
this.get('attrArray')[0]['item_id'] = this.cid;
}
Note that you'll run into some issues with array attributes that require special handling:
get('attrArray') will return a reference to the array that is inside the model so modifying that return value will change the model.
Things like a = m.get('attrArray'); a.push({ ... }); m.set('attrArray', a) won't work the way you expect them to, the set won't notice that the array has changed (because it hasn't, a == a is true after all) so you won't get "change" events unless you clone the attrArray somewhere between get and set.
There are several problems with your code
1: The defaults setting is an object literal which means the value that you assign to it is set as soon as it's defined. You need to set your defaults to a function, instead of a literal value. This will ensure each model instance gets it's own copy of the default values, instead of sharing a copy across every model instance.
2: You should also not use new Array, just use an array literal syntax []. But you're not really using an array in this code, so I removed the array wrapper for now.
3: You can't access attrArray directly. You must get it from the model's attributes and then update it
var bGenericItem = Backbone.Model.extend({
defaults: function(){
return {
attrArray: {'item_id': '', 'type': '', 'name':''}
};
},
initialize: function(){
// Set the id to cid for now
var arr = this.get("attrArray");
arr["item_id"] = this.cid;
}
});
Related
What is the idiomatic way to create a new Instance of an associated Model inside a Controller?
Motivation:
If I change the Class of my Model the code below doesn't get updated and I have to find all the breakages and fix them manually.
If I could use the the association in the controller, it gets updated automatically, but I can't find any documentation on the magical incantation to create a new instance from the controller reference.
Here is the relevant parts of my controller:
Ext.define('AdminApp.controller.SelectFilesController', {
extend: 'Ext.app.Controller',
models: [
'File'
],
// lots of stuff snipped for brevity
onFilefieldChange: function(filefield, value, eOpts) {
Ext.each(Ext.getDom('select-upload-button-fileInputEl').files, function(f) {
var fm = new AdminApp.model.File({
name: f.name,
size: f.size,
type: f.type,
md5: '',
status: 0
});
}
});
// lots more stuff snipped for brevity
}
What I want to know:
I found this in the documentation:
It’s important to note that the getters for both views and models
return a reference to the class (requiring you to instantiate your own
instances), while the getters for stores and controllers return actual
instances.
I can't find any examples of what the code to instantiate an instance should look like.
After much trial and error and just plain educated guessing ...
I figured out what the syntax should be.
First you need to create a reference to the class of the Model.
By convention this is the name of the model prefixed with get and suffixed with Model.
var fmc = this.getFileModel();
I had to do this outside the Ext.each() function that visits each item because the this reference pointed to an HTML5 File object instead of my Controller.
Then you simply use a regular Ext.create(fmc, { /* config */ } to get a new instance like so.
var fm = Ext.create(fmc, {
name: f.name,
size: f.size,
type: f.type,
md5: '',
status: 0
});
So the correct version of the onFilefieldChange function in the question should look like:
onFilefieldChange: function(filefield, value, eOpts) {
var fmc = this.getFileModel();
Ext.each(Ext.getDom('select-upload-button-fileInputEl').files, function(f) {
var fm = Ext.create(fmc,{
name: f.name,
size: f.size,
type: f.type,
md5: '',
status: 0
});
}
});
}
I have a backbone collection.I want to add or remove some models dynamically in the collection. But if i am using collection.set() method then it is going to remove first all elements and then it will add all elements again.
What i want to do is trigger add event of collection for those whose are really new added and trigger remove events for those whose are removed from previous collection.
Here is a example [http://jsfiddle.net/PkJCx/2/]
From the docs
The set method performs a "smart" update of the collection with the
passed list of models. If a model in the list isn't yet in the
collection it will be added; if the model is already in the collection
its attributes will be merged; and if the collection contains any
models that aren't present in the list, they'll be removed.
It is also a good idea to provide `idAttribute' to the model so that the collection identifies that based on the id. Otherwise the collection would not know if the model is a new one or not.
So after setting the id and using set, you can see that is performs a smart update
$(function () {
var MyModel = Backbone.Model.extend({
// This attribute should be set as a default
defaults: {
Name: ''
},
// Set the id attribute so that the collection
// know that it is the old model
idAttribute: 'id'
});
var Coll = Backbone.Collection.extend({
model: MyModel
});
var models = [{
Name: 'A',
id: 1
}, {
Name: 'B',
id: 2
}];
var collection = new Coll(models);
collection.bind('add', function (model) {
alert('addb')
});
collection.bind('remove', function () {
alert('add')
});
models = [{
Name: 'A',
id :1
}, {
Name: 'B',
id: 2
}, {
Name: 'C',
id: 3
}];
collection.add(models);
});
Check Fiddle
It will not try to remove the other 2, but Backbone is smart enough to identify that 2 of them are old models and then just merges the newer one into the collection.
I am trying to search a collection for a model attribute and then grab and return the entire model ?
var myModel = Backbone.Model.extend({
defaults: {
a: '',
b: '',
c: '',
d: '',
e: ''
}
});
My collection has around 100 of myModels.
I am trying to search through the collection by a, find it and then return the entire myModel of a so I can access the other attributes ?
If I understand your question correctly, you want to use the where method on Backbone collections, here in the docs:
http://backbonejs.org/#Collection-where
So, given an instance of MyCollection called myCollection that has MyModels in it, you can say:
var foundModels = myCollection.where({a:'some value'});
and foundModels will contain an array of the models you seek
BTW, if you are doing a more complex search, use the filter method instead, passing a function as the first argument that returns true on the desired match:
var modelsWhoseAStartsWithA = myCollection.filter(function(anyModel) {
var startsWithA = new RegExp(/^[aA]/);
return startsWithA.test(anyModel.get('a'));
});
Taking the following Model:
MyModel= Backbone.Model.extend({
defaults : {
myNestedModel:undefined,
},
initialize: function() {
this.set({myNestedModel: new MyNestedModel());
}
});
It has a single property named 'myNestedModel' which has the following definition:
MyNestedModel= Backbone.Model.extend({
defaults : {
myModel:undefined,
}
});
It too has a single Property name 'myModel'. Now if I create an instance of MyModel:
aModel = new MyModel();
The nested model will have been set in MyModel's initialize method. I then use JSON.stringify in a two step process:
// Use Backbone.js framework to get an object that we can use JSON.stringfy on
var modelAsJson = aModel.toJSON();
// Now actually do stringify
var modelAsJsonString = JSON.stringify(modelAsJson);
This works fine and I get the JSON representation of MyModel and it's property of MyNestedModel. The problem occurs when I use defaults, for example:
MyModel= Backbone.Model.extend({
defaults : {
new MyNestedModel(),
}
});
This causes a problem with JSON.stringify since it doesn't support circular references. I assume the circular reference is being created because all instances of MyModel share the same instance of MyNestedModel. Whereas the initialize method creates a new nested model for each instance.
Questions:
Is my understanding of defaults:{} being the 'cause' of the
problem correct?
From a question I posted recently I got the
impression I should be using defaults for all properties. If that is
the case, how should I be using defaults in the scenario presented
in this post/question?
Can someone clarify the use of defaults:{}
with regards to when the value applies, when it's overridden and
whether instances share the same default 'instances'?
Defaults is used only for attributes inside your model ( the data in the model ), and whenever you create your model it takes the values from defaults and sets the attributes. e.g.
User = Backbone.Model.extend({
defaults : {
rating : 0
}
})
User1 = new User({ name : 'jack', email : 'jack#gmail.com' });
User2 = new User({ name : 'john', email : 'john#gmail.com' });
User1.set({ rating : 2 });
Now your two models when called with toJSON will print
{
rating: 2,
name: 'jack',
email: 'jack#gmail.com'
}
{
rating: 0,
name: 'john',
email: 'john#gmail.com'
}
Since defaults is an object, every value you place there is evaluated immediately so :
defaults : {
rating : defaultRating()
}
will call defaultRating() - not everytime when you initialize the model, but immediately ( in the extend method )
You should use defaults for models where you need some data to exist on the creating of the model ( e.g. new myModel() )
In your example you have the following mistakes :
1.set a value without a property
defaults : {
PROPERTY : new Model()
}
2.you don't need such an option for your defaults - you should place there only attributes ( data ) for the model
Defaults applies always as long as it is not replaced by a new defaults in an extended model e.g.
var Model = Backbone.Model.extend({ defaults : { alpha : 'beta' } });
var myModel = Model.extend({ defaults : { beta : 'gama' } });
now your myModel when initialized will have
{ beta : 'gama' } // alpha : 'beta' will not be set as value, because it is replaced
I think I have a very popular problem, but not found answer for it now. :)
I got 2 similar comboboxes - at first i set my value by id - comboT.setValue("22763"); and it properly set a text value linked with this id.
At second combobox i at first reload store(jsonstore) and then set value - comboC.setValue("3"); But this combo set only ID not text value (if i open list i can see what combo properly marked text value. And after (if list simply close without select) text value properly displayed at combo.
How to solve this problem?
Thanks.
Something like this, syntax may be slightly off since I am doing it from memory:
var val = 3;
var store = comboC.getStore();
store.on("load", function() {
comboC.setValue(val);
}):
store.load();
Loading the store is asynchronous, you might want to move setting the new value into the callback: event handler of store.load({...}), because otherwise, you set the value before the store is actually loaded.
EDIT: for completeness, an example, so you have an alternative version (in some cases it might be undesireable to bind the callback to the store itself, like ormuriauga did):
var val = 3;
var store = comboC.getStore();
store.load({
callback: function() {
comboC.setValue(val);
}
});
One more example on how to set the combobox's value by searching a string in the underlying data store. I was able to code this by using the samples in these answers as a baseline:
//The store's data definition must have at least a data.id field defined
set_combobox_value_from_store = function (combobox, valueField, value) {
//Get a reference to the combobox's underlying store
var store = combobox.getStore();
store.load({
callback: function () {
//Find item index in store
var index = store.find(valueField, value, false);
if (index < 0) return;
//Get model data id
var dataId = store.getAt(index).data.Id;
//Set combobox value and fire OnSelect event
combobox.setValueAndFireSelect(dataId);
}
});
In extjs 4.1 looks like combo.setValue() works when the type of valueField in the Model is "string". this was my code
Ext.define('Model.CboObras', {
extend: 'Ext.data.Model',
idProperty: 'co_obra',
fields: [{
name: 'co_obra',
type: 'int'
}, {
name: 'nb_obra',
type: 'string'
}]
});
this does not work.
When I changed my code to this:
Ext.define('Model.CboObras', {
extend: 'Ext.data.Model',
idProperty: 'co_obra',
fields: [{
name: 'co_obra',
type: 'string'
}, {
name: 'nb_obra',
type: 'string'
}]
});
After that I use this:
var store = comboC.getStore();
store.load({
callback: function() {
comboC.setValue(val);
}
});
it now works like a charm!