Okay, so I've been using .net MVC (and nowadays .net core) for years and have never had this issue... to my recollection.
I've got a parent view with its own ViewModel, and a partial with its own view model. Now, the razor code that calls the partial does so by string name, and it has no idea what viewmodel it should use because it's dynamic. Now, when we call a controller method that returns a view that requires a viewmodel and we don't provide an instance of that viewmodel, MVC wires us up a new instance by default. However, with a partial view inside of a parent view, even if we don't specify a viewmodel, or intentionally pass in null, we will still receive the error that the parent viewmodel type was passed in when the partial's viewmodel type was required. I simply want to, by default new up a viewmodel of whatever type the partial needs, without hard-coding it... just like a normal view would do when returned by a controller method...
Any ideas?
For example
public IActionResult DoSomething() => View();
With a view calld "DoSomething" that looks like this.
#model app.viewmodels.DoSomethingViewModel
<h1>#(Model.SomeStringProperty)</h1>
The controller view will use a newly constructed model for reference.
However, if I have another view called "DoSomethingElse"
#model app.viewmodels.DoSomethingElseViewModel
<h1>#(Model.SomeStringProperty)</h1>
and I change the first view to include a partial of that view
#model app.viewmodels.DoSomethingViewModel
<h1>#(Model.SomeStringProperty)</h1>
#await HTML.PartialAsync("DoSomethingElse")
By default the partial will get passed into it the "DoSomethingViewModel" from its calling parent.
How can I make the partial use its default constructor without hardcoding something like this.
#model app.viewmodels.DoSomethingViewModel
<h1>#(Model.SomeStringProperty)</h1>
#await HTML.PartialAsync("DoSomethingElse", new
DoSomethingElseViewModel())
I wanted to avoid using reflection in my particular circumstances, and it just seems that there should be an overload that allows you to uses a newly constructed model by default.
Related
I noticed almost all code I found just return View(); in [HttpGet] Create action method. I wonder why don't we need to return View(new Person()) for example?
Most of the default constructs you use in a razor view template (like Html.EditorFor, Html.LabelFor, etc) work when the model is null. They can access the meta data of the class type of the model for tasks like displaying the label or deciding on the required format of the value. And they just use an empty value (empty string, 0, false) for the value when the model instance is null.
So, the code you see (View called without model instance) will only run into trouble if there is code in the view template that manually attempts to access the model instance without checking whether it is null.
Still, in my opinion it is best practice to pass a new instance of the Model or ViewModel type when calling a view to create a new object. This way, default values set in the constructor of the Model or ViewModel or values set in the controller action before will not be lost and used in the view. Also, there will not be a problem when someone modifying the view template decides to access the model instance without null checks.
I'm not sure what code you're looking at, but if I'm returning a view that requires a viewmodel, I will pretty much always include the viewmodel object when I call the view.
public ActionResult ViewTime(int id, DateTime? from, DateTime? to)
{
var viewTimeModel = _repository.ViewTime_Read(User, from, to, id);
return View(viewTimeModel);
}
It's possible that the view has some dynamic controls, like grids, etc in it that are responsible for getting their own data, perhaps via javascript calls to the controller methods for json. In that case, the view is pretty much a "dumb shell" and the controls on it are doing the heavy lifting.
I have an example from a project I'm working on in which a particular model is passed on to the view from the controller, like this:
result = View(stu) //stu is an object of type Student
//
//
return result;
In that particular view, at the top I have #model Project.Models.Student and I have textboxes which bind the information entered to the model. However, in another controller I have another method that has the following code:
//
//
result = View();
//
and the view for this has #model.Project.Models.Login, but I can still bind things to the model from this view just like in the other view (eg, I have something like:
#Html.TextBoxFor(model => model.login_name, new { maxlength = "30" })
I didn't pass a model to the view like I did in the first method, and yet it seems to have the same functionality. If I am using #model at the top of the view, does that mean that it is not necessary to explicitly pass a model object as a parameter from the controller method?
If you declare #model at the top of your cshtml page, then your page expect to get model of the type you have declared. Of course you can choose not to use a model in your page, and in this situation your declare is useless.
In addition you can declare a model, but don't send any object to the view.
If you send an object, it must be the same type as you declare in #model.
I have an Editor Template called "Address.cshtml" that has a model defined as:
#model Acme.Models.Address
In a View I want call the Editor Template and pass a local variable of the same type, and define the name it will use for the variables, I've tried a number of things including:
#Html.Editor("address", "Address", new { Model = address })
How do I pass the model?
Note, I cannot use #Html.EditorFor() because the view uses a different model.
The only purpose of EditorFor is to work with your view's model. If you need to work with a completely different class instance that's not your view's model or accessible through you're view's model. Then just use Html.Partial. They're functionally the same. If you're worried about using a specific editor template, you can always pass the full path to the view to Html.Partial.
In a view (in its header or somewhere else, but before the row you are calling your Html.Editor) you can add the model in the ViewData with the key equals to your Html.Editor's expression and it will be used as a model in your called editor. For example:
#{
var address = new Acme.Models.Address();
ViewData["address] = address;
}
#Html.Editor("address", "Address")
I'm working on a MVC 5 project, very new to MVC. I noticed this line in the code:
#Html.DropDownListFor(model => model.ContractorId, Model.Contractors)
the directive on the top of the page is:
#model Project.Models.ContractViewModel
The ContractViewModel class does have a Contractors object in it.
public IEnumerable<SelectListItem> Contractors
I'm just a little confused on the capital M (Model.Contractors), does Model always refer to the object passed in the #model? Then what is the difference between using Model.Contractors and model.Contractors?
Model actually represents an instance of the class that is your model and model is an alias for lambda expression.
When you write #Model in your view you are using the object of ContractViewModel which is passed from Controller action, if it is not passed from View it can be null and accessing any property of Model can throw Null Reference Exception and writing Model.Contractors you basically mean ContractViewModel.Contractors
and when you write a html helper you need to write an alias for it, its not mandatory to write model you can write anything.
for example:
#Html.DropDownListFor(m=> m.ContractorId, Model.Contractors)
It is just an alias to access the model properties inside Html Helper.
When you write #model Project.Models.ContractViewModel at the top of View that is a different thing, in this case we are defining the model of view that it will take instance of Project.Models.ContractViewModel , actually our view is now strongly typed to instance of class Project.Models.ContractViewModel.
I have two views:
1) Register – primary view for creating user account
2) Category – partial view for adding category dynamically if it's not there in a register view combo field.
The Category view is shown as jQuery dialog when user click on Add Category while registering. This view shows fields required to make up a new category like name and description. For this reason, it has a separate model.
On get page everything works fine, however on post, if there's some validation error, the page needs to reload with user provider values for making correction, instead it throws following error:
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'Delight.Models.User', but this dictionary requires a model item of type 'Delight.Models.Category'.
I thought that using the following statement might be causing this problem (since it doesn't specify the model object to use for partial view):
#Html.Partial("CreateCat")
However, resorting to the following overload didn't solve the problem either:
#Html.Partial("CreateCat", null, null)
Above the second parameter (with null value) represents the model object.
However, unexpectedly following solved my problem:
#Html.Partial("CreateCat", new Category(), null)
Why is empty object working in this case but null isn't?
Is there any other better way to render strongly-typed partial view with different model type.
Unless you want to use the parent's model or no model, you should always pass in the appropriate model for a partial view. Passing in null is silly, if you don't want an instantiated model in your partial view then why would your partial view use a model at all? In this instance, I think new Category() is the correct choice. However, I've always seen sub models passed into partials
#Html.Partial("CreateCat", Model.Category)
something about needing to do what you're doing seems strange, but I'm not certain how you're using the partial view.