For some reason, I can't get preventDestroy: true to work.
In my example, the loading view is removed, when the applicationsListView is showing - even though I pass in preventDestroy true.
var loadingView = new App.Common.Loading.View();
App.layout.mainRegion.show(loadingView);
// Fetch the applications
var fetchingApplications = App.request('application:entities');
$.when(fetchingApplications).done(function(applications) {
var applicationsListView = new List.Applications({
collection: applications
});
App.layout.mainRegion.show(applicationsListView, { preventDestroy: true });
});
It is removed from region but not destroyed.
When you pass preventDestroy : true, it means that marionette doesn't itself call destroy method and event on previous view. Destroy method provides ubinding events and calls destroy on subviews and so on (https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.view.md#view-destroy).
But call show with preventDestroy : true still clear innerHTML and put new view in region (look at attachHtml method in backbone.marionette/src/marionette.region.js).
It can be helpfull if you reuse your existing loading view.
Related
I have a Marionette composite view that displays a collection, which I set in my Application start handler:
App.on('start', function() {
Backbone.history.start({pushState: true});
// I load up this.appsCollection in my before:start handler
var tblView = new this.appsTableView({
collection: this.appsCollection
});
this.regions.main.show(tblView);
});
This works as expected, displaying my entire collection. In my models, I have a state field, and I want to display only models with state 0. I tried:
collection: this.appsCollection.where({state: 0})
but that doesn't work. I actually want to display states in 0 and 1, but I'm trying to just display state in 0 for right now.
What am I missing?
The problem probably resides in that .where() doesn't return a collection, but an array. http://backbonejs.org/#Collection-where This was supposedly to maintain compatibility with underscore.
If you change the line to:
collection: new Backbone.Collection( this.appsCollection.where( { state: 0 } ))
Does that help?
I was able to override the filter method in my Marionette CompositeView:
http://marionettejs.com/docs/v2.4.3/marionette.collectionview.html#collectionviews-filter
I'm using a kendo grid and have a checkbox column with the following template:
"<input class='gridCheckbox' id='gridCheckbox_#=name#' name='Selected' type='checkbox' ng-model='dataItem.checked'/>"
In addition I'm also using an observableArray as the grid's dataSource.
When clicking the chekcbox the data in the observableArray is changed as expected but no "change" event is triggered.
Here is how I define the observableArray:
var obsArray = new kendo.data.ObservableArray(scope.gridData);
this.gridDataSource = new kendo.data.DataSource({
data: obsArray
});
obsArray.bind("change", function (e) {
console.log(e.action, e.field);
});
"scope.gridData" is the original dataModel. When I click the checkbox the observableArray is changed but not the "scope.gridData". In order to change the "scope.gridData" I want to listen to the "change" event and change the "scope.gridData" manually but as I said the "change" event is not triggered.
Any suggestions to what am I doing wrong and maybe there is a better solution.
Read This
your issue is that kendo uses a copy of your scope object
I manually added an event to my input checkbox (in our class we're using Angular so it was on ng-click="doSomething()" but maybe yours is just click="doSomething" and recorded handling the boolean change manually.
We have the Kendo Observables, too - but I got **lucky because we're also using the Breeze JS stuff where we can do data detection and refresh the grid once the data propagates backwards to the right place to be set to dirty. ( grid.dataSource.read(); )
If you want the full row value, make the click="doSomething(this)" and then capture it as the Sender. Just debug in and you should the dataItem attached to the Sender.
This might help you & this is not the correct figure but i did one example like this similar to your problem
var contentData = [
{ organization: 'Nihilent', os: 'Window' }
];
var nihl = contentData[0];
var viewModel = kendo.observable({
gridSource: new kendo.contentData.DataSource({
contentData: contentData
})
});
kendo.bind($(document.body), viewModel);
contentData.push({ organization: 'Dhuaan', os: 'Android' });
nihl.set('os', 'iOS');
I'm adding an item to my backbone collection like this:
item = existingItem.clone()
myCollection.add(item)
I have overwritten sync in MyCollection like this:
sync: function() {
console.log('sync is called')
}
however it seems that sync does not get called after the add - which executes successfully and fires an 'add' event. Am I missing something? or is this the correct behavior?
What you want is myCollection.create(item).
Check the Backbone Collection.create() doc
Collection.create returns the model, but in some cases you might need access to the xhr object. In that case you can do:
// add the model to the collection first
// so that model.url() will reference the collection's URL
myCollection.add(myModel)
// now save. this will trigger a POST to the collection URL
// save() returns the xhr so we can attach .done/.fail handlers
myModel.save()
.done(function(res) {
console.log('it worked')
})
.fail(function(err) {
console.log('it failed')
// might be a good idea to remove the model from the collection
// since it's not on the server
myCollection.remove(myModel)
})
My Code:
I am new to Backbone.js and trying to build an app with Backbone.js and PHP. When I am trying to call add in the router, I am getting error:
Uncaught TypeError: Object [object Object] has no method 'set'.
Please help me to find my mistake.
Thanks.
// Models
window.Users = Backbone.Model.extend({
urlRoot:"./bb-api/users",
defaults:{
"id":null,
"name":"",
"email":"",
"designation":""
}
});
window.UsersCollection = Backbone.Collection.extend({
model:Users,
url:"./bb-api/users"
});
// Views
window.AddUserView = Backbone.View.extend({
template:_.template($('#new-user-tpl').html()),
initialize:function(){
this.model.bind("click", this.render, this);
},
render:function(){
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
events:{
"click .add":"saveUser"
},
saveUser:function(){ alert('saveUser');
this.model.set({
name:$("#name").val(),
email:$("#email").val(),
designation:$("#designation").val()
});
if(this.model.isNew()){
this.model.create(this.model);
}
return false;
}
});
// Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"welcome",
"users":"list",
"users/:id":"userDetails",
"add":"addUser"
},
addUser:function(){
this.addUserModel = new UsersCollection();
this.addUserView = new AddUserView({model:this.addUserModel});
$('#content').html(this.addUserView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
As suggested in the comments, the problem starts here here:
this.addUserModel = new UsersCollection();
this.addUserView = new AddUserView({model:this.addUserModel});
and finishes here:
saveUser:function(){ alert('saveUser');
this.model.set({
By passing a collection in place of a model you create confusion, and as a result later in the saveUser function you try to call a Backbone.Model method (set) on a Backbone.Collection instance.
Note: As of version 1.0.0 Backbone.Collection now has a set method. In previous versions, such as the one used by the question's author, that method was instead called update.
There are several steps you can take to clarify this code. For starters, I would rename your model and collection classes so that it's clear that the model is the singular form and the collection is the plural form:
window.Users => window.User
window.UsersCollection => window.Users
Next, I would create a new User model, instead of a Users collection, and pass that to your view:
this.addUserModel = new User();
this.addUserView = new AddUserView({model:this.addUserModel});
Finally, I'd remove these lines:
if(this.model.isNew()){
this.model.create(this.model);
}
For one thing, the model will always be new (as you just created it before passing it in), but more importantly you don't need to call the Collection's create method because that method creates a new model, when you already have one created. Perhaps what you should add instead is :
this.model.save();
if your intent is to save the model to your server.
Since you already specified a urlRoot for the model, that should be all you need to create a new model, pass it to your view, have your view fill in its attributes based on DOM elements, and finally save that model's attributes to your server.
I think you are facing problem with object scope. When event fired it send to event object to that function. Just try this it may work
Declare global variable with the current view inside the initialize
initialize : function(){ self = this; }
then change this to self,
saveUser:function(){ alert('saveUser');
self.model.set({
name:$("#name").val(),
email:$("#email").val(),
designation:$("#designation").val()
});
if(self.model.isNew()){
self.model.create(this.model);
}
return false;
}
I have a "Cancel" button on my page which should reverts all the changes I made back to the state it was loaded from server..
I guess I need to store an initial state of Backbonejs model and restore a current (changed) state back to initial.
What is the best way to achieve that?
Thank you
FWIW - i wrote a plugin to handle this automatically, specifically with the idea of "cancel" buttons in mind: http://github.com/derickbailey/backbone.memento
model.previousAttributes() returns all of the previous attributes, while model.changedAttributes() returns all the changed attributes, but with their new values (or false if nothing has changed). So you could combine them to write a cancelChanges method in your prototype :
var MyModel = Backbone.Model.extend({
cancelChanges: function() {
var changed = this.changedAttributes();
if(!changed)
return;
var keys = _.keys(changed);
var prev = _.pick(this.previousAttributes(), keys);
this.set(prev, {silent: true}); // "silent" is optional; prevents change event
},
});
I dont believe there's a single method call for returning a model to its unedited state.. but the unedited values are available individually through model.previous(attribute) and collectively via model.previousAttributes.
Here is what I came up with:
var RollbackEnabledModel = Backbone.Model.extend({
initialize: function() {
this._initAttributes = _.clone(this.attributes);
},
parse: function(data) {
this._initAttributes = _.clone(data);
return data;
},
rollback: function() {
this.set(this._initAttributes);
}
});
Take a look at NYTimes' backbone.trackit. It tracks multiple changes to the model instead of only the most recent change like model.changedAttributes() and model.previousAttributes(). From the README:
var model = new Backbone.Model({id:1, artist:'Samuel Beckett', 'work':'Molloy'});
model.startTracking();
model.set('work', 'Malone Dies');
console.log(model.unsavedAttributes()); // >> Object {work: "Malone Dies"}
model.set('period', 'Modernism');
console.log(model.unsavedAttributes()); // >> Object {work: "Malone Dies", period: "Modernism"}
model.save({}, {
success: function() {
console.log(model.unsavedAttributes()); // >> false
}
});
In addition, the library adds functionality to resetAttributes to
their original state since the last save, triggers an event when the
state of unsavedChanges is updated, and has options to opt into
prompting to confirm before routing to a new context.