Multiple Views for one ViewModel on a CreateCar Wizard - c#

I have a wizard with 5 steps, where in each step I get information from the user for his car sale. First it's the operation type (Sale, Rent), second is the Category (Car, Truck, Boat), third is the details of the item (Make, Model, Version), four is the extras and five is the member address.
Each step list of options is build using #Ajax.ActionLink and send the result to a specific DIV in the following master page (Operations -> Category -> Ad -> Extras -> Member -> Create):
#model Heelp.ViewModels.CreateAdViewModel
#using (Html.BeginForm(MVC.Ad.Create(), FormMethod.Post, new { id = "createAdForm" }))
{
#Html.AntiForgeryToken()
<div id="operation">
#{ Html.RenderAction(MVC.Ad.CreateAdOperation()); }
</div>
<div id="category"></div>
<div id="ad"></div>
<div id="extras"></div>
<div id="member"></div>
<input type="submit" value="Create" />
}
This master page has the "master" ViewModel "CreateAdViewModel" that will receive all the information from all the steps, creating all the "inputs" to be sent to the CreateAd Action.
What I have now is a ViewModel for each step:
public class CreateAdOperationViewModel
{
// Operation Fields
}
public class CreateAdCategoryViewModel
{
// Category Fields
}
public class CreateAdViewModel
{
// Common Ad Fields
}
public class CreateAdCarViewModel : CreateAdViewModel
{
// Car View Model Fields
}
The main problem is to know if this solution makes sense because has far has I know, each View should have his own ViewModel, but I need to submit a master ViewModel based on diferent Views, each one with its own ViewModel.
Thank you for your time.

You can share ViewModels across Views, on the basis that these Views need access to the same data. If the shared data is only partial, and not the entire view, you can have separate ViewModels (one ViewModel per View) with a shared "context" property that contains the shared data.
Here is an example article that shows how to do that: http://blog.bitdiff.com/2012/05/sharing-common-view-model-data-in.html
Good luck!

Related

Several Shared layouts for several sidebars

So I'm new to MVC 4 and I'm currently contemplating on what should I do.
CURRENT CODE
I made several SHARED layouts for each type of user. So I now have, _AdminLayout.cshtml for the Admin's sidebar, _FacultyLayout.cshtml for Faculty, and _StudentLayout.cshtml
PROBLEM
I have several types of users: Admin, Student, and Faculty. The content of the sidebar changes depending on which type of user is logged in.
Are there any other solutions for this?
I was planning to put all the sidebars in one SHARED LAYOUT but MVC 4 doesn't have Controllers for Shared Layouts unlike in Web Forms Master Pages, there is a Codebehind file. I'm just trying to shorten my code and lessen the redundancy between the 3 SHARED LAYOUTS that I currently have.
You can have one single layout and use #Html.Action() to render the sidebar as a partial view depending on the role of the user. This could go in a BaseController from which all other controlles inherit, or in a separate controller (say) LayoutController
[ChildActionOnly]
public ActionResult SideBar()
{
var role = ... // your code for getting the users role
if (role == "Admin")
{
return PartialView("_AdminSideBar");
}
else if (role == "Faculty")
{
return PartialView("_FacultySideBar");
}
else if ..........
}
and then create a associated partial views _AdminSideBar.cshtml, _FacultySideBar.cshtml etc.
Then in the Layout Page
......
<div id="sidebar">
#{ Html.RenderAction("", ""); }
</div>

How to work with a partial view within a view for same model?

