BackBoneJs donot Delete/Update Model to the server - javascript

I have an application that reads/create/update model and save it to the server. But presently I am able to read and save my models from and to the database. But I am unable to delete / update the model from and to the server. currently the views gets deleted of the model but not the model it self
here is the JSFiddle path http://jsfiddle.net/u17xwzLh/1/
$(function() {
Model
var modelContact = Backbone.Model.extend({
defaults: function() {
return {
Id: 0,
Name: "",
Address: ""
};
},
//if i add this idAttribute = "Id" it deletes the value from the server
//but i am unable to create a new model/new entry to the database
clear: function () {
// Deletes the model but the changes are not posted back to the server
this.destroy();
}
});
Collection
// runs fine
var contactCollection = Backbone.Collection.extend({
model: modelContact,
url: 'api/Contact'
});
var contacts = new contactCollection;
ModelView
var contactView = Backbone.View.extend({
tagName: "tr",
events: { // runs fine
"click a.destroy": "clear"
},
template: _.template($("#newContacttemplate").html()), // runs fine
initialize: function() {
this.model.on("change", this.render, this);
this.model.on('destroy', this.remove, this);
},
render: function() { // runs fine
this.$el.html(this.template(this.model.toJSON()));
return this;
},
clear: function () {
this.model.clear();
}
});
MainView
var main = Backbone.View.extend({
el: $("#contactApp"),
events: { // runs fine
"click #btnsave": "CreateNewContact"
},
initialize: function() { // runs fine
this.Nameinput = this.$("#contactname");
this.Addressinput = this.$("#contactaddress");
contacts.on("add", this.AddContact, this);
contacts.on("reset", this.AddContacts, this);
contacts.fetch(); // Note : populates all the database values
},
AddContact: function(contact) { // runs fine
var view = new contactView({ model: contact });
this.$("#tblcontact tbody").append(view.render().el);
},
AddContacts: function() { // runs fine
contacts.each(this.AddContact);
},
CreateNewContact: function(e) { // runs fine
contacts.create({ Name: this.Nameinput.val(), Address: this.Addressinput.val() });
}
});
var m = new main;
});

Right now you have a URL defined on your Backbone.Collection but not on your Backbone.Model, which means you have to do all AJAX work through the Collection. It doesn't have to be that way though: you can add a second URL on yours server-side for Model AJAX operations, or the two could even share a URL (if you set it up appropriately).
The important part, if you want to be able to call this.destroy(); and have it reflected on your server, is that you need:
a URL on your server that can handle requests with the DELETE method (vs. the usual GET or POST methods)
a url property on your Backbone.Model that is set to that server-side URL
Once you have that your call to this.destroy(); will create a DELETE AJAX request, your server will receive that request and know that it should delete the appropriate database record, and then that model will be deleted on both the client- and server-side.

Related

Emulating socket event how do I update the relevant view with received model data?

