Dynamic header and side menu on MVC 3 - asp.net-mvc

I would like to know how I can render a custom header (based on customerid) and a side menu (again, based on the customerid) + the main content.
On a search page, I have a list of customers. When I select a customer, it will take me to another page passing the id of that customer (/controller/action/id). However, on this new page, I would like to list customer details on the header (name, email, phone, etc), on the side menu, I would like to list the pets that this customer owns (dog1, cat2, etc) and on the main content I want to show details of the first pet on the list (side menu). The list of pets on the side menu should be links. For instance, the dog1 should be a link to its own details shown on the main content. Cat2 should be a link to show details on the main content, etc
I just don't know how I am going to pass the id to the header, side menu and content page, all at once!
I have seen the mvcMusicStore where we have a list of the genres on the left menu, but that does not change! In my case, the side menu should change according to the customer selected on the previous page! Make sense?

You have multiple choices. At first, you can use partial views:
in layout.cshtml [or in your usecase, search page]:
#Html.Partial(your-partial-view-name)
view: headerPartial.cshtml as an example:
#if(User.Identity.IsAuthenticated) {
<span>Welcome</span> #User.Identity.Name
} else {
<span>Login or Register</span>
}
This is the base logic. For your situation, you need something like this. But, if you have to load some data from for example database, I would suggest you using partial actions:
// in layout or search or any page you want to render partial views:
// For example you have a controller named MyPartials:
// Put this line where you want to render header:
#Html.Action("HeaderInfo", "MyPartials")
and action:
public class MyPartialsController : Controller {
public ActionResult HeaderInfo() {
var model = retrive header needed info from db
return Partial(model);
}
}
Let me know more about your problem in details, to suggest you a good solution
UPDATE:
Suppose:
Controller: CustomerController
Action: SeletedCustomer
Solution:
1. Models:
public class HeaderInfoModel { } // contains header data: name, email, phone, etc
public class SideInfoModel { } // dog1, cat2, etc
public class MainContentModel { } // holding main content data
// bringing all together
public class CustomerModel {
public HeaderInfoModel HeaderInfo { get; set; }
public SideInfoModel SideInfo { get; set; }
public MainContentModel MainContent { get; set; }
}
2. Controller
public class CustomerController : Controller {
public ActionResult SeletedCustomer(int id) {
// use id and retrieve data you want, from db or anywhere else you want
var model = new CustomerModel {
HeaderInfo = SomeMethodToFetchHeaderData(id),
SideInfo = SomeMethodToFetchSideData(id),
MainContent = SomeMethodToFetchMainContent(id)
};
return View(model);
}
}
3. Main View: Customer/SeletedCustomer.cshtml
#model CustomerModel
<div id="header">
<!-- put this where you want to render header -->
#Html.DisplayFor(model => model.HeaderInfo)
</div>
<div id="side">
<!-- put this where you want to render side -->
#Html.DisplayFor(model => model.SideInfo)
</div>
<div id="main">
<!-- put this where you want to render main content -->
#Html.DisplayFor(model => model.MainContent)
</div>
4. In your situation, using Display Templates is the easiest way. Create a folder named DisplayTemplates in one of these places: ~/Views/Customer/ or ~/Views/Shared. It depends on where and how you want to use provided models. Again, in your case, I suggest create this: ~/Views/Customer/DisplayTemplates/. And then add some views in that folder, which are described below:
4.1. Create HeaderInfoModel.cshtml in ~/Views/Customer/DisplayTemplates/:
#model HeaderInfoModel
<div>
implement your model presentation, I mean, show the data how you want
</div>
4.2. Create SideInfoModel.cshtml in ~/Views/Customer/DisplayTemplates/:
#model SideInfoModel
<div>
implement your model presentation, I mean, show the data how you want
</div>
4.3. Create MainContentModel.cshtml in ~/Views/Customer/DisplayTemplates/:
#model MainContentModel
<div>
implement your model presentation, I mean, show the data how you want
</div>
That is it. Done! With your situation, the best way I can suggest is that. However, you have multiple choices; It's completely depended on your project.

You probably need to use some kind of partial view with condition like it shown below inside razor
if(this is correct page && CustomerID is available) { show the view }
You can pass parameters in ViewBag and use them inside your view.
If you are new to ASP.NET MVC, here are some cool tutorials http://www.asp.net/mvc.
And here is tutorial of building partial views http://mvc4beginner.com/Tutorial/MVC-Partial-Views.html for beginners.
Hope this helps

Related