I have a simple form with below functionalities.
First Part: It has a table which will display value from a database table, e.g. tblProfile and it contains three columns like profileid, profilename, description and in each row an edit button to edit values.
Second Part: In the same page, I have three text boxes to enter the value to the same database table, e.g. tblProfile. After clicking on the submit button it will insert the value into the table and will immediately display it on the table mentioned above and based on the last profileid it will show the top+1 id in the profileid textbox. If any edit button clicked on the table, these three text boxes will be populated with the values. After modification they have to be updated.
I have created a view which uses IEnumerable<MyModel> as model so that all the profile values are displayed in the table (strongly typed view). Now for the form part I have created a partial view which is also a strongly typed view but instead of IEnumerable<MyModel> it uses MyModel as its model type.
I amd confused about where I should put the Save, Update, Cancel etc. buttons: in the main view or in the partial. How to complete the functionality? I am using VS2010 SP1 and MVC4 and EF in the Main solution, which has one MVC project, an two folders: BusinessLayer and DataAccesLayer. The BusinessLayer folder contains separate class libraries for BO and BA, and the DataAccesLayer folder contains a class library file, and two other folders one for EF .edmx file and other one for DA class.
How can I implement this?
Simple approach: create one partial view for adding a new record, and another partial view for updating an existing record. The reason for this approach is to separate concerns of how your post-action should handle your request. Unless you add in a bool flag to tell the action whether the post command is to insert or update, it will not know what to do with the data. So, I suggest creating separate partials
// Insert partial "InsertPartialView"
#YourApp.Models.MyModel
#using (Html.BeginForm("InsertNewRecord", "Home"))
{
// your field controls here
<input type="submit" value="Add" />
}
// Update partial "UpdatePartialView"
#YourApp.Models.MyModel
#using (Html.BeginForm("UpdateRecord", "Home"))
{
// your field controls here
<input type="submit" value="Update" />
}
In your controller, use the "InsertNewRecord" action for adding new records, and "UpdateRecord" for updating an existing record.
Now initially, you can have your "insert" partial displayed in your View, so adding records requires no effort on the serve up this partial view.
<div id="partialDiv">
#Html.Partial("InsertPartialView")
</div>
Now when you want to update a record, you'll need to make a call to the server to replace your "insert" partial with the "update" partial. You can do this by creating an "edit" ajax-action link that accommodates each record in your table:
#Ajax.ActionLink("Edit", "GetUpdatePartial", new { id = item.profileid }, new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "partialDiv" })
I'll explain this if you've never used the ajax link before. The first three params are the text of the link, the action's name, and the id of the item -- no different than a regular Html-actionlink. The 4th param lets us define our ajax options. The "GET" method is self-explanatory -- we are not processing any data on this call, only retrieving from the db. The InsertionMode option defines how our return data should be handled in the View. In this case, we want it to replace the current "insert" partial view, so we choose "replace". Lastly, we define the element where we want our partial view to be inserted, our named "partialDiv".
At this point, the only thing missing is the action for our ajax-call.
[HttpGet]
public PartialViewResult GetUpdatePartial(int id)
{
var record = db.tblProfile.Single(r => r.profileid == id);
return PartialView("UpdatePartialView", record);
}
This action uses the profileid from the "edit" ajax-link to retrieve its record from the db, and then we're returning the partial view with the record as its model. The result should be that this partial view will now be inserted into our "partialDiv", replacing the initial "insert" partial view.
Here are the other actions if you want them:
[HttpPost]
public ActionResult InsertNewRecord(MyModel model)
{
if (model.IsValid)
{
db.tblProfile.Add(model);
db.SaveChanges();
}
return RedirectToAction("Index");
}
public ActionResult UpdateRecord(MyModel model)
{
if (model.IsValid)
{
db.Entry(model).State = EntityState.modified;
db.SaveChanges();
}
return RedirectToAction("Index");
}
Hope that helps

Adding my own name tag to a form component in a partial view - MVC 5.0

