Using Backbone.js as a front-end framework for my Rails app. In terms of Rails models I have a Publication model and an Article model. Each publication is associated with many articles and each article only belongs to one publication. When the user decides to delete a publication I want all the associated articles also deleted. When I say deleted I mean removed from the database.
The following code works in terms of deleting the specified Publication from the database but does not work for deleting the associated articles:
// destroys the proper publication but still needs to update the view
delete_publication: function(id){
var publication = new SimpleGoogleReader.Models.Publication({id: id});
publication.fetch({
success: function(x){
}
});
publication.destroy();
var articles = new SimpleGoogleReader.Collections.Articles();
articles.fetch({
data: {publication_id: id},
success: function(x){
}
});
articles.destroy();
}
});
I also tried moving the articles.destroy() line inside the success function but that did not work either. I could be wrong but I think by the time I call the .destroy() function on articles I am no longer working with a Collection object. Am I going about this wrong? I want a Collection object just not containing every Model, only the specified ones.
Any ideas?
Have you tried to delete the articles first :
// destroys the proper publication but still needs to update the view
delete_publication: function(id){
var articles = new SimpleGoogleReader.Collections.Articles();
articles.fetch({
data: {publication_id: id},
success: function(x){
}
});
articles.reset(); // empty the collection
articles.sync(); // persist the state of the collection to the server
var publication = new SimpleGoogleReader.Models.Publication({id: id});
publication.fetch({
success: function(x){
}
});
publication.destroy();
}
});
Related
I'm using OData v4 to load data from my backend to my frontend (developed with SAP UI5) and I am using a form to display a detail page. When I click the "edit" button I'm able to edit the data. My implementation is similar to this example: https://sapui5.hana.ondemand.com/explored.html#/sample/sap.ui.layout.sample.Form354/code/Page.controller.js
When editing something, the data is directly edited at the model and, therefore, updated at the backend. However, I want to be able to choose if I want to save the changes or if I want to cancel the edit before it is updated at the backend.
I read on other questions that one can copy the ODataModel to a JSONModel and use that copy instead, by doing something like:
var oModel = this.getView().getModel();
var oModelJson = new sap.ui.model.json.JSONModel();
oModel.read("/Data", {
success: function(oData, response) {
oModelJson.setData(oData);
sap.ui.getCore().setModel(oModelJson, "oJSONModel");
alert("Success!");
},
error: function(response) {
alert("Error");
}
});
However, the read method seems not to be available for OData v4. the code of my controller where the data is loaded looks like following:
onInit: function() {
this.oModel = new ODataModel({
groupId : "$direct",
synchronizationMode : "None",
serviceUrl : '/odata/'
});
this.getView().setModel(this.oModel, 'oModel');
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("details").attachPatternMatched(this._onObjectMatched, this);
this._showFormFragment("display");
},
_onObjectMatched: function (oEvent) {
this.getView().bindElement({
path: "/Data(" + oEvent.getParameter("arguments").dataPath + ")",
model: "oModel"
});
//I want to copy the data from the ODataModel to the JSONModel here
},
What's the best way to accomplish this? And how to do it with OData v4?
I suppose you want to resetChanges in case user cancels the save.
For V2 ODataModel, there is deferedGroup concept which you can use to resetChanges or submitChanges.
I have not much experience with V4. Though from the documentation, it is possible.
Please try to pass a updateGroupId in the constructor. Then you can choose resetChanges or submitBatch by group Id.
mParameters.updateGroupId? The group ID that is used for update requests. If no update group ID is specified, mParameters.groupId is used. Valid update group IDs are undefined, '$auto', '$direct' or an application group ID, which is a non-empty string consisting of alphanumeric characters from the basic Latin alphabet, including the underscore.
Thank you!
I am creating simple book library using backbone.js. Functionalities are very simple.
1.) Get All Books
2.) Get Particular Book (from server)
3.) Display Books
from the collection i can get all the books. But how can I get single book from server using Models ? I need to make ajax call to server to get the book details. since the details may update pretty soon (I don't want to get it from all books collection)
So far I created Views, Model, Collections as follows
var Book: Backbone.Model.extend({
defaults:{
name: "",
id: ""
}
});
var Library: Backbone.Collection.extend({
model: Book,
url: function(){
return "/books";
},
fetchBooks: function(){
fetch({
success: success_callback,
error: error_callback
});
},
fetchBook: function(bookId){
//how to get single book ?
}
});
How do I get single item from the server using my existing models and collections ?
If you need to use your Model outside the scope of a collection, you need to specify the urlRoot property:
var Book: Backbone.Model.extend({
defaults: {
name: ""
// ** See Note on Defaults below ...
},
urlRoot: "/books"
});
After that you can initialize the Model with a given ID and fetch it:
fetchBook: function(bookId){
var book = new Book({ id: bookId });
book.fetch({
success: function() {
console.log('Received book: ' + book.toJSON());
}
});
}
This will result in a GET request to `/books/{id}'. Assuming the server returns JSON, all your results will be populated as attributes on the book instance.
** NOTE on Defaults: You definitely shouldn't have an id default to an empty string, as Backbone uses the presence or absence of an id to determine if a model has been saved on the server or not. For instance if you ever use model.save(...), Backbone will issue a POST if the id does not exist, or a PUT if the id does exist.
I have a collection which is fetched from a REST endpoint, where it receives a JSON.
So to be completely clear:
var Products = Backbone.Collection.extend({
model: Product,
url : 'restendpoint',
customFilter: function(f){
var results = this.where(f);
return new TestCollection(results);
}
});
var products = new Products();
products.fetch();
If I log this, then I have the data. However, the length of the object (initial) is 0, but it has 6 models. I think this difference has something to do with what is wrong, without me knowing what is actually wrong.
Now, if I try to filter this:
products.customFilter({title: "MyTitle"});
That returns 0, even though I know there is one of that specific title.
Now the funky part. If I take the ENTIRE JSON and copy it, as in literally copy/paste it into the code like this:
var TestCollection = Backbone.Collection.extend({
customFilter: function(f){
var results = this.where(f);
return new TestCollection(results);
}
});
var testCollectionInstance = new TestCollection(COPY PASTED HUGE JSON DATA);
testCollectionInstance.customFilter({title: "MyTitle"});
Now that returns the 1 model which I was expecting. The difference when I log the two collections can be seen below. Is there some funky behaviour in the .fetch() I am unaware of?
Edit 2: It may also be of value that using the .fetch() I have no problems actually using the models in a view. It's only the filtering part which is funky.
Edit 3: Added the view. It may very well be that I just don't get the flow yet. Basically I had it all working when I only had to fetch() the data and send it to the view, however, the fetch was hardcoded into the render function, so this.fetch({success: send to template}); This may be wrong.
What I want to do is be able to filter the collection and send ANY collection to the render method and then render the template with that collection.
var ProductList = Backbone.View.extend({
el: '#page',
render: function(){
var that = this; /* save the reference to this for use in anonymous functions */
var template = _.template($('#product-list-template').html());
that.$el.html(template({ products: products.models }));
//before the fetch() call was here and then I rendered the template, however, I needed to get it out so I can update my collection and re-render with a new one (so it's not hard-coded to fetch so to speak)
},
events: {
'keyup #search' : 'search'
},
search : function (ev){
var letters = $("#search").val();
}
});
Edit: New image added to clearify the problem
It's a bit tricky, you need to understand how the console works.
Logging objects or arrays is not like logging primitive values like strings or numbers.
When you log an object to the console, you are logging the reference to that object in memory.
In the first log that object has no models but once the models are fetched the object gets updated (not what you have previously logged!) and now that same object has 6 models. It's the same object but the console prints the current value/properties.
To answer your question, IO is asynchronous. You need to wait for that objects to be fetched from the server. That's what events are for. fetch triggers a sync event. Model emits the sync when the fetch is completed.
So:
var Products = Backbone.Collection.extend({
model: Product,
url : 'restendpoint',
customFilter: function(f){
var results = this.where(f);
return new TestCollection(results);
}
});
var products = new Products();
products.fetch();
console.log(products.length); // 0
products.on('sync',function(){
console.log(products.length); // 6 or whatever
products.customFilter({title: 'MyTitle'});
})
It seems like a response to your ajax request hasn't been received yet by the time you run customFilter. You should be able to use the following to ensure that the request has finished.
var that = this;
this.fetch({
success: function () {
newCollection = that.customFilter({ title: 'foo' });
}
});
I have a this collection
var Product = Backbone.Model.extend({
urlRoot:'/api/products',
idAttribute:'id'
});
var Products = Backbone.Collection.extend({
model:Product,
url : '/api/products/'
});
I fetch Models from server
var products = new Products();
products.fetch();
At some moment i change data on server and want 'products' to have the new data (renew the collection). I tried something like this
products.reset()
products.fetch()
But after this products in empty. Help me plz
It is possible that the server does not respond in time. The proper way to reset your data is
products.fetch({reset: true}).success(function(response){
// update the view here if necessary
})
It might be because you're checking the contents of products before the server has time to return with the data. Check if products is still empty on the success callback instead:
products.fetch({
success: function() {
console.log(products.toJSON()); //is it still empty here?
}
});
I’m trying to understand how and where to use data after a fetch using Backbone.js but I’m a little confused.
I’ll explain the situation.
I have an app that, on the startup, get some data from a server. Three different kind of data.
Let’s suppose Airplanes, Bikes, Cars.
To do that, I’ve inserted inside the three collections (Airplanes, Cars, Bikes) the url where to get these data.
I’ve overwrited the parse method, so I can modify the string that I get, order it, and put it in an object and inside localstorage. I need it to be persistent because I need to use those 3 data structure.
So with a fetch i get all those data and put them inside localstorage. Is it correct doing it that way?
Now i need to make other calls to the server, like “get the nearest car”.
In the view i need to see the color, name and model of the car, all that informations are inside the object “Cars” in localstorage.
In my view “showcars.view” I just call a non-backbone js, (not a collection, model or view) where i get all the informations i need. In this js i do:
var carmodel = new Car(); //car is the backbone model of the cars
carmodel.url = '/get/nearest/car'; //that give id of the nearest car
carmodel.fetch ({
success: function () {}
//here i search the Cars object for a car with the same id
//and get name, color, model and put them in sessionstorage
})
So after that call, in the view I can get the data I need from the sessionstorage.
Is that a bad way of doing things? If so, how i should fetch and analyze those informations? I should do all the calls and operations inside the models?
Thanks
This would be the way that you might implement what you want.
var Car = Backbone.Model.extend();
var Cars = Backbone.Collection.extend({
model: Car,
url: '.../cars'
});
var NearestCar = Backbone.Model.extend({
url: '...nearest/car'
});
var cars = new Cars();
var nearestCar = new NeaerestCar();
cars.fetch({
success: function() {
nearestCar.fetch({
success: function(model) {
var oneYouWant = cars.get(model.get('id'));
// do something with your car
// e.g.:
// var carView = new CarView({model: oneYouWant});
// $('body').append(carView.render().el);
});
});
});
});
In general, Backbone keeps everything in memory (that is, the browser memory) so there is no need to save everything to local storage, as long as your Collection object is somehow reachable from the scope you are sitting in (to keep things simple let's say this is the global window scope).
So in your case I will have something like three collections:
window.Cars
window.Airplanes
window.Bikes
Now you want the nearest. Assuming you are in a Backbone View and are responding to an event, in your place I would do something like this (just shows the meaningful code):
var GeneralView = Backbone.View.extend({
events: { "click .getNearestCar": "_getNearestCar" },
_getNearestCar: function () {
$.getJson('/get/nearest/car', function (data) {
// suppose the data.id is the id of the nearest car
var nearestCar = window.Cars.get(data.id)
// do what you plase with nearestCar...
});
}
});