Restangular - Get object w/id, edit object, update object - angularjs

I can't seem to figure this out -
I want to do something like:
content = Restangular.one('contents', 2).get()
content.name = "Chunky Bacon"
content.put()
But this doesn't work - content doesn't have a put function, and interestingly, it doesn't have a name, either (is that because it's a "promise"?)
Now, the Restangular docs do describe updating an object, but you get that object as part of a collection:
content_id2 = Restangular.all("contents").getList()[2]
content_id2.name = "Chunky Bacon"
content_id2.put()
And this works. But! Not only have I fetched the entire list, I can't figure out how to easily get my content by it's ID; something like Restangular.all("contents").getList().find(2)
customPUT(..) seems like the next best bet:
Restangular.one("contents", 2).customPUT("", {"content" : {"name" : "Chunky Bacon"} } )
Only when the Rails server receives it, params["content"] == {id: 2} - it seems like something on the client side is stomping that value. (Again, note, no name field). Now, I can work around it simply by doing something like:
Restangular.one("contents", 2).customPUT("", {"content_data" : {"name" : "Chunky Bacon"} )
but it really seems like I'm just missing how you're supposed to do this.

Alright, found it, thanks to this question:
#In the beginning:
Restangular.one("content", 2).get().then(function(c){
$scope.content = c
})
#Elsewhere:
$scope.content.name = "Chunky Bacon"
#Finally:
$scope.content.put()

Related

How use set method on model Backbone to change a data?

I'm new with Backbone and I think I misunderstand the use of get/set with the model. I made a really simple example with IPython widget :
class Automaton(widgets.DOMWidget):
from IPython.display import Javascript
_view_name = traitlets.Unicode('AutomatonView', sync=True)
nodes = traitlets.List(sync=True)
%%javascript
require(['widgets/js/widget'], function(WidgetManager){
var AutomatonView = IPython.DOMWidgetView.extend({
render: function(){
var n_nodes = this.model.get("nodes").slice();
n_nodes[0] += 1;
this.model.set("nodes", n_nodes);
return this;
},
});
WidgetManager.register_widget_view("AutomatonView", AutomatonView);
});
So now I can call the widget like that:
a = Automaton(nodes=[1])
What I understand here is: nodes is passed to the model and now in the model nodes = [1], then I create a new node [2] that I'm setting to the model, so I'm expected for the nodes value be [2] now, but If I ask
a.nodes
It's say:
[1]
Sorry if I don't understand something really basic, if you can explain whats is happening here it will be really useful for me. I checked if the n nodes is [2] with a console log and it's fine so it's really with "this.model.set" the problem.
Are you sure you're rendering the view? Try to put this code into an initialize method instead, and everything should work just fine. By the way, this should work even without a set method call:
initialize: function() {
var n_nodes = this.model.get("nodes");
n_nodes[0] += 1;
return this;
},
Maybe that can help someone but I fixed my error with adding
this.touch();
I found my answer on this example : http://nbviewer.ipython.org/github/ipython/ipython/blob/2.x/examples/Interactive%20Widgets/Custom%20Widgets.ipynb
" it is very important that we call this.touch() to let the widget machinery know which view changed the model "

Restangular - Removing property from element

Im trying to remove a property element before doing the submission of that element.
$scope.changeTitle = function(song, title){
song.title = title;
delete song.label;
song.put();
}
When doing so it seems that the property "label" is removed. When I do the PUT operation the object actually has the property label.
// This is the object I'm sending (checked from the chrome dev tool - network)
{
artist: "XXXXX"
title: 'XX'
label: []
}
Is there any way to remove a property from an element?
If you inspect the object's put method in Developer console, you'll find that the referenced object is actually your original object (even after changes). In order fix the reference, use Restangular.copy() before you manipulate the object.
Earlier when you wrote something along the lines of:
$scope.song = restangular.one(song, 1).get().$object;
You should instead write:
$scope.song = restangular.copy(restangular.one(song, 1).get().$object);
To see the related issue, checkout: https://github.com/mgonto/restangular/issues/55
You can use underscore's _.omit function (http://underscorejs.org/#omit)
song = _.omit(song,'label');

backbone.js not updating id of model object after save, why not?

I have been trying out backbone.js and have been stymied when I create a new model object then call model.save(). I am expecting the backbone.js default behavior to update the model object with the id from the database but it is not. Is this not supposed to happen? I have verified that I am getting a post with the attributes in json format. My server saves the json to a table and then returns the json with a new id field to backbone.js. Is this correct? Should my server return the entire new object or just the id or what?
//contents of the POST from backbone.js
{ "text":"this is a test" }
//reply from my server
{ id:"15", text:"this is a test" }
My sample code is below
var SQLRow = Backbone.Model.extend({
table:"",
urlRoot:'db',
url:function () {
return "/" + this.urlRoot + "?table=" + this.table +
"&id=" + this.attributes.id;
}
});
var Xtra = SQLRow.extend ({
table:'Xtra'
});
var row = new Xtra({
text: "this is a test"
});
alert(row.url());
row.save()
alert("row:" + row.get("id"));
Tough to tell from your post. Two ideas :
1) the response from the server isn't successful What does your save call return ?
2) Your "id" attribute is named something other than ID. To account for the different name add the following to your model :
idAttribute : "MyModelsID",
EDIT
You're likely facing a timing issue, where the alert fires before the ID has returned. Instead of your last two lines try this :
row.save( null,
{
success : function(model, response) { alert(model.get('id'); }
}
);
ALTERNATIVE
As #mu_is_too_short mentioned, another way is to listen for the change even on the model and respond to the event. (i was just trying to keep the answer as close to your code as possible). But something like the following pseudo code should get you started...
var myView = Backbone.View.extend({
....
initialize : function () {
this.collection.bind('change', this.SOME_LISTENING_FUNC );
}
});
OR, if you're in a collection/view-less world something like this creates a listenr ...
row.on('change', function() { /* do stuff */ }, this);
This answer is based on one comment of Cjolly in the answer above.
It is essential for making the Backbone.Model.save([attributes],[options]) successful in assiging the model with the newly generated model's id from the server, that the server returns the model's id in a JSON string like this { "id" : <the id> }. (note it is "id" and not id).
In essence backbone rightly expects a JSON string and in contrast to how objects may be defined in Javascript without quoted keys, JSON requires the object keys to be quoted (see JSON Spec - does the key have to be surrounded with quotes?)
Since according to Cjolly's comment this has been the essential problem, I want to hightlight this solution in an second answer. Partially because I was hit by the very same problem and only by reading througth the comments I was able to receive the insight.
I've faced the same issue and what I've found is that my validate function of the saved model actually invalidates the model returned from the back end. That's why my fields were not updated properly.
Maybe its a little outtimed, but today I had the same missing id.
It turns out, that the server just sends a Header 'Location' with a redirect containing the new id, but dosen't return the persisted object.
Adding the object to the response was the solution.
It seems, that not returning the object is standard behavier with Roo(Spring) generated Json-Controllers.

Backbone - fetched model, set an attribute(modify), then save model, it should update but sending POST request

I created my web site having 2 types of users: admin and user. So, I created 3 pages mainpag.html, admin.html, user.html. and separate models, views, collections, routers.js files for each of them. After logging in, as I am sending users to separate HTML pages with different models, I can't automatically get user model. so I did like this:
First, I made AJAX call to server, asking for the _id (username in session, so I can get id)
from the id, I fetched the model, by model.fetch(), then I got my usermodel with all attributes.
then in the success callback of fetch, I did model.save({weight: "somevalue"}). According to me, it should update right, as the model is already available, that attribute weight also available with some old value, but it is sending POST request, also when I tried model.isNew(), it returned true. Where am I wrong? how can I update my model? I will post more details if required.
More details:
If I remove that save method, then I am getting correct attributes in the model.
If I don't remove that save method, that success and error callbacks are also appearing as attributes in the model.
Code:
addWeight : (e)->
arr=new Array()
arr['_id']=app._id
console.log "asdasd"
console.log arr
console.log arr['_id']
#user_model =new UserModel(arr)
#user_model.fetch({
success : (model,res,options) =>
console.log model
console.log res
arr=new Array()
arr['_id']=e.target.id
#arr['action']='weight' #means , update weight
##user_model.setArr(arr)
##user_model.set({weight : arr['_id']})
console.log "new : "+#user_model.isNew()
#user_model.save({weight : e.target.id})
##user_model.save({
# success : (model,res,options) =>
# console.log "model updated: "+JSON.stringify(model)
# console.log "Res : "+JSON.stringify(res)
# error : (model,res,options) =>
# console.log "Error : "+JSON.stringify(res)
#})
error : (model,res,options) =>
console.log "Error "
})
the above code is written in coffeescript, so even if you don't know coffeescript, don't worry, you can understand easily, and those # mean, it is a comment. here we follow indentation instead of braces.
one more doubt, a model's URL must be changed dynamically according to the requirement, right? what is the best way to achieve that? I am doing like this:
I am populating "array" containing the required fields that should be present in the URL. In model, s init func, I am using #arr=arr, then in URLs function, I check like this.
url : ->
if #arr['id']
"/user/#{#id}"
Is my approach right, or any better approach is there for dynamically setting URLs. Or can I directly set the URLs like this:
#user_model.setUrl "/someurl/someid" //this setUrl method is available in model's definition
#user_model.fetch() or save() or watever that needs url
Just a hunch, but you mentioned that you call model.fetch() to retrieve the _id field. Be sure to either return an id field instead _id (notice the underscore).
The call to model.isNew() returning true is an indicator that the id property was never set from the model.fetch() call.
I look forward to a possible further explanation with your code...
Looking at your code:
/* The model needs an 'id' attribute in order to marked as not new */
#user_model = new UserModel(id: arr['_id'])
Actually if you call
model.set({weight: "somevalue"});
It will update the value in the model, but it won't send a POST request
model.save(attribute);
Actually calls Backbone.sync as you probably know.
EDIT :
You might want ot set
m = Backbone.Model.extend({
idAttribute: '_id'
});
to every model, because the isNew method actually checks if the model has id attribute
Regarding to this you could see here that .set doesn't call backbone.sync here : http://jsfiddle.net/5M9HH/1/