I need to be able to show a dropdown list for some items in various different locations... I can safely assume that considering it is a dropdown, it will always be used in context with a form and thus a name tag will always be needed. However I wanted to know what's the best practice for this type of problem.
I have the following in a partial view and you can see I have explicitly set the name="Name" which at this moment in time is correctly going to map to the items Class property:
#model IEnumerable<TEST.Domain.Entities.James>
#{
Layout = "";
}
<select name="Name" id="JamesDropdownList">
#foreach (var item in Model)
{
<option value="#item.JamesID">#item.Name</option>
}
</select>
I may then use this partial view in such a context:
#using (Html.BeginForm("LoadInformation", "James"))
{
<div class="col-lg-9 remove-padding-left">
#{ Html.RenderAction("DropdownList", "James"); } // This is the partial view being used in a form
</div>
<div class="col-lg-3 remove-padding-right">
#Html.ContinueButton("Continue") // This is a custom button html helper I have created
</div>
}
So the question is, is this absolutely okay, to declare specifically that the name for this select field is "Name" or is there some special clever razorey way of doing this?
EDIT
In fact, don't I have to do #Html.Hidden("JamesID", item.JamesID) or something in the select? So that when I submit that form, it pushed the JamesID to the controller which looks like this:
[HttpPost]
public ViewResult LoadInformation(int jamesID)
{
// Do something with jamesID
return View("LoadInformation");
}
You can see i'm a bit confused...
Maybe a better question is, how do I have a re-usable dropdown list that I can use in a form that requires the JamesID from that dropdown list as one of the receiving controller's parameters?
One razory way would be to do this:
#Html.DropDownList("JamesDropdownList", new SelectList(Model,"JamesID","Name"))
This will assign JamesDropdownList to both the name and id attribute of the dropdown.

How to handle a strongly typed modal in a strongly typed view with different types

I'm using modals for logging in and registering a user. Each modal is strongly typed to make use of the ASP.NET built-in account classes (RegisterModel and LoginModel). However, since the two buttons to call these modals are located on the navbar and the navbar is placed on every page I receive errors because most views are strongly typed and thus cannot handle a partialview (the modal) to be using a different strongly typed model.
How are strongly typed modals handled in a strongly typed environment?
_Layout:
<body>
<div class="navbar">
#Html.Partial("_LoginPartial") // contains buttons to call login/register modals
</div>
<div>
#Html.Partial("_LoginModal")
#Html.Partial("_RegisterModal")
</div>
<div class="container">
#Html.RenderBody()
</div>
</body>
/News/Index:
#model List<NewsBulletinViewModel>
LoginModal:
#model NoName.Models.LoginModel
On a related note:
Since I have forms inside my modals, how can I refer back to those modals when validation errors occur? Ideally the modal should popup again (or never be closed) with the validation errors shown.
There is an overload in #Html.Partial that takes an object, used for the partial page's Model. If you include the Partial in your layout, in every page, you need a logic to keep that data. For example, if you take LoginModel and RegisterModel, you could do this:
#Html.Partial("_LoginPartial", ViewBag.LoginModel ?? new LoginModel())
#Html.Partial("_RegisterPartial", ViewBag.RegisterModel ?? new RegisterModel())
And leave to the executing controller the role to put a LoginModel (or RegisterModel). If there isn't any in the ViewBag, it will fallback to creating an empty one.
Edit: Based on the additional information, I'd do this for the LoginPartial (RegisterPartial would be the same logic):
public class AccountController : Controller
{
public ActionResult LoginPartial()
{
return PartialView("_LoginPartial", (Session["Login"] as LoginModel) ?? new LoginModel());
}
[HttpPost]
public HttpStatusCodeResult SaveLoginModel(LoginModel model)
{
Session["Login"] = model;
return new HttpStatusCodeResult(200);
}
}
And then, in _LoginPartial, do as you would, but add a javascript code to send an ajax post request to the SaveLoginModel action of your controller when the value change to keep your model in sync (there is plenty of information around on how to do that).
Now, instead of doing:
#Html.Partial("_LoginPartial", ViewBag.LoginModel ?? new LoginModel())
#Html.Partial("_RegisterPartial", ViewBag.RegisterModel ?? new RegisterModel())
You would do:
#Html.Action("LoginPartial", "AccountController");
#Html.Action("RegisterPartial", "AccountController");

Show different sub-sets of a view model's properties in an edit view

