I'm having a problem persisting disparate model changes at a later time.
When my view catches an update on a field, it triggers an update on the associated model.
Here's some rough psuedo-code:
[ snip view boilerplate ]
"events": {
"change input": function( event ){
EventManager.trigger( "field:update", {
"model": this.model,
"field": event.target
});
},
"click button": function(){
EventManager.trigger( "save", { "model": this.model };
}
}
In the event manager:
[ snip event manager stuff ]
vent.on( "field:update", function( eventData ){
var setter = InterfaceClass.getSetterName( eventData.field );
eventData.model[ setter ]( eventData.field.value );
});
vent.on( "save", function( eventData ){
eventData.model.save(
eventData.model.changedAttributes(),
{ "patch": true }
);
});
Unfortunately, changedAttributes only returns the changed attributes since the last .set. This works fine for the workflow of: update -> save -> repeat, but not at all for update -> repeat -> save.
Using...
eventData.model.save( null, { "patch": true } );
...behaves in essentially the same way, PATCHing only the last modified attribute of the model, not all of the changes since the last .save.
Now, it's completely possible to circumvent this issue by simply performing a PUT with the entire model instead of a PATCH, but I want to keep my network requests as simple as possible:
PATCH {"field": "value", "more": "changes"}
is highly preferable in my opinion to
PUT {"field": "value", "more": "changes", "other": "same", "thing": "same"}
How can I PATCH all of my model's changed attributes since the last time I saved them?
Related
I have a two Ext.panel.Grid (let's call it "A" and "B")
When i select line in the grid "A", the data loads to the grid "B. The main problem is the grid "B" is editable, and when i select another record in "A", the changes in "B" record are lost :(
there are next logic in controller
"grid id[A]": {
selectionchange: function(grid, record) {
this.getGridB.getStore().loadData( record.get('options'), false );
}
}
and the sample record is:
{
name: "Item1",
owner: "user1",
options: [
{
value: "345",
name: "option1"
},
{
value: "100500",
name: "option2"
}
]
}
where i should store the changed values? Or is there another, more "straight" solution?
LoadData() loads an array of data straight into the Store.If the data is in the correct format with respect to the model of other store then we can use this method directly loading the model object. If you wish to add data to the existing records in the store,you have to set append to true which is by default false.
store.loadData(data, true);
where data : Ext.data.Model[]/Object[]
So it should be something like this :
listeners: {
selectionchange: function(thisObj, selected, eOpts) {
gridB = Ext.getCmp('gridA');
gridB.getStore().loadData(selected, true);//true to append records
gridB.getView().refresh();
}
},
I keep running into some confusing solutions and unclear ways to wrap items that match into a div using backbone.
I am just building a simple example for myself, and would like to nest all models in a collection that have the same attribute team, using a comparator works well in organizing the list, but for the life of me I can't find a clear solution to wrapping each so that I have more control over the list of players inside the team.
There has to be a clear easy solution for a beginner like me. I really just want to keep things as clean and simple as possible. My desired html result looks like below.
<div class="pacers">
<li>Paul</li>
<li>Roy</li>
</div>
<div class="bulls">
<li>Kirk</li>
<li>Taj</li>
</div>
Based on a backbone friendly json array like below.
[
{
"name": "Paul",
"team": "pacers"
},
{
"name": "Kirk",
"team": "bulls"
},
{
"firstname": "George",
"team": "pacers"
},
{
"name": "Taj",
"team": "bulls"
}
]
So using a comparator is awesome I just write this comparator : 'team' and it handles the list order for me, cool, but I dont have much control I would like to wrap the list in a more hierarchical system.
Another approach:
If you are using underscore's templates this could be one way of doing it. You can use underscore's groupBy function to group the list based on teams.
var teams = [
{
"name": "Paul",
"team": "pacers"
},
{
"name": "Kirk",
"team": "bulls"
},
{
"firstname": "George",
"team": "pacers"
},
{
"name": "Taj",
"team": "bulls"
}
];
var groupedList = _.groupBy(list, function(l){
return l.team;
});
console.log(JSON.stringify(groupedList));
This is how it would be grouped.
{
"pacers": [
{
"name": "Paul",
"team": "pacers"
},
{
"firstname": "George",
"team": "pacers"
}
],
"bulls": [
{
"name": "Kirk",
"team": "bulls"
},
{
"name": "Taj",
"team": "bulls"
}
]
}
You can then use for each loop and in template and generate HTML in following way. The groupedList is passed as teams to below template.
<%
_.each(teams, function(team, teamName){
%>
<div class="<%=teamName%>">
<%
_.each(team, function(player){
%>
<li><%=player.name%></li>
<%
});
%>
</div>
<%
});
%>
This would generate the HTML the way you expected.
NOTE:
The code snippets are given considering underscore templating, you might have to make changes based on what you use. Hope it helps.
Correct me if I am wrong the problem being described relates more to controlling the contents of each item in relation to it's model as well as how to simply render them in groups.
1) Niranjan has covered grouping out the data into separate lists but remember that this list returned is not a Backbone construct.
2) As per the manual the '_.groupBy' method should be available to you via the collection i.e.:
myCollection.groupBy(etc);
3) I would personally consider mapping the results of the groupBy back into models and pass each and every model into a separate view and render them from within the main list view.
var CollectionView = Backbone.View.extend({
initialize : function () {
// Note: I am pretending that you have a real collection.
this.collection.fetch().then(
this.addAll(true);
);
}
addOne : function (model) {
// call .render individual template items here for each model.
var view = new ItemView(model);
this.$el.append(view.render();
},
addAll : function (groupOpts) {
var col = this.collection;
if(groupOpts === true) {
// Do grouping (or do it in the model). Maybe put back into new collection?
}
_.each(col, function(model) {
this.addOne(model);
}, this);
},
render : function () {
// Render your template here.
}
});
var ItemView = Backbone.View.extend({
render : function () {
}
});
Not a complete example but that's the general pattern I would follow when attempting the same thing. Having an individual view/model for each item, in my opinion, gives you more control.
This could be handled in a pretty crazy view template (depends on your template language)... or you could use a simpler template/view and just make some more crazy collection queries (first using a pluck to get the team, de-dupping that array, then running some where's for each of the teams... but you can see how this gets crazy)
I'd vote for the view and view template should handle this... what are you using? Jade? Mustache?
Something like this - logical psuedo code here since I don't know your template language:
var team;
forEach player in players
if(!team) {
set team = player.team
print open holder and then the first row
} (team !== player.team {
set team = player.team
print close of previous holder, then open holder and then the first row of new team
} else {
print just the player row
}
Even so, you can see how this is a bit dirty in and of itself... but what you are describing is a view/presentation concern, and you can do it here with no new additional loops and maps and wheres (like you'd have to do if you did it in the data layer before calling the views)
ExtJS 4.2 MVC: I have two models ServiceM and CommentsM. ServiceM has association(hasmany) with CommentsM. I DIDNOT forget to add the requires section ServiceM. Proxy is an ajax type defined in the model itself. I also created stores for each. Coming to the view, I have a grid for viewing all the services which are derived on loading the application. itemdblclick event is used to provide a detailed view about the service which is a window extending a form. The form is popullated by the below code:
var ServiceDetailV = Ext.widget('alias name of the service detail view');
ServiceDetailV.down('form').getForm().loadRecord(record);
I have two questions here.
When using developer tools in google chrome, in the above code I place debugger; at the end. I have highlighted the record, right clicked and evaluated that part. I see the data part and raw part. what is this raw part. It has all the data which the server is giving me(payload), even the nested comments section which is associated with the Service data.
I am able to popullate the fields in the form, but not the list of comments. the list of comments goes into a panel present in the form. How can I popullate the comments section.
JSON data:
{
"data": [
{
"id": 1,
"x": "some text",
"q": "some text",
"a": "some text",
"r":"some text",
"comments": [
{
"id": 87,
"commentDate": "date",
"description": "some text"
},
{
"id": 86,
"commentDate": "date",
"description": "some text"
}
]
} "total": 1,
"success": true}
Now, how can i access the comments field and poppulate the form with this data?
Please shed some knowledge on Associations ExtJs MVC.
Cheers!
Well, I took a step and got the solution for this. the raw parameter actually has the raw JSON payload. In the controller part I have handled it via a select event.
onSelectIssueShowComments : function(selection,record, index, eOpts) {
this.getComments().setRecord(record.raw);
}
I the view part
tpl : [ '<p>Previous Comments: ', '<tpl for="comments">',
'<p>{#}. {description} {commentDate}</p>', '</tpl></p>' ],
setRecord : function(record) {//this record here is record.raw
this.record = record;
if (record) {
this.update(record);
} else {
this.update(' ');
}
}
So it displays the array of comments in a Panel.
I am trying to pull in a collection from the url attribute and am having some problems. It seems fetch() returns successfully, but then I cannot access the models in my collection with get(). I am using bbb and requireJS to develop my modules
var rooms = new Rooms.Collection(); // calls the rooms module
rooms.fetch({success: function(){
console.log(rooms.get(1)); // should output the first model
});
Here is my collection code in the rooms module:
Rooms.Collection = Backbone.Collection.extend({
model: Rooms.Model,
url: 'http://localhost:8888/projects/meeting-room/app/data/rooms.json'
});
If I output rooms, everything turns out fine. But when I try for a specific model, that is when I get an error.
[{
"id": 12345,
"name": "Ford",
"occupied": false
},
{
"id": 23458,
"name": "Chevy",
"occupied": false
},
{
"id": 83565,
"name": "Honda",
"occupied": false
}]
The collection.get method looks up a model by id. If you want to find a model by position, use collection.at instead.
Also notice that array indices in javascript are 0-based, so the first model can be found with:
var model = collection.at(0);
For convenience, Backbone collections also implement some of underscore's array and collection methods, including first. That means you can also find the first model with:
var model = collection.first();
I have a model, and when I do model.attributes.model I see the attributes of the model. One attribute is name, so model.attributes.model.name returns the right name. However, when I do model.get('name') I get the default that I had set in the model.
How do I set all the attributes of the model so that it works with get?
JSON used to build the model
[{
"model":{
"name":"My name",
"description":
"Description goes here!",
"vote_score":null
},
"context":{}
}]
Either modify your server-side code to return an array of model attributes, as you said in the comments, or modify your model definition to override the parse method :
var MyModel=Backbone.Model.extend({
parse: function(data) {
return data.model;
}
});