I am a beginner in ASP.NET MVC.
My page has one partial view called _Navigation that I am reusing.
If the user is in the "Home" the <a> of the navigation needs to point to the "#" char, if the user is in the "Services" page, the href of the navigation needs to point to other url, let's say "www.mysite.com". It will occur with other links in this menu too.
I tried to do the following
#if (ViewContext.RouteData.Values.ContainsValue("Services"))
{
#model MySite.Models.ServicesNavigation
}
else
{
#model MySite.Models.HomeNavigation
}
But it says I can have only one model.
How to solve it?
You can try using Interface.
public interface INavigation
{
//Your props here
}
public class ServicesNavigation : INavigation
{
}
public class HomeNavigation: INavigation
{
}
Then your view can be of type INavigation.
#model INavigation
And in your controller based on your conditions you can pass the impementation of INavigation you want.
.......
INavigation model;
if(conditionOneIsMet)
{
model = new ServicesNavigation();
}
else
{
model = new HomeNavigation();
}
return View(model);
Your view is in fact a class derived from the WebViewPage<TModel> class. The #model statement defines type of the model (TModel) Because it is the compile time statement, you can't change it in run time.
If you need two different models, you should have two different views.
Related
I am looking to rewrite an existing ASP.NET MVC application to .Net 5 Razor pages and one of the issues I am struggling to resolve is how to create a partial razor page that creates it's own model rather than having the model passed in from the parent page.
In MVC, I would simply add an action to the controller and specify this within the view;
#Html.Action("_SideNavigation", "Shared")
Ultimately, this would call the _SideNavigation action on the share controller, which would then create the model and pass it back to the view.
I am trying to achieve something similar in Razor Pages, I do not want to define the model of the partial within the model of the parent view, such as this;
<partial name="_Partial1" model="Model.PartialModel" />
I have tried creating an empty model within the parent page and assumed this would then call the OnGet() method within the partial itself;
<partial name="_Test" model="new _TestModel()" />
public class _TestModel : PageModel
{
public TopNavigation TopNavigation { get; set; } = new TopNavigation();
public void OnGet()
{
TopNavigation = new TopNavigation { NotificationCount = 1 };
}
}
and then referencing the model in the page;
#Model.TopNavigation.NotificationCount
But it is always null.
Is this behaviour not available in razor pages?
So I have a View that contains 2 models. Each model has its own form and submit button.
Currently, I have both submit buttons are processed by the same controller method and use reflection to figure out which model type was passed. But it seems like there would be a better way... any ideas?
I have something like this:
Models:
public class Model1
{
// Elements
}
public class Model2
{
// Elements
}
Controller:
public ViewResult ConMeth(Object model)
{
Type t = model.GetType();
if(t == typeof(Model1)
{
// Do work for Model1
}
else if(t == typeof(Model2)
{
// Do work for Model2
}
else
{
// Do something else...
}
}
If you show your view info, I suspect you've got two seperate things happening in the view. Just put each thing in it's own form and use the
#using (Html.BeginForm(...)){}
and specify the actions by name and the controller (if necessary) in the BeginForm params... That should get rid of the ambiguous reference error
Here is an example w/ the older (not razor) tags
You can use a Tuple<> in your view to have two view models, and then in the #Html.BeginForm() helper method for each form, you can specify POSTs to two different controllers to process your form data.
#model Tuple<ProjectName.Models.Model1, ProjectName.Models.Model2>
I`m writing an ASP.Net MVC application with Razor.
Assume that I have HomeController and some views for it.
1. View1
2. View2
3. View3
All this views use common _MyLayout file, which should look like this:
When the links are clicked, the views are rendered by RenderBody() method.
Each view is strongly typed: it requires its own Model.
Everything was fine untill I decided to add special Model to _MyLayout view.
But now I get error
The model item passed into the dictionary is of type 'TestUp.Models.UserModels.PendingTestsModel', but this dictionary requires a model item of type 'TestUp.Models.UserModels.UserNavigationModel'.
Here is controllers code
public ActionResult View1()
{
ModelForView1 model = new ModelForView1();
return View(model);
}
public ActionResult View2()
{
ModelForView2 model = new ModelForView2();
return View(model);
}
public ActionResult View3()
{
ModelForView3 model = new ModelForView3();
return View(model);
}
Shortly speaking if layout view doesn`t require model, specific method for View is invoked,
model is created, passed to view and everything is ok. But now layout requires model as well so it crashes.
The question is: how do I elegantly resolve this problem?
Desired workflow is:
View1 is requested
Method in controller for this view is called, model instance created, passed to view
Some method for layout is called, model for layout created, passed to layout.
Is it possible to make things work somehow like this?
Thanks.
Create a base model type and have your specific view models extend it. This base model can have a property of type UserNavigationModel. The layout can accept the base model and use the new property as the model for the navigation menu.
public abstract class ModelBase
{
public UserNavigationModel NavigationModel { get; set; }
}
public class ModelForView1 : ModelBase { ... }
public class ModelForView2 : ModelBase { ... }
public class ModelForView3 : ModelBase { ... }
View1:
#model ModelForView1
Layout:
#model ModelBase
#* use Model.NavigationModel for nav bar *#
I have web application based on ASP.Net MVC3. I have a need for a "Create" view which will not know the model type until the user picks a sub-type to create from a drop-down. To attempt to solve this problem, I have created an editor template under Shared/EditorTemplates for each derived model type. This allows me to create a single "Create.cs" which is strongly-typed to a view model. The view model only has two members, an enum and a complex type. The idea is that the view will initially show only a drop-down (editor for the enum member) then when the user initially submits the specified "model type" (drop-down selected value), the POST action can check the "model type" specified and instantiate the correct derived model type for the view model's single complex member who's type is the base type for all possible "model types".
The abstract + derived type model objects...
public abstract class MyModelBase
{
public MyModelType_e ModelType {get; set; }
[Required]
public string Name { get; set; }
}
public class DerivedType1 : MyModelBase
{ ... }
public class DerivedType2 : MyModelBase
{ ... }
public class DerivedType3 : MyModelBase
{ ... }
I have a complex view model as follows...
public enum MyModelType_e
{
DerivedType1 = 0,
DerivedType2 = 1,
DerivedType3 = 2
}
public class MyModelCreate
{
public MyModelType_e ModelTypeForSelectList { get; set; }
public MyModelBase ModelBase { get; set; }
}
My GET controller action instantiates the above view model for the view i.e., only a drop-down list displayed with items based on the MyModelType_e enum + the value of the model's "ModelBase" property is initially null. So the GET action method looks like this...
[HttpGet]
public ActionResult Create()
{
return View(new MyModelCreate());
}
Note the comment in caps bellow regarding the crux of my issue which is that TryUpdateModel fails (see below) even though it sets the properties of the ModelBase (derived-type) member to the corresponding form values as expected...
[HttpPost]
public ActionResult Create(MyModelCreate model)
{
if (model.ModelBase == null ||
(int)model.ModelTypeForSelectList != model.ModelBase.ModelType)
{
switch (model.ModelType)
{
case MyModelType_e.DerivedType1:
model.ModelBase = new DerivedType1();
break;
case MyModelType_e.DerivedType2:
model.ModelBase = new DerivedType2();
break;
case MyModelType_e.DerivedType3:
model.ModelBase = new DerivedType3();
break;
}
return View(model);
}
if (!TryUpdateModel(model.ModelBase))
return View(model); // <<< THIS HAPPENS EVEN THOUGH ModelBase APPEARS TO BE UPDATED PROPERLY...
// For instance, I can see right here with intellisense that model.ModelBase.Name
// is NOT null or empty but rather is truly updated with the correct form value(s)...
// TODO: Insert the record, etc... (currently we never get here...)
}
So the above section is where the problem stems from but here is my view to help understand...
#model MyNamespace.MyModelCreate
<h2>Create</h2>
...
#using (Html.BeginForm())
{
#Html.ValidationSummary(false)
<fieldset>
<legend>Input</legend>
<div class="editor-label">
#Html.Label("Select Model Type")
</div>
<div>
#Html.EnumDropDownListFor(model => model.ModelType)
#Html.ValidationMessageFor(model => model.ModelType)
</div>
#*
Conditionally show the correct editor template...
There is one existing under ../Shared/EditorTemplates for each
derived type (DerivedType1, DerivedType2, DerivedType3, etc...)
This much is working in the sense that the correct editor fields
are displayed based on what the user selects in the above drop-down.
*#
#if (Model.InputModel != null)
{
#Html.EditorFor(model => model.ModelBase);
}
<p>
<input type="submit" value="Continue" />
</p>
</fieldset>
}
So once with the initial POST (model type is selected), my POST action method falls through to the TryUpdateModel line by design but for some reason the validation fails. The part I really don't understand is that the validation summary reports "Name is required" even though I can clearly watch TryUpdateModel set the Name propery properly on the ModelBase member of the view model.
I'd greatly appreciate any help or guidance here... I am fairly new to ASP.NET MVC and I am open to doing things differently since I understand there are probably other ways I could design my requests/actions + views to accomplish this "multi-step" problem but I really am just going for the simplest possible thing which I why I like the EditorTemplate approach for handling the derived model types, etc.
Thanks in advance...
Check ModelState. There should be errors set after TryUpdateModel runs.
I had to remove a Required attribute from some properties.
My solution basically involved growing two additional controller actions (GET and POST). I created separate views under ../Shared/EditorTemplates which are strongly typed (one for each derived model type). This way the initial "Create" POST action only receives the selected type (model is just an enum value specifying the desired type) and then redirects to another controller action like "CreateByType" which gets a new instance of the requested concrete type and returns the correct edit view. The additional "CreateByType" controller actions (GET and POST) only needs to deal with the abstract base because it requests the instance from the service layer (passing the enum value). Then all I had to do was create a view under EditorTemplates for each concrete type.
At the end of my view, I call this:
<%= Html.Action("ProductLayoutAndLimits", this.Model) /* Render product-specific options*/ %>
That action is virtual in my controller:
[ChildActionOnly]
public virtual ActionResult ProductLayoutAndLimits(DeliveryOptionsViewModel optionsViewModel)
{
return new EmptyResult();
}
The intent was that I would override this method in a product specific controller. So naturally, I did this:
public override System.Web.Mvc.ActionResult ProductLayoutAndLimits(DeliveryOptionsViewModel optionsViewModel)
{
But the breakpoint isn't hitting, so my override is not getting picked up. Is there a different overload I should be using? Or do I need to pass in a different object? Or is there an annotation that I need on my product specific action in order for it to be detected?
Any help is appreciated. Thanks!
Edit
While all suggestions are appreciated, I am most interested in solutions that actually answer my question, rather than suggesting a different technique.
Templates have been suggested, but please note that I need controller code to be executed before any new additional view code is rendered. The base controller is in a solution that serves as a platform to other products. They cannot do anything product specific. After they render their view, the intent is that my override of the child action will be called. My controller code will check a number of things in order to determine how to set properties on my model before it renders the view.
Edit
I found the problem. I feel silly, as usual. Html.Action was being called from Platform's view code. It turned out that we have been using a product specific view for this since July. I didn't notice because we don't typically use product specific views. Whoops!
Why are you using child actions and overriding controllers to handle this? Why not display templates:
<%= Html.DisplayForModel("ProductLayoutAndLimits") %>
where ProductLayoutAndLimits would be the name of the corresponding display template.
Example:
Model:
public class BaseViewModel
{
public string BaseProp { get; set; }
}
public class DerviedViewModel : BaseViewModel
{
public string DerivedProp { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new DerviedViewModel
{
BaseProp = "base prop",
DerivedProp = "derived prop"
});
}
}
View (~/Views/Home/Index.cshtml):
#model AppName.Models.BaseViewModel
#Html.DisplayForModel()
Display Template (~/Views/Home/DisplayTemplates/DerviedViewModel.cshtml):
#model AppName.Models.DerviedViewModel
#Html.DisplayFor(x => x.BaseProp)
#Html.DisplayFor(x => x.DerivedProp)
Of course you could also have a display template for the base class (~/Views/Home/DisplayTemplates/BaseViewModel.cshtml):
#model AppName.Models.BaseViewModel
#Html.DisplayFor(x => x.BaseProp)
and this template would have been rendered if your controller action had returned the base class as model.