Parameterizing the name of the store in backbone-localStorage.js

The standard way to use the localStorage plugin for Backbone.js works like this:
App.WordList = Backbone.Collection.extend({
initialize : function(models, options){
},
localStorage : new Store('English')
}
But I want to make different, parallel wordlist collections in different languages. So, I want to be able to instantiate the name of the Store upon initialization of the collection. AFAICT, this works ok:
App.WordList = Backbone.Collection.extend({
initialize : function(models, options){
this.localStorage = new Store(options.language);
}
}
Then I can instantiate a WordList like:
english = new Wordlist([], {language: 'English'});
Or:
chinese = new Wordlist([], {language: 'Chinese'});
The thing is, I haven't really seen this done in any other examples and I'm wondering if anyone out there would have any "Eek! Don't do that, because..." sorts of reactions.
EDIT
I should add that I have already tried doing it this way:
App.WordList = Backbone.Collection.extend({
initialize : function(models, options){
},
localStorage : new Store(options.store)
}
And then:
chinese = new Wordlist([], {language: 'Chinese'});
But for some reason options.store is coming up undefined.
It's easier to explain myself as an answer, so I'll go ahead and give one.
In:
App.WordList = Backbone.Collection.extend({
initialize : function(models, options){
....
},
localStorage : new Store(options.store)
})
This is really little different from
var newInstanceConfig = {
initialize : function(models, options){
....
},
localStorage : new Store(options.store)
}
App.WordList = Backbone.Collection.extend(newInstanceConfig);
Think of it this way; there's nothing magical about the object being passed in to Backbone.Collection.extend(...). You're just passing in an ordinary object. The magic happens when Backbone.Collection.extend is invoked with that object as a parameter
Thus, the options parameter of the object method initialize is completely different that which is being passed in to new Store(...). The function being assigned initialize is defining the scope of options. Who knows where the one referred to in new Store(options.store) is defined. It could be window.options or it could be options defined in some other scope. If it's undefined, you're likely getting an error
That being said, I only see two or three strategic options (oh jeez, forgive the pun please!).
Whenever you're creating a new instance of the collection, either:
Pass in the language and let your Backbone collection create the new Store(..) where needed.
Pre-Create the Stores and either pass or give the specific Store want to that instance (either directly through its constructor or maybe you have your constructor "look-up" the appropriate pre-created Store).
And finally, I guess you could delegate the task of creating stores to another object and have it implement either options one or two. (Basically a Store Factory/Resource Manager kinda thing).
What you need to figure out is which one of those strategies should work for you. I have never used localStorage so, unfortunately, I can't help you in that regard. What I can do is ask, is there ever going to be multiple instances created from App.Wordlist where there might accidentally be created two of the same kind of Store?
In fact, I've got another question. where is this Store defined? Are you sure that's not defined somewhere in one of your other API libraries you're using? Perusing the localStorage docs I know about mentions something of a Storage constructor but nothing of a Store. So you might want to figure out that as well.
Edit #1: Nevermind, I see you mentioned where Store was defined.
I got around this by creating a method which allows you to configure the localStorage after instantiation:
var PageAssetCollection = Backbone.Collection.extend ({
initialize: <stuff>
model: <something>
...
setLocalStorage: function ( storageKey ) {
this.localStorage = new Backbone.LocalStorage(storageKey),
},
});
you can then set the localStorage after you have set up the collection:
fooPageAssets = new PageAssetCollection();
fooPageAssets.setLocalStorage('bar');

Resources