I have a good asp.net mvc background and I have recently started to learn Rails platform.
I'm trying to map my asp.net mvc experience to Rails concepts and want to ask you a question:
Does Rails have the same cool binding feature that allows me to use View Model to display a form and bind form data after submit to a View Model? I'm a bit confused, because I do understand that all Rails models are actually ActiveRecord's model, not a something like POCO objects, but can I do this in Rails? Can I have my custom model (non ActiveRecord) and bind form's data to this model after form's submit.
Thank you
You can if your custom model has an initializer function that accepts a hash as argument. That way you can create a form with the form_for tag and in the controller created a new object like so:
myObject = myObject.new(params[:myObject])
this will create a new object with the data from the hash, submitted by the form.
Ofcourse the initializer of your object needs to know which values are potentially available in the hash and extract them.
Your object could looks like this:
class MyObject
attr_accessor :prop1, :prop2, :prop3
def initialize(args = {})
self.prop1 = args[:prop1] if args.keys.contains(:prop1)
self.prop2 = args[:prop2] if args.keys.contains(:prop2)
self.prop3 = args[:prop3] if args.keys.contains(:prop3)
end
end
The above is a bit of pseudo code from ruby, but you should get the idea.
Related
In ASP.NET, when I want to send a list of model instances to view layer, I convert them into another type (ModelView) by the following code:
var userViewModels = users.select(new {
Name = Name,
UserName = Username
});
I do this because I don't want to send all of my user model data (like password) to view layer. I put this code in my business logic Layer.
I'm using AJAX, and I'm sending my data by JSON protocol. What is the best practice in Ruby on Rails to do a similar action?
In RoR you simply pass models to your views.
Also, view in RoR can directly access (though it's not recommended) models from database. So "hiding" models does not make sense here.
You can consider rails generated scaffold as a close to the best practices.
Controller is used for selecting models from database
View is used for rendering model to the user
Model is the main place to store business logic
Although it is probably unnecessary, as dimakura pointed out, you can select arbitrary attributes when finding by using the select method. Your example would end up looking something like:
#users = User.select(:name, :username)
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 have a Search model and controller. The business logic is that if the user's keyword exact matches a product's model number, redirect them to the product page.
In this situation, should I just do the redirection from inside the model (where most of the logic resides already)?
Or should I return a flag or something to the controller so I can handle the redirect?
THe model object cannot ans shall never do a redirect. The application logic is the duty of the controller, so the controller should ask the model object (as result of a request) if the product matches a model number, and then the controller does the redirect. The model object should not know anything about controller or views. This is part of the "Model-View-Controller concept" this is implemented by Rails.
Rails implements the model as the ActiveRecord pattern, so it is ok that the model object is responsible for the database, and that includes the search on the database. See the many options you have in the Rails Guides for ActiveRecord Queries to see what falls in the responsibility of the model objects.
Always remember MVC pattern: MVC in Rails
Model must not take care of redirection or some other stuff that related to controller. Let redirection be in controller.
Ok, let's assume we are working with ASP.NET MVC 2 (latest and greatest preview) and we want to create AJAX user interface with jQuery. So what are our real options here?
Option 1 - Pass Json from the Controller to the view, and then the view submits Json back to the controller. This means (in the order given):
User opens some View (let's say - /Invoices/January) which has to visualize a list of data (e.g. <IEnumerable<X.Y.Z.Models.Invoice>>)
Controller retrieves the Model from the repository (assuming we are using repository pattern).
Controller creates a new instance of a class which we will serialize to Json. The reasaon we do this, is because the model may not be serializable (circular reference ftl)
Controller populates the soon-to-be-serialized class with data
Controller serializes the class to Json and passes it the view.
User does some change and submits the 'form'
The View submits back Json to the controller
The Controller now must 'manually' validate the input, because the Json passed does not bind to a Model
See, if our View is communicating to the controller via Json, we lose the Model validation, which IMHO is incredible disadvantage. In this case, forget about data annotations and stuff.
Option 2 - Ok, the alternative of the first approach is to pass the Models to the Views, which is the default behavior in the template when you start a new project.
We pass a strong typed model to the view
The view renders the appropriate html and javascript, sticking to the model property names. This is important!
The user submits the form. If we stick to the model names, when we .serialize() the form and submit it to the controller it will map to a model.
There is no Json mapping. The submitted form directly binds to a strongly typed model, hence, we can use the model validation. E.g. we keep the business logic where it should be.
Problem with this approach is, if we refactor some of the Models (change property names, types, etc), the javascript we wrote would become invalid. We will have to manually refactor the scripting and hope we don't miss something. There is no way you can test it either.
Ok, the question is - how to write an AJAX front end, which keeps the business logic validation in the model (e.g. controller passes and receives a Model type), but in the same time doesn't screw up the javascript and html when we refactor the model?
Stick with Option 2, but there are ways to test the code. You can use a web application testing tool like WatiN or Selenium to perform integration tests on your HTML pages. Also, FireUnit gives you the ability to unit test your JavaScript code (you'll need Firefox and Firebug in order to use it).
In the spirit of full disclosure, I haven't tried out MVC 2 yet. However, I've been using MVC 1 for some time now and have used these tools with some pretty good results.
Problem with this approach is, if we
refactor some of the Models (change
property names, types, etc), the
javascript we wrote would become
invalid.
I dont see how changing a property of the model changes javascript-code. Usually you hijack the submit event of a form and submit it via ajax. No properies envolved, a long as you take option 2.
Changing properties might break your MVC - application, but thats not specific to ajax.