When Backbone.js passes data to the server, it doesn't nest the CGI params
in a model-name hash like Rails expects, so you have to pick them out of all
the other params via Hash.select or a reverse merge or some other hack in the controller action, or use
the emulateJSON hack in Backbone which JSON-encodes all the values in to a
single "model" parameter and decode them in the controller. Is there a more elegant solution on either side?
You can override toJSON() in each Backbone Model so that it gives itself a root element. For example:
toJSON: function() {
return {modelname: _.clone(this.attributes)}
}
It is also easy to generalize this in a model base class, perhaps checking for the existence of a Model's 'jsonRoot' property or similar.
Related
Say I have a person model, with a name attribute.
Server-side, when I create a JSON representation of the model, I include a calculated value:
name: 'Jack'
name_backwards: 'kcaJ' # example
Client-side, when I do backbone_model.save(attributes), it includes name_backwards in the object it sends to the server. This isn't ideal, because it doesn't correspond to an actual attribute on the server.
PATCH is not an option because I'm using Rails 3.2. Is there any way around this issue?
Seems a bit odd to me that your server is sending something out that it won't accept back. I see two options:
Filtering out an incoming name_backwards in your controller.
Stop sending name_backwards and let the client deal with it.
1 is easy, just delete :name_backwards from the appropriate part of params.
2 is a little more involved. First you'd stop adding name_backwards to the JSON that goes out. Then you could add name_backwards in your model's parse:
parse: function(response) {
response.name_backwards = reverse(response.name);
return response;
}
See this answer for a reliable way to reverse a string in JavaScript.
Then you'll want to ignore name_backwards in your model's toJSON:
toJSON: function() {
var o = _(this.attributes).clone(); // This is what the standard toJSON does.
delete o.name_backwards;
return o;
}
Then add a serialize method to use instead of toJSON when you're feeding your models to your templates:
serialize: function() {
return _(this.attributes).clone();
}
You can easily patch serialize into Backbone.Model.prototype if you want an easy way to have a consistent interface between your models and templates.
Override the toJSON method in your Backbone.Model subclass and select only the canonical fields from this.attributes.
Register view is rendered by backbone. When I submit the form to register a new user, I got the error like this.
I understand it's gonna check whether the email exists or not. But I don't know why I posted all my attributes but the backend seems not getting it.
I'd guess that your controller is looking at params[:user] to find the user's information but you don't have :user in your params. The manual talks about this:
Working with Rails
[...]
Similarly, Backbone PUTs and POSTs direct JSON representations of models, where by default Rails expects namespaced attributes. You can have your controllers filter attributes directly from params, or you can override toJSON in Backbone to add the extra wrapping Rails expects.
So adjust your controller to look at params rather than at something inside params or adjust your Backbone model's toJSON to look something like this:
toJSON: function() {
return { user: _.clone(this.attributes) };
}
If you change toJSON you'll want to adjust how you feed data to your templates to compensate for the toJSON return value change.
so, I've seen this working on a previous project in MVC3, so wondering if a) i've screwed it up, or b) MVC4 is doing something different (can't see it would be).
I have a model bound Razor view which submits to a controller action method (as is the way with MVC)
post action method:
[HttpPost]
[AutoMap(typeof(MyViewModel), typeof(MyDomainObj))]
public void PostAction(MyDomainObj obj)
{... etc.. etc..
The action filter eventually does something like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = filterContext.Controller.ViewData.Model;
NOTE: In Jimmy Bogard's example he used OnActionExecuted which I also tried..
The key issue I'm having is that at the pint where we get the "model" variable from the context, it's null. If I look at the filterContext.ActionParameters whilst debugging I can see a MyDomainObj instance!! which appears to (because it happens to have a prop name in common with the MyViewModel type) have been mapped from my form data!
So.. yes if I had MyViewModel type as the parameter to this method, then the param would be properly populated from the submitted form. But. I don't want to do that, I want to (and have done before based on JB's succinct how-to) converted the view model to domain model as part of the action executed/ing and then been able to just hit save on my domain model.
Summary - why is my ViewData.Model null on the post action filter?
Jimmmy also has/had a couple of ideas on how to implement post actions, along with another guy Matt Honeycutt who shares his view on how to do posts. I also believe Jimmy has gone in the direction of using explicit mapping in his get requests instead of attributes, as it can be hard to inject any extra code you need after mapping.
http://lostechies.com/jimmybogard/2011/06/22/cleaning-up-posts-in-asp-net-mvc/
http://trycatchfail.com/blog/post/Cleaning-up-POSTs-in-ASPNET-MVC-the-Fail-Tracker-Way.aspx
For a post you really want a couple of things imo, the original Entity and the Form Data. You could load the entity like you do for the GET request and do normal model binding to get the form data (remember you can accept a different model for post backs than you spit out in your view) then make the changes in the action.
Of course this would require using AutoMapper in your action, which you seem to be trying to avoid. But unless you write a custom model binder then you're not going to magically get the formdata in a model as the built in one looks at the action paramters to figure out what to bind. For this reason i'd recommend not using a domain model as a parameter for an action, as it may fill out fields that you don't wish it to.
I've also seen Jimmy using a ModelBinder to do something similar to your AutoMapGet, which may be another alternative method, but I'm guessing you've seen that post.
My standard post takes Matt's approach, moving the validation out into a global attribute so it can't be forgotten (there are some downsides to this) then accepting a new view model explicity for the form data, then I explicity load the entity, use automapper to update it and call save. Most the actions only end up being about 5 lines long.
I do avoid using the static methods on AutoMapper and pass a IMapper in via constructor injection. This allows me to make it a bit more testable if I really need to, however the methods are normally so simple the tests don't add all that much value.
I'm new to Backbone.js and working with JSON.
I'm having trouble working with the following JSON:
http://pastebin.com/FFEwc0Fb
What I want to achieve is to
create a model of that JSON data
bind the model to a view
bind the view to a template and learn how to show certain attributes of the data e.g. feed->title and the titles of each playlists which is feed->entry->array->title
Could someone provide sample code of that does the above or point me in the right direction?
Possible Alternative Solutions: I'm also using Rails in my App so I could use try using Rails to turn the JSON into the JSON I want it to be ...
look at the parse method on models and collections. After you make your fetch the response is always passed through a parse function that you define on your model or collection. In there you can go through the object and build up the attributes to be set to the model how you want them. For example:
parse: function(response) {
var attrs = {
title: response.feed.title,
author: response.feed.author
};
return attrs;
}
That's a small example of how you could do it within backbone.
public ActionResult RenderMyThing(IList<String> strings)
{
return View("RenderMyView");
}
How do I pass in strings?
routes.MapRoute("MyRoute", "RenderMyThing.aspx", new { controller = "My", action = "RenderMyThing" });
Is there a way I could pass in strings here?
Secondly, how does ASP.NET MVC know that action is my action, and controller is my controller. Like I saw this in samples, and it does work, but isn't it just an anonymous object with no type?
This is the provenance of model binding: the framework needs to have some instruction as to how to turn a "request", which comes out of the routing context, query string, forms collection, etc., into the parameters that your action method wants.
The DefaultModelBinder will generate a list if it sees that you have multiple key-value pairs with the same key (and appropriately typed/convertible values) - for the details, Phil wrote a good post about this:
If you need fancier binding requirements, you can implement a custom model binder and explicitly define how route values and the other bits get translated into objects (or collections of objects).