I am trying to emulate multiple socket responses listening for each response in my view and updating the model, right now however I am managing to update each view with the same data. Can anyone advise what I would need to have in place in order to update the view relating to the data, right now I'm very confused about how this all works like should there be unique data in the response, should I check this in the view or the model etc?
Sample JS
function outputData(id, name) {
return {
id: id,
name: name
}
};
var View = Backbone.View.extend({
className: 'view',
template: Handlebars.compile( $('.tmpl-view').html() ),
initialize: function() {
this.listenTo(Backbone.Events, 'data:recieved', function(response) {
// Check if this model data is related to this view then set?
this.model.set(response);
this.render();
}.bind(this), this)
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var viewOne = new View({
model: new Backbone.Model()
});
var viewTwo = new View({
model: new Backbone.Model()
});
$('body').append(
viewOne.render().el,
viewTwo.render().el
);
Backbone.Events.trigger('data:recieved', outputData(1, 'Data for viewOne'));
setTimeout(function() {
Backbone.Events.trigger('data:recieved', outputData(2, 'Data for viewTwo'));
}, 400);
JS Fiddle
http://jsfiddle.net/9kf9qvdg/
I would take a slightly different approach. Your view should only listen to changes on the one model it is backed by. This way each view doesn't need to parse every socket message:
initialize: function() {
this.listenTo(this.model, 'change', this.render);
}
Instead you would have separate logic that handles updating your models appropriately when you receive data. This might look like:
function updateData(id, msg) {
var data = outputData(id, msg);
var modelToUpdate = collection.findWhere({id: data.id});
if(modelToUpdate) {
modelToUpdate.set(data);
}
}
Here is a fiddle showing the above in action: http://jsfiddle.net/xwmx64y3/

Backbone view doesn't update after event fires for the first time

I have a backbone view that has a single click event to update a collection. In my console, I can see the object being updated and the number of models being returned is changing, however the view stays static after the event is fired for the first time and any second attempt to fire it gives me the error TypeError: text is undefined. For reference, I have my script loading at the bottom of the page and the template (using underscore) is above it.
Here's my view
app.MyView = Backbone.View.extend({
el: 'body',
events: {
'click #submit': 'fetchData'
},
initialize: function() {
this.collection = new app.MyCollection();
// On my first attempt, I tried having a the render tied to the sync event when the view initiated, although the sync event doesn't actually occur until something is clicked
// this.collection.on('sync', this.render, this);
},
render: function() {
console.log('rendering');
var schedule = $('#schedule');
var template = _.template($('#times-template').html());
schedule.html(template({ collection: this.collection.toJSON() }));
},
fetchData: function() {
that = this;
stationQuery.station_start = $('#start').val();
stationQuery.station_end = $('#end').val();
var query = stationQuery;
this.collection.fetch({
data: query,
success: function(collection, response) {
console.log('fetching data');
console.log(collection);
// attempt #2 - moving the sync event to the success callback of fetch doesnt allow the view to update a second time either
// collection.on('sync', that.render, that);
},
error: function(collection, response) {
console.log('error on fetch', response);
}
});
},
});
app.myView = new app.MyView;
// Third attempt, this time checking if listenTo will allow the view to update every time the event is fired. It still only works on the first time and fails to render for consecutive clicks, even though the console is showing updated models
app.myView.listenTo(app.myView.collection, 'sync', app.myView.render);
Below is a working code, I just added initial call to fetch data at the end
app.myView.fetchData();
And uncommented your .on('sync', ... inside initialize
this.collection.on('sync', this.render, this);
I tested it with jsbin so you can see what's was wrong for you. As a result I see initial rendering of fetched data and clicking the #submit will re-fetch and re-render view.
app.MyView = Backbone.View.extend({
el: 'body',
events: {
'click #submit': 'fetchData'
},
initialize: function() {
this.collection = new app.MyCollection();
// Here is a binding to each fetch data and on success render view
this.collection.on('sync', this.render, this);
},
render: function() {
console.log('rendering');
var schedule = $('#schedule');
var template = _.template($('#times-template').html());
schedule.html(template({ collection: this.collection.toJSON() }));
},
fetchData: function() {
that = this;
var stationQuery = {};
stationQuery.station_start = $('#start').val();
stationQuery.station_end = $('#end').val();
var query = stationQuery;
this.collection.fetch({
data: query,
success: function(collection, response) {
console.log('fetching data');
},
error: function(collection, response) {
console.log('error on fetch', response);
}
});
},
});
app.myView = new app.MyView;
// Here is first call to fetch data and on success render view
app.myView.fetchData();

Backbone js not populating a model with data using fetch()

I am using Backbone.js and trying to populate my model using fetch(). The problem I am having is that the returned data is not populating my model. I have found a similar question here. The difference is that inside of my success function I am not seeing any data changes nor is a 'change' event being fired.
The code:
Model
window.Company = Backbone.Model.extend({
urlRoot: "/api/company",
defaults:{
"id":null,
"name":"",
"address":"",
"city":"",
"state":"",
"phone":""
},
events: {
'change': 'doChange'
},
doChange: function(event) {
alert('company changed');
}
})
The Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"home",
"company/:id":"companyDetails"
},
initialize:function () {
var user = new User();
this.headerView = new HeaderView({
model: user
});
$('.header').html(this.headerView.el);
console.log("router initialized.");
},
companyDetails: function (id) {
var company = new Company({
id: id
});
company.fetch({
success: function(){
console.log('company.id is ' + company.id);
console.log('company.name is ' + company.name);
console.log('company.address is ' + company.address);
$("#content").html(new CompanyView({
model: company
}).el);
}
});
}
});
JSON
{"address":"555 Main St","name":"Confused Technologies","id":"8dc206cc-1524-4623-a6cd-97c185a76392","state":"CO","city":"Denver","zip":"80206","phone":"5551212"}
The name and address are always undefined. I have to be overlooking something simple???
Edit
Including the view that erroneously left out passing the model to the template.
View
window.CompanyView = Backbone.View.extend({
initialize:function () {
this.render();
console.log('CompanyView initialized');
},
render:function (eventName) {
$(this.el).html(this.template());
return this;
}
})
The attributes are not stored directly on the model. They are stored in an attributes hash, so you would access them through company.attributes, though company.get(attribute) is the way it's usually done. Along the same lines, you would pass company.toJSON() to your template function, as that returns a cloned hash of the model's attributes.
As for your change event not firing, I assume you mean the change: doChange in the model's events hash. Backbone Models do not actually do anything with an events hash. That's for delegating DOM events on Backbone Views. I bet if you put company.on("change", function (model) { console.log(model.toJSON()); }) before your fetch call and removed the success callback, you'd see your model in the console.
Also, I don't think your $("#content").html... line is going to work like you expect. I'd rewrite your router callback like this:
companyDetails: function (id) {
var company = new CompanyView({
el: "#content",
model: new Company({ id: id })
});
// This line would be better in your view's initialize, replacing company with this.
company.listenTo(company.model, "change", company.render);
company.model.fetch();
}
CompanyView#render would typically pass this.model.toJSON() to a template function that returns html, and pass that to this.$el.html(). So something like this.$el.html(this.template(this.model.toJSON()));
OK. The problem with not updating my model was as far as I can tell an async issue. I updated the success callback to include the data parameter like so:
success: function (data) {
$('#content').html(new CompanyView({
model: data
}).el);
}
Note that I am not passing the company object as the model rather the raw returned data. This solved my model problem.
I mentioned in a comment that this started with my underscore template variables `<%= name %>' etc... being empty. I changed my view to this:
window.CompanyView = Backbone.View.extend({
initialize:function () {
this.render();
console.log('CompanyView initialized');
},
render:function (eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
})
Those to things got both my model updated and variables propagating to the template.

Scope error using Backbone.js?

I believe my problem relates to scope somehow, as I'm a js newbie. I have a tiny backbone.js example where all I am trying to do is print out a list of items fetched from the server.
$(function(){
// = Models =
// Video
window.Video = Backbone.Model.extend({
defaults: function() {
return {
title: 'No title',
description: 'No description'
};
},
urlRoot: 'api/v1/video/'
});
// VideoList Collection
// To be extended for Asset Manager and Search later...
window.VideoList = Backbone.Collection.extend({
model: Video,
url: 'api/v1/video/'
});
// = Views =
window.VideoListView = Backbone.View.extend({
tagName: 'ul',
render: function(eventName) {
$(this.el).html("");
_.each(this.model.models, function(video) {
$(this.el).append(new VideoListRowView({model:video}).render().el);
}, this);
return this;
}
});
// VideoRow
window.VideoListRowView = Backbone.View.extend({
tagName: "li",
template: _.template("id: <%= id %>; title: <%= title %>"),
className: "asset-video-row",
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
// Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"assetManager"
},
assetManager:function() {
this.assetList = new VideoList();
this.assetListView = new VideoListView({model:this.assetList});
this.assetList.fetch();
$('#content').html(this.assetListView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
// The following works fine:
window.mylist = new VideoList();
window.mylistview = new VideoListView({model:window.mylist});
});
If I access mylist.fetch(); mylist.toJSON() from the console, mylist populates fine. I can tell that this.assetList.fetch() is accurately fetching the data from the backend, but it doesn't appear to be adding the objects to this.assetList.
The fetch method on Backbone collections is asynchronous:
Fetch the default set of models for this collection from the server, resetting the collection when they arrive. [...] Delegates to Backbone.sync under the covers, for custom persistence strategies.
And Backbone.sync says:
Backbone.sync is the function that Backbone calls every time it attempts to read or save a model to the server. By default, it uses (jQuery/Zepto).ajax to make a RESTful JSON request.
So fetch involves an (asynchronous) AJAX call and that means that you're trying to use the collection before fetch has retrieved the data from the server. Note that fetch supports success and error callbacks so you can do this instead:
var self = this;
this.assetList.fetch({
success: function(collection, response) {
$('#content').html(self.assetListView.render().el);
}
});
Or you could bind a callback to the collection's reset event as fetch will reset the collection. Then render your assetListView when the collection's reset event is triggered.
Also, your assetList is a collection so you should be doing:
this.assetListView = new VideoListView({collection: this.assetList});
and:
window.VideoListView = Backbone.View.extend({
tagName: 'ul',
render: function(eventName) {
$(this.el).html("");
_.each(this.collection.models, function(video) {
// ...

How do I bind an event to a model that isn't loaded yet?

So I've got a pretty simple backbone app with a model, a collection, and a couple of views. I'm fetching the actual data from the server by doing a collection.fetch() at page load.
My problem is that one of my views is a "detail" view, and I want to bind it to a particular model - but I don't have the model yet when the page loads. My code looks a lot like this:
window.App = {
Models: {},
Collections: {},
Views: {},
Routers: {}
}
App.Models.Person = Backbone.Model.extend({
urlRoot: '/api/people'
});
App.Collections.People = Backbone.Collection.extend({
model: App.Models.Person,
url: '/api/people'
});
people = new App.Collections.People()
App.Views.List = Backbone.View.extend({
initialize: function() {
this.collection.bind('reset', this.render());
},
render: function() {
$(this.el).html("We've got " + this.collection.length + " models." )
}
});
listView = new App.Views.List({collection: people})
App.Views.Detail = Backbone.View.extend({
initialize: function() {
this.model.bind('change', this.render());
},
render: function() {
$(this.el).html("Model goes here!")
}
});
App.Routers.Main = Backbone.Router.extend({
routes: {
'/people': 'list',
'/people/:id': 'detail'
},
list: function() {
listView.render();
},
detail: function(id) {
detailView = new App.Views.Detail({model: people.get(id)})
detailView.render()
}
})
main = new App.Routers.Main();
Backbone.history.start();
people.fetch();
But if I start with the detail route active, the people collection is empty, so people.get(id) doesn't return anything, so my new view has this.model undefined, and won't let me bind any events relating to it. The error is:
Uncaught TypeError: Cannot call method 'bind' of undefined
If I start with the list route active, then by the time I click on an item to bring up the detail view people is populated, so everything works.
What's the right way to bind model-related events for a "detail" view when you're fetching the data after page load?
You have a part of the answer here: Backbone.js Collections not applying Models (using Code Igniter)
Indeed, you need to wait that people.fetch finishes its ajax request before to call Backbone.history.start(); and trigger the actual route.
Your code should look like:
// [...]
main = new App.Routers.Main();
peoples.fetch({
success: function (collection, response) {
// The collection is filled, trigger the route
Backbone.history.start();
}
});
You can add a loader on the page and hide it when the collection is loaded.

Categories

Resources