MVC5 - Dynamically choose which properties are displayed in an form

Let's say I have two users who are accessing a form. One is an admin user and one is a normal user. And only the admin can view and edit AdminOnlyField Then let's say I have the following class:
public class Car
{
public string Make {get;set;}
public string Model {get;set;}
public string AdminOnlyField {get;set;}
}
I'm not able to figure out how to conditionally make the AdminOnlyField visible and editable using #html.BeginForm(), I know you can use properties from ViewBag but in this case all the information I need is coming back from a database so I don't think I can utilize ViewBag.
Is this something MVC can accomplish or do I need to explore Angular?
The easiest way is to insert if(User.IsInRole(stringRole))
Yet if you want to hide this mechanism you can make an EditorTemplate.
The file has to be in location Views/Shared/EditorTemplates/EditFormTemplate.cshtml
For AdminOnlyField you can hide this functionality by template.
#if(User.IsInRole("Admin")
{
<div>
<label>AdminOnlyField: </label>
#Html.EditorFor(model => Model.AdminOnlyField)
</div>
}
Usage:
#Html.EditorForModel("YourCustomTemplateName")
If you need more information:
There is more information

mvc4 #Html.DropDownListFor use two models or entities?

I am new to MVC.
I have two tables: first one stores the vendor’s information, the other one stores the order details. I want to use the vendor table’s values for the dropdown list.
#model Ore.Domain.Entities.OrderDetail
#{
ViewBag.Title = "";
}
<h2> </h2>
#using (Html.BeginForm())
{
<div>vendor: #Html.DropDownListFor(x=>x.Vendor,’**how to use the value from another entities or models?** ’)</div>
<div>Contract Number: #Html.EditorFor(x=>x.ContractNo)</div>
Thanks
This is a good example of when you need to break away from your DO and make a hybrid view that either contains composite DO's like a Parent item and a List of Child items or some combination thereof. The more it resembles your DO the more likely that you can leverage mapping techniques such as auto mapper.
Just introduce a model specificly for your view. Let's call it OrderDetailViewModel:
public class OrderDetailViewModel
{
public Ore.Domain.Entities.OrderDetail Order {get;set;}
public IEnumerable<SelectListItem> Vendors {get; set;}
}
Then on your view:
#model OrderDetailViewModel
<h2> </h2>
#using (Html.BeginForm())
{
<div>vendor: #Html.DropDownList("Vendor", Model.Vendors)</div>
<div>Contract Number: #Html.EditorFor("ContractNo", Model.ContractNo)</div>
Or just pass Vendors throught ViewBag like this:
in controller:
ViewBag.Vendors = // populate items as new List<SelectListItem>()
on view:
<div>vendor: #Html.DropDownListFor(n=>n.Vendor, (List<SelectListItem>)ViewBag.Vendors)</div>

Submit a List from View to Controller

can anyone please show me the code for posting a list on button click from view to controller in mvc3-asp. i have a list in which one of the list element is check box.at first i have to select some of the values from list then submit it to save the selected values only.
Your question is two broad to answer. So i will give you a generic example which you need to customize according to your scenario.
Assuming your view is to assign some tasks to users. Each user can be assigned to one ore more tasks.
So i will create a viewmodel like this.
public class UserViewModel
{
public int UserId { set;get;}
public string Name { set;get;}
public List<Task> Tasks { set;get;}
public UserViewModel()
{
Tasks=new List<UserTask>();
}
}
public class UserTask
{
public int ID { set;get;}
public string Name { set;get;}
public bool IsSelected { set;get;}
}
Now in your GET action, you create an object of UserViewModel and set the Tasks property to a list of available taks (from your database may be)
public ActionResult Add()
{
var vm=new UserViewModel();
vm.Tasks=GetAvailableTasks();
return View(vm);
}
Assuming GetAvailableTasks is a method which returns a list of UserTask objects.
Now create an editor templates. Go to your ~/Views/YourControllerName folder and create a folder called EditorTemplates. Add a new view to the newly created folder and give name as Task.cshtml. Add the below code to that
#model YourNameSpace.UserTask
<p>
#Html.CheckBoxFor(x => x.IsSelected) Model.Name
#Html.HiddenFor(x => x.ID)
</p>
Now go back to your main view and use EditorFor helper method to bring the EditorTemplate.
#model YourNamespace.UserViewModel
<h2>Quiz 24</h2>
#using (Html.BeginForm())
{
#Html.EditorFor(x=>x.Tasks)
<input type="submit" />
}
Now when the form gets submitted, you can get the selected checkbox values like this
[HttpPost]
public ActionResult Save(UserViewModel model)
{
List<int> taskIds=new List<int>();
foreach (var task in model.Tasks)
{
if (task.IsSelected)
{
//you can get the selected task id's here.
taskIds.Add(task.ID);
}
}
//to do : save and redirect (PRG pattern)
}
Here is a blog post explaining how to use EditorTemplates to handle collections on form submit. the post is showing an example to handle radio buttons.
You can actually skip all this rebuilding in the controller. Razor can rebuild the list of any object using a single form. You just need to fallback to the old for and assign a unique name, as well as include the primary key for that collection.
On postback razor will rebuild your ViewModel with the modified list without any extra work.
In the markup it looks something like this
And in the controller after posting back the model is rebuilt with the new data the user has entered, and also lazyBound to ForeignKey models!
Using checkboxes or radio inputs it's slightly different. The problem is that when a checkbox is not ticked, browsers do NOT submit it back, and the model will NOT rebuild. This is not a shortfall of VS but an intended implementation of HTML1 that still lives in HTML5!
If this happens the Index Out of Bounds will occur. So you have include a hidden field for checkboxes with a value of false before the Input Box (if a user checks it it will overwrite the false to true)
But if you are using a StronglyTyped model, like in my case, any check boxes evaluated to null are automatically set to false, known as the default value for a boolean as defined in the C# specification.

ASP.NET MVC3 view authorization design

Imagine you have a secured site and a View that can be generated in multiple ways, depending on the user role. Say, an Admin sees all, Manager sees some columns and some action buttons, User sees other columns and other action buttons.
How would you implement this? As far as I see it, there are three main options:
Controller with [Authorize] attribute and the action returning 1 View per user role, being those views tailored to the role;
Controller with [Authorize] attribute and the action returning 1 View for all roles, with logic to hide/show columns, fields, buttons;
Controller with [Authorize] attribute and the action returning 1 View, which based on the roles renders different Partial Views.
I prefer the third approach, but do you see a better way to implement this?
Thank you in advance
Depending on the complexity of your view, either the first or the third option would seem ok to me. As for the second option; Its generally recommended to avoid logic in views so I'd stay away from that one.
If you go for the third option you should consider using EditorTemplates and DisplayTemplates. This will allow you to make your (main) view agnostic to what partial view to render.
Make your viewmodel (or part of it) inherit from a single base class. Create display and/or editor templates for each view model type and in your view just say Html.DisplayFor( ... ) or Html.EditorFor( ... ). MVC will automatically pick the right template, without the need for logic in your view.
What I have been doing for menus and other navigational items is that I have a ViewModel class for it. Here is a simplified version.
ViewModel
public class Action
{
public string DisplayName { get; set; } // localized
public string Url { get; set;
}
public class MenuViewModel
{
public List<Action> Actions { get; set; }
public MenuViewModel()
{
this.Actions = new List<Action>();
}
}
I fill that depending on the user role. Admin gets more links etc.
That ViewModel is part of "main" view model
public class AlbumEditorViewModel
{
public MenuViewModel Menu { get; set; }
}
Then I'll pass that view model for the partial view that is responsible for the menu.
View (Razor)
#model AlbumEditorViewModel
.. razor stuff here ..
#Html.Partial("Menu", Model.Navigation)
.. razor stuff here ..
Partial View
#model MenuViewModel
<ul>
#foreach (var action in Model.Actions)
{
<li>
#GridHelper.GetAction(action)
</li>
}
</ul>
I hope that this will give you ideas

How to auto-populate specific fields when using #Html.EditorForModel() to build a form?

I have users fill out their profile information somewhere on my site using the AspNetSqlProfileProvider that came with the IDE.
I allow my users to edit their profile at any time by going to the edit profile page where the form to edit their profile auto-populates. I currently do this by using ViewBag to send each individual part of a user's profile from my controller to the view (address and city for example):
Controller get:
ViewBag.address = CustomProfile.GetUserProfile(User.Identity.Name).Address;
ViewBag.city = CustomProfile.GetUserProfile(User.Identity.Name).City;
View:
<div class="editor-field">
#Html.TextBox("Address", (string)ViewBag.address)
</div>
<div class="editor-field">
#Html.TextBox("City", (string)ViewBag.city)
</div>
Controller post:
public ActionResult ChangeProfile( FormCollection favorites )
{
CustomProfile profile = CustomProfile.GetUserProfile();
profile.Address = favorites["Address"];
profile.City = favorites["City"];
profile.Save();
return RedirectToAction("Profile");
}
The above works fine for editing profiles, it is very smooth from my user's perspective (although, from reading questions/answers online it appears I should be using a ViewModel, not exactly sure how to make the transition - have been failing).
When my user's go to 'checkout' from the website (they've been shopping), they are presented with a screen that allows them to input final information that allows them to make their purchase. Let's call this the, "Order Page." These details include name, address, credit card info, etc. Some of these pieces of information are the same as the information within the profile page of the website.
I want to be able to auto-populate some of the details in this Order Page (name, address) while leaving some of the fields blank (credit card - so the user has to fill these details in each time they visit the order page).
The way the Order page works is as the MVC Store Tutorial describes. When the Order Page is displayed it displays the form using:
#Html.EditorForModel()
Which is great and allows you to edit the details specified within the Order model and will allow for the data verification stuff (name is required, address is required, credit card has to be numbers, email is properly formatted, etc), but I cannot figure out how to populate specific fields within this Order Page with details from my user's profile.
I have attempted building a new ViewModel that uses just the information contained within my profile, but I do not have a clear grasp of what is needed because I am not getting the end-result I am desiring.
I have considered using just the Order Model as my Profile information, but that is not sufficient because I want to have different information available in both.
I have considered using the Profile information as the Order Model, but I want my users to have the flexibility to store profile information separately from whatever information they use to actually place orders.
I think what I need to know specifically to fix my problem is how do I auto-populate specific fields while using "#Html.EditorForModel()" ?
Any other help with my overall situation would be great, I am very open to any advice that would simplify my flow (it feels like I am making it harder on myself than I need to, and I am at a point where collaboration would feel like a breath of fresh air).
I was able to follow only the first half of your question. It's the one in which you have showed some code and the one with which I can help you. The second half was a complete mist for me.
So when you want to design a view think of what fields this view needs to display/edit. And design a view model for it:
public class ChangeProfileViewModel
{
[Required]
public string Address { get; set; }
[Required]
public string City { get; set; }
}
and then have your GET controller action that is supposed to render this view populate the view model:
public ActionResult ChangeProfile()
{
CustomProfile profile = CustomProfile.GetUserProfile(User.Identity.Name);
ChangeProfileViewModel model = new ChangeProfileViewModel
{
Address = profile.Address,
City = profile.City
};
return View(model);
}
then you design the corresponding view which will be strongly typed to this view model:
#model ChangeProfileViewModel
#using (Html.BeginForm())
{
#Html.EditorForModel()
<button type="submit">OK</button>
}
and finally you have a POST controller action that will handle the submission of this form:
[HttpPost]
public ActionResult ChangeProfile(ChangeProfileViewModel model)
{
if (!Model.IsValid)
{
// there were validation errors => redisplay the view
return View(model);
}
// validation succeeded => process the results
CustomProfile profile = CustomProfile.GetUserProfile();
profile.Address = model.Address;
profile.City = model.City;
profile.Save();
return RedirectToAction("Profile");
}
Now what we observe here is that in both our GET and POST actions we have repetitive mapping code between our domain model (CustomProfile) and our view model (ChangeProfileViewModel). To solve this issue I may recommend you using AutoMapper. It could simplify your GET action to:
public ActionResult ChangeProfile()
{
CustomProfile profile = CustomProfile.GetUserProfile(User.Identity.Name);
ChangeProfileViewModel model = Mapper.Map<CustomProfile, ChangeProfileViewModel>(profile);
return View(model);
}
or with a custom action filter to even:
[AutoMap(typeof(CustomProfile), typeof(ChangeProfileViewModel))]
public ActionResult ChangeProfile()
{
CustomProfile profile = CustomProfile.GetUserProfile(User.Identity.Name);
return View(profile);
}
and your POST action to:
[HttpPost]
public ActionResult ChangeProfile(ChangeProfileViewModel model)
{
if (!Model.IsValid)
{
// there were validation errors => redisplay the view
return View(model);
}
// validation succeeded => process the results
CustomProfile profile = CustomProfile.GetUserProfile();
Mapper.Map<ChangeProfileViewModel, CustomProfile>(model, profile);
profile.Save();
return RedirectToAction("Profile");
}
What's important to know about view models is that you should always use them for each view and design them in such a way so that they contain only the specific information that this view needs to handle. Leave to the mapping layer handle the conversion between your domain entities and your view models.

Resources