I have the following ViewModels :
public class MyViewModel
{
public BaseViewModel mySubViewModel;
}
public class ChildViewModel: BaseViewModel
{}
I then create a MyViewModel model, which contains a property of type ChildViewModel. In the View, it is displayed just fine.
Then I hit the save button to submit changes to my Model and I call the following controller:
[HttpPost]
public ActionResult Edit(MyViewModel model)
{
return null;
}
To my surprise, the property mySubViewModel is now of type BaseViewModel instead of ChildViewModel ! I have no idea what's going on here. What am I doing wrong ?
To my surprise, the property mySubViewModel is now of type
BaseViewModel instead of ChildViewModel ! I have no idea what's going
on here. What am I doing wrong ?
You shouldn't be surprised by that. You are doing nothing wrong. This is by design. The default model binder sees that your controller action is taking a MyViewModel argument and is attempting to bind the contents of the POST request to it. The default model binder has absolutely no way of knowing that you might have written some derived classes (such as ChildViewModel in your case) and that you want those derived classes to be used instantiated here. The fact that you have passed this concrete view model instance from your GET action to the view has no influence to the POST action. Think of it in terms of HTTP and what the default model binder sees. Think for example that this POST action could be invoked from some completely different client and not from a form submission. Could be for example an iPhone application performing an HTTP request to the POST action. See, now it makes perfect sense. The default model binder can only see the payload of the POST request and the type you specified as action argument. And that's what he does => it instantiates this types and binds its properties from the POST payload data.
So one possibility is to write a custom model binder that will instantiate the concrete instance of your view model you wish. I have exemplified such a custom model binder at this post. In this example I have included a hidden field inside the form that will contain the concrete type of the view model that we would like to be instantiated and then the custom model binder simply uses this information to create this type at runtime.
Related
I have seen a lot of people say that using Viewdata or ViewBag is not a good practice for this matter(displaying messages from the controller) because of security reasons. Everyone seems to suggest ModelState
My question is what is wrong with using viewdata to display error messages? If we arent supposed to use ViewData then what should we use it for?
As #shenku answered before, ViewData and ModelState reference the exact same thing, if you look at the code for System.Web.Mvc.Controller class you will see the implementation for ModelState is:
public ModelStateDictionary ModelState
{
get
{
return this.ViewData.ModelState;
}
}
and #JimmiTh said,
although the main use of ModelState from an "end developer"'s perspective is in the controller, ViewData is used as a container for all data that's communicated between the controller and the view. Which is why it also needs to include ModelState - because, although you'd rarely use it directly in the view, ModelState is where e.g. many of the HtmlHelper methods actually get the values from by default when rendering the view from a POST action - rather than Model.
Consider a fresh Asp.Net Core 2.1 MVC Web app created via the Visual Studio 2017 template. Now, consider a custom view (MyView) and also a controller (ActualController) so that the project structure looks similar to this picture:
The MyView shows nothing special, and it's off the point. However, the page should appear when the user enters an URL like http://(domain)/desired/myview or also via a hyperlink in the home page:
<a asp-area="" asp-controller="Desired" asp-action="MyView">MyView</a>
Now let's focus on the controller, which is a class named differently from what the routing expects:
[Route("desired")]
public class ActualController : Controller
{
[Route("MyView")] //without this the method won't be called
public IActionResult MyView()
{
return this.View();
}
}
From what I know, by decorating the controller with a Route attribute tells the URL resolver to match this class. However, the mapping works only if I explicitly add a (redundant) Route attribute on the target method/action. If I remove it, the path won't be found, and the server returns a 404-error.
The question is: why should be mandatory to decorate with Route the method, even the action is implicitly defined by the method name (as usual)?
NOTE: is rather simple for me to rename the controller class, but I'd like to know what are the reasons behind this behavior.
You are overriding the default route of [controller]/[action] with [Route("desired")]. Since you don't define an action parameter on controller level, all other routes have to be done explicitly.
Changing the top route parameter to [Route("desired/[action]")] should solve it and the method name will be used as parameter. You can still override single actions if you want to name them differently by adding the [Route("")] attribute to them.
Also see the docs (Token replacement in route templates) for further description on the route parameters
I'm working on creating n-tiered application where I will have two separate project
1) project EF (where it will have all my edmx...)
2) project MVC 4 (internet application.)
In my EF i have, I have my .edmx file and it generate couple of classes with all props as show below (as sample)...
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
public partial class Requester
{
public int Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
<//more...........>
}
everything is good so far!!
Back to MVC project
Now I will be creating a new Controller in my MVC project and when I'm trying to create Scaffolding and provide the Controller name and Controller expects a Model so the real question is:
What Model should I be passing here?
should I have the same class that EF created? or should I be creating another Model in my 'Model Folder` (MVC) and bind it? if yes than am I not creating duplicate property if I go ahead and create my same Model in MVC model Folder project?
What I'm trying to do? : Well my purpose of this exercise is to have my Data Access Layer (DAL) totally separate from MVC project.
any thoughts?
I'll suggest to create a view model so you can decorate the properties with view related stuff (i.e. UIHint). Also, this view model will be a reduced version of the class (for example, it can contain just the id of a related object instead of the whole object) making it easier for using as action parameters.
Also, you are talking about objects here, try not to think about "Data".
MVC really needs to be renamed VMVC - ViewModel View Controller.
The models in MVC have nothing to do with EF, Persistence, or your domain. They are a composition of multiple sources of data/settings/things which are represented/required in the View.
So create new View Models for your Views.
Edit:
All examples/tutorials which use EF Code First Models as View Models, are terrible tutorials / examples. They teach you bad practice because in the real world, you would never, and should never use those directly in your view.
The ViewModel is a composition or aggregation of data that's going into your view. For example:
If you had a product detail page, you might get the Product information from the Database, the availability of the product from a Web Service, your Shopping Cart from some Cache.
These would be composed into a ViewModel which represents the View that you're displaying. And rendered.
ViewModels should not be shared between views because if you change a ViewModel you change the meaning of the views that share that View Model.
I'm in the process of upgrading from Cake 1.3 to the latest Cake 2.
I used to access the user model from the App Component as:
$this->Auth->userModel->
However, that field is not there anymore.
Is there an alternative?
I also tried
App::uses('User', 'Model');
$user = new User();
but I get the error
Class 'AppModel' not found
Any alternative way of accessing the user model?
What exactly do you want to do by accessing the User (or any) model?
If in a Controller you can add the Model with the controller's $uses variable:
public $uses = array('User');
You can also access any Model by "jumping over the Model associations". For example if you're in the UsersController and you wish to use the Costume model where User hasMany Costume and Costume is not in the Controller's $uses property, you can do:
$this->User->Costume->find();
The existence of a Model Association is mandatory otherwise it will not work. This is possible since the User model has an internal association to any of it's associated Models.
Since Components wrap Controllers you should be able to do this also there or there should be a reference to the Controller from where you could call: User->Costume->find(); But in the Component you're better of passing data to it from the Controller than trying to call a model from it.
I'm new with structs, so pardon if I'm not using the correct terms or being too vague.
I have multiple forms on a page. I have each form's action property tied to separate actions in struts-config. Each form's submission works correctly. But I'd also like to prepopulate all of the forms when the page loads.
I tried creating multiple s in struts config, all with the same path but each with different type properties. But only the final seems to be executed. Is there a way I can make all of these actions execute, or is there some way to make an action trigger other actions, or something like that? I would just make one setup controller, but you can only tie a single form to an action, so I don't think that would work.
I think I'm using Struts 1.
Not sure if your form data is related, but even if its not, but one strategy that can work is implementing a base data, eg:
public class DataAction extends ActionSupport
{
private Data1 data1 = new Data1() // or whatever strategy of populating data
public Data1 getData() { return data1 }
public void execute()
{
// any common execution code
}
}
and then extend the action for each form
public class Form1Action extends DataAction
{
public void execute()
{
super.execute();
// unique execution code
}
}
then from your jsp page, just call the data object(s) for each form, as they will be inherited form the base class. For situations where you don't want to load a data object on each request of the Action, you can simply use some logic in the base Data class and have the extending class use that logic to decide whether or not to prepopulate that data object.
Another strategy, is to have an individual Action process all your forms. Again this only makes sense depending on your data and your design. If they aren't logically related, don't merge them into a single action.