In the context of C# 4, ASP.NET MVC 2, and NHibernate; I've got the following scenario:
Let's assume an entity Product that have an association to ProductType.
In a product edit view; how do I implement that only a sub-set of the product's properties are shown based on the ProductType association in an elegant and DRY way? I.e., different properties shall be shown for different values of a property of the ProductType.
Use a product view model builder, and from different view models automagically generate the view with my own Html.EditorForModel() (including drop-downs and other stuff not out-of-the-box)?
Attribute the properties of one view model and use the Html.EditorForModel() way aforementioned?
Use one model, but implement different web controls (view strategies) (can it be done DRY?)?
Something else entirely?
Ah I see - my apologies. That's not 'technically' supported - however, you could alter a custom attribute to use some funky reflection to achieve the same thing. That's definitely not a best practice though.
Another option would be to use Html.EditorFor(m => m.ProduceView()), where ProduceView is a method that returns a new ViewModel type based on the state of your properties - so if some Property is set, then ProduceView sends a SetPropertyViewModel, or a NotSetPropertyViewModel, both of which who implement some base class or interface. Each of those view models could be then annotated differently.
My first step is to create a view model. Even if this is very similar to your actual entity, the separation is important. so I would create a ProductEditViewModel class.
Next, determine the properties that will be changing based on the different product types. Create separate Partial View Models for each product type. This allows you control over what properties are displayed and how they are formatted.
In your main Product Edit view, use a switch statement to "swap in and out" the different partial views, as required. If you use AJAX, you can even do this dynamically.
In this example, we have a number of different reports that have different report types. The main part of the reports doesn't change, just a number of different parameters (depending on type).
For each report type we have separate partial views, which you can see are added in depending on the report type. This code snippet is inside a <% using (Html.BeginForm()) %> code block.
<% switch (Model.ReportType)
{
case (int)ReportType.summary:
Html.RenderPartial("Edit/SummaryControl", Model);
break;
case (int)ReportType.exception:
Html.RenderPartial("Edit/ExceptionControl", Model);
break;
case (int)ReportType.leakdetection:
Html.RenderPartial("Edit/LeakDetectionControl", Model);
break;
} %>
and the summary report partial view:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Footprint.Web.ViewModels.ReportsEditViewModel>" %>
<fieldset>
<legend>Summary Report Parameters</legend>
<div class="editor-label">
<%= Html.LabelFor(model => model.Frequency)%>
</div>
<div class="editor-field">
<%= Html.DropDownListFor( model => model.Frequency,Model.Frequencies) %>
<%= Html.ValidationMessageFor(model => model.Frequency)%>
</div>
<div class="editor-label">
</div>
<div class="editor-field">
<%= Html.CheckBoxFor(model => model.Combine) %><%= Html.LabelFor(model => model.Combine)%>
</div>
</fieldset>
HTH
You can use EditorForModel and have only a subset of the properties get displayed if you add the attribute:
[ScaffoldColumn(false)]
To the property you don't want to display. Of course, if you implement your own custom editor template, then you have full control over what HTML gets rendered.
create a custom DataAnnotationAttribute that takes in a ProductType as a parameter. Then apply to the corresponding properties that are to be viewed. You can the extend this functionality further by creating a EditorTemplate for Product that will handle further process, form element look and feel, or and some JQuery to some form elements.
I've reviewed all answers and thought the issue through more thoroughly. I went with an approach like
Use a product view model builder, and
from different view models
automagically generate the view with
my own Html.EditorForModel()
(including drop-downs and other stuff
not out-of-the-box)?
from my original question.
I've got a view model creator that takes an instance of the corresponding domain model entity, as well as different lists from the domain model to be used for drop-downs.
Instead of using the EditorFor/EditorForModel out-of-the-box, I've made my own custom template based on Brad Wilson's ideas described in this post.
DRY, simple, and explicit (although I've wished I had a dynamic language more than once in order to reduce code and get rid of some reflection…).

Categories