variable to show/hide link in View of ASP.NET MVC3 - asp.net-mvc

Fairly new to MVC3 and I am doing a simple project for testing purposes. So I have encountered a slight problem which I would like your help about.
At the moment I have this code :-
<p>
#if (UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}
My question is, how can I populate the boolean value UserCanCreate from my controller? I tried creating a property in the Controller, but the View still does not see this.
Is there maybe a better way to do this?
Thanks for your help
UPDATED with ViewModel
#model IEnumerable<DBName.ViewModels.ProfileData>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.ActionLink("Details", "Details", new { id=item.FantaTeamID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.FantaTeamID })
</td>
</tr>
}
How can I replace this code now? from where to get item.UserName?
Thanks

There are at least two different ways you can do it.
First one (better one) is using a Model to pass data. For that you should create a Model class:
//Model.cs
public class MyModel
{
public bool UserCanCreate { get; set;}
}
and specify it at the beginning of your View (use typed view):
#model %Your_model_namespace%.MyModel
...
<p>
#if (Model.UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}
And, of course, in your controller action do the following:
public ActionResult YourAction()
{
...
MyModel model = new MyModel();
model.UserCanCreate = ... ; // Determine if link shall be visible here
...
return View(model);
}
Second (faster one, although not recommended): use ViewData collection to pass values.
For example, in your controller action do:
public ActionResult YourAction()
{
...
ViewData["UserCanCreate"] = ... ; // Determine if link shall be visible here
...
return View();
}
and your view will look like:
...
<p>
#if ((bool)ViewData["UserCanCreate"])
{
#Html.ActionLink("Create New", "Create")
}
Also important: if you're trying to secure some actions that way then do not forget to add some checks for permissions inside "Create" action code - otherwise any user will be able to execute it by just entering corresponding URL in browser address bar - even if the ActionLink is not present.
UPDATE
As for your comment - you seem to be mixing domain models with view models. Domain models are the ones that represent your database entities, while view models are the ones that should be used for presentation. Generally it's controller's job to convert objects from domain to view models and vice versa.
In your case EF models are domain models.
For ViewModel you should create a separate class which will aggregate your domain model instance and any additional properties you want - like UserCanCreate.
Example model class:
public class UsersViewModel
{
public IEnumerable<DBName.Models.Users> Users { get; set; }
public bool UserCanCreate { get; set; }
... // Any other additional properties you may require
}
In your controller it will be:
public ActionResult YourAction()
{
...
UsersViewModel model = new UsersViewModel();
model.Users = ... ; // Get your IEnumerable<DBName.Models.Users> collection from DB and assign it to model property here
model.UserCanCreate = ... ; // Determine if link shall be visible here
...
return View(model);
}
and in view:
#model %Your_model_namespace%.UsersViewModel
... // use Model.Users wherever you need your IEnumerable<DBName.Models.Users>
<p>
#if (Model.UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}

When you create the model, set the value
UserCanCreate = _permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel),

Your View should interact with Controller using Model. So you should create a strongly typed View and pass to it a Model instance. For example:
// ViewModel
public class MyViewModel
{
public bool UserCanCreate{get;set;}
}
// Controller
public class MyController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
UserCanCreate = _permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel)
};
return View(model);
}
// Other Actions goes here....
}
// View
<p>
#if (Model.UserCanCreate)
{
#Html.ActionLink("Create New", "Create")
}

Your view must have a Model, for example UserModel.
Add UserCanCreate boolean property to UserModel and then you're able to access it in view.
For example:
namespace MvcApplication1.Models
{
public class UserModel
{
public bool UserCanCreate { get; set; }
}
}
and use it in the view:
#model MvcApplication1.Models.UserModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
#Model.UserCanCreate
</div>
</body>
</html>

Related

Model send from PartialView to controller

I start work in asp.net-mvc and I have problem to send model from partialview to controller.
So first this is the way I create partialview
#Html.Partial("Weather", ShopB2B.Controllers.HomeController.GetWeather())
GetWeather() is controller metod that initializes first data to model. Model looks like this
public class Weather_m
{
public IEnumerable<SelectListItem> City_dropdown { get; set; }
public string Temperature { get; set; }
}
It is nesesery to DropDownListFor, and partialview looks like this
#model ShopB2B.Models.Weather_m
#using (#Html.BeginForm("GetWeatherNew", "Home", new { weather = Model }, FormMethod.Post))
{
<table>
<tr>
<td>#Html.DropDownListFor(x => x.City_dropdown, Model.Miasta_dropdown)</td>
</tr>
<tr>
<td>#Html.LabelFor(x => x.Temperature, Model.Temperatura)</td>
<td><<input type="submit" value="Send" class="submitLink" style=" height: 40px;" /></td>
</tr>
</table>
}
And here is problem because I want send this model to controller and then check which field is selected, add something, and send this model again to partialview. Any idea, how to do it?????
You really should not be getting the data for your ViewModel type on view rendering.
Your data is type of ShopB2B.Models.Weather_m. Your strongly typed partial view expects this, this is all good. But instead of getting your ShopB2B.Models.Weather_m instentiated with ShopB2B.Controllers.HomeController.GetWeather(), you should create a ViewModel and return it to your strongly typed view, say MyViewModel. This should wrap an instance of ShopB2B.Models.Weather_m. So in your main view, your view would be strongly typed for:
#model ShopB2B.Models.MyViewModel
and you render your partial view like
#Html.Partial("Weather", Model.MyWeather_m)
I usually wrap the partial view inside the form as well, like:
#using (#Html.BeginForm("GetWeatherNew", "Home", new { weather = Model }, FormMethod.Post))
{
#Html.Partial("Weather", Model.MyWeather_m)
}
Hope this helps.
You should define the property bind to Dropdown appropriately. Since, you have defined the city_dropdown defined as IEnumarable so model binding will fails while sending from data to server due to data type mismatch (at client side City_dropdown will be generated as string for select control). In this case, you should change the property of Model as follows.
public class Weather_m
{
public string City_dropdown { get; set; }
public string Temperature { get; set; }
}
And
#Html.DropDownListFor(x => x.City_dropdown, Model.Miasta_dropdown)

ViewViewdata.Model; what model does?

ViewData.Model
i know that Viewdata contains the data that has been returned by view via
return view(//data);
but what does this .Model represents ?
Controller:
using Testing_linq.Models;
namespace Testing_linq.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
var DataContext = new RegistrationDataContext();
var Registration = DataContext.SelectRegistration_sp();
return View(Registration);
}
View:
<table>
#foreach(SelectRegistration_spResult reg in (IEnumerable<Object>)ViewData.Model)
{
<tr>
<td>#reg.userEmail </td>
</tr>
}
</table>
I'm using LInq to Sql classes in model.
The Model represents whatever dynamic information is required to render the page, and is something you provide to your view. For example, if you were going to create a View that shows a person's First name and Last name, you might come up with the following model:
public sealed class PersonViewModel
{
public string FirstName {get; set; }
public string LastName {get; set; }
}
It's preferable to use strongly-typed views, so the first line in your .cshtml file would be:
#model MyNameSpace.PersonViewModel
And when your controller returns the view, you would pass what needs to be rendered in to it:
public ActionResult Index()
{
return View(new PersonViewModel() { FirstName = "Moo", LastName = "Juice" });
}
ViewData.Model refers to the instance of the PersonViewModel passed in, and is accessed in your view via Model shortcut:
<body>
<p>
Hello #Model.FirstName !!
</p>
</body>

Single strongly Typed Partial View for two similar classes of different types

I have a Register Primary View which shows two different types of Addresses 1. Home Address 2. Mailing Address
public class RegisterModel
{
public AddressModel HomeAddress { get; set; }
public AddressModel MailAddress { get; set; }
}
public class AddressModel
{
public string Street1 { get; set; }
public string Street2 { get; set; }
public string State { get; set; }
public string City { get; set; }
}
My main Register View is Strongly Typed to RegisterModel as follows
#model MyNamespace.Models.RegisterModel
#{
Layout = "~/Views/_Layout.cshtml";
}
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
<div id="form">
#Html.Action("MyAddressPartial")
#Html.Action("MyAddressPartial")
</div>
}
MyAddressPartialView as follows : -
#model MyNamespace.Models.AddressModel
#{
Layout = "~/Views/_Layout.cshtml";
}
<div id="Address">
#Html.TextBoxFor(m=>m.Street1 ,new { #id="Street1 "})
#Html.TextBoxFor(m=>m.Street2,new { #id="Street2"})
#Html.TextBoxFor(m=>m.State ,new { #id="State "})
#Html.TextBoxFor(m=>m.City,new { #id="City"})
</div>
My RegisterController:-
// Have to instantiate the strongly Typed partial view when my form first loads
// and then pass it as parameter to "Register" post action method.
// As you can see the #Html.Action("MyAddressPartial") above in main
// Register View calls this.
public ActionResult MyAddressPartial()
{
return PartialView("MyAddressPartialView", new AddressModel());
}
I submit my Main Form to below mentioned action method in same Register Controller.
[HttpPost]
public ActionResult Register(RegisterModel model,
AddressModel homeAddress,
AddressModel mailingAddress)
{
//I want to access homeAddress and mailingAddress contents which should
//be different, but as if now it comes same.
}
I don't want to create a separate class one for MailingAddress and one for HomeAddress. if I do that then I will have to create two separate strongly typed partial views one for each address.
Any ideas on how to reuse the classes and partial views and make them dynamic and read their separate values in Action Method Post.
Edit 1 Reply to scott-pascoe:-
In DisplayTemplates Folder, I added following AddressModel.cshtml
<div>
#Html.DisplayFor(m => m.Street1);
#Html.DisplayFor(m => m.Street2);
#Html.DisplayFor(m => m.State);
#Html.DisplayFor(m => m.City);
</div>
Also In EditorTemplate Folder, I added following AddressModel.cshtml but with EditorFor
<div>
#Html.EditorFor(m => m.Street1);
#Html.EditorFor(m => m.Street2);
#Html.EditorFor(m => m.State);
#Html.EditorFor(m => m.City);
</div>
Now how do i use them in RegisterView and also how i read values in Controller's post Action Method ? What else would have to be modified ? I have added almost entire code above. I am pretty beginner to MVC.
The typical ASP.NET MVC method for doing this is to use EditorTemplates and DisplayTemplates for your custom types.
In ~/Views/Shared, Create two folders, DisplayTemplates, and EditorTemplates.
In the DisplayTemplates folder create a partial view with the name of your Model, ie (AddressModel), and create a DisplayFor Template.
In the EditorTemplates folder create another partial view named AddressModel.cshtml and create an EditorFor Template.
MVC will then automatically use your templates and give you the data that you are asking for.
Use #Html.EditorFor (or #Html.DisplayFor, for display) in your view:
#model MyNamespace.Models.RegisterModel
#{
Layout = "~/Views/_Layout.cshtml";
}
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
<div id="form">
#Html.EditorFor(m => m.HomeAddress)
#Html.EditorFor(m => MailAddress)
</div>
}
You will not need to have a separate controller action for the parts, just populate the addresses in the RegisterModel before in your controller. Like this:
[HttpGet]
public ActionResult Register() // this will be the page people see first
{
var model = new RegisterModel();
return View(model); // assuming your view is called Register.cshtml
}
[HttpPost]
public ActionResult Register(RegisterModel model){
DosomethingWithHomeAddress(model.HomeAddress);
DosomethingWithMailAddress(model.MailAddress);
model.IsSaved = true; // some way to let the user knwo that save was successful;
// if this is true, display a paragraph on the view
return View(model);
}

MVC 3 using #model twice

I would like to using two times #model to get data from another part of my website is it possible? Because now I have got error but if I have only this first #model everything working correct.
Look -> MVC 3 - Exception Details: System.InvalidOperationException
Error 2 'SportsStore.Entities.Kategorie' does not contain a definition for 'Opis' and no extension method 'Opis' accepting a first argument of type 'SportsStore.Entities.Kategorie' could be found (are you missing a using directive or an assembly reference?) c:\Users\Rafal\Desktop\MVC ksiązka\moj projekt\sklep\SportsStore.WebUI\Views\Product\List.cshtml 16 4 SportsStore.WebUI
#model IEnumerable<SportsStore.Entities.Towar>
#model IEnumerable<SportsStore.Entities.Kategorie>
#{
ViewBag.Title = "List";
}
<h2>List</h2>
#foreach (var p in Model)
{
<div class="item">
<h3>#p.Nazwa</h3>
#p.Opis
<h4>#p.Cena.ToString("c")</h4>
</div>
}
You only can have one Model per View. But you can use another object to declarate the model:
public class SomeViewModel
{
public IEnumerable<Towar> Towars;
public IEnumerable<Category> Categories;
public SomeViewModel(IEnumerable<Towar> towars, IEnumerable<Category> categories) {
Towars = towars;
Categories = categories;
}
}
And then use it in your view like this:
#model SportsStore.Entities.SomeViewModel
#foreach (var item in Model.Towars)
{
<div class="item">
<h3>#p.Nazwa</h3>
#p.Opis
<h4>#p.Cena.ToString("c")</h4>
</div>
}
#foreach (var item in Model.Categories) {
#item.Name #* or what you need down here *#
}
I would also recommend you to use english names in MVC. It's more clear to read and understand ;).
I think this would be a case to create a ViewModel (to combine the two entities you have) and then base a View off that ViewModel.
It is best to create a view model to represent your data. Your view model must only contain what you need to display on your view. Anything that is not used you can remove, no point of it being there. Your view model can look like this:
using SportsStore.Entities;
public class YourViewModel
{
public IEnumerable<Towar> Towars { get; set; }
public IEnumerable<Kategorie> Categories { get; set; } // I assume this is categories
}
Lets say that you have to use this view model in a create view then your create action can look something like this:
public class YourController : Controller
{
private readonly ITowarRepository towarRepository;
private readonly ICategoryRepository categoryRepository;
public YourController(ITowarRepository towarRepository, ICategoryRepository categoryRepository)
{
this.towarRepository = towarRepository;
this.categoryRepository = categoryRepository;
}
public ActionResult Create()
{
YourViewModel viewModel = new YourViewModel
{
Towars = towarRepository.GetAll(),
Categories = categoryRepository.GetAll()
};
return View(viewModel);
}
}
And then in your view:
#model YourProject.DomainModel.ViewModels.YourViewModel
#foreach (var towar in Model.Towars)
{
// Do whatever
}
#foreach (var category in Model.Categories)
{
// Do whatever
}

ASP.NET MVC - Problem with EditorTemplate for ICollection<T> mapped to Enum

I have an ASP.NET MVC 3 (Razor) website, and a (simplified) model called Review:
public class Review
{
public int ReviewId { get; set; }
public bool RecommendationOne
{
// hook property - gets/set values in the ICollection
}
public bool RecommendationTwo { // etc }
public ICollection<Recommendation> Recommendations { get; set; }
}
Recommendation is as follows:
public class Recommendation
{
public byte RecommendationTypeId
}
I also have an enum called RecommendationType, which i use to map the above recommendation to. (based on RecommendationTypeId).
So to summarize - a single Review has many Recommendations, and each of those Recommendations map to a particular enum type, i expose hook properties to simplify model-binding/code.
So, onto the View:
#Html.EditorFor(model => model.Recommendations, "Recommendations")
Pretty simple.
Now, for the editor template, i want to display a checkbox for each possible RecommendationType (enum), and if the model has that recommendation (e.g on edit view), i check the box.
Here's what i have:
#model IEnumerable<xxxx.DomainModel.Core.Posts.Recommendation>
#using xxxx.DomainModel.Core.Posts;
#{
Layout = null;
}
<table>
#foreach (var rec in Enum.GetValues(typeof(RecommendationType)).Cast<RecommendationType>())
{
<tr>
<td>
#* If review contains this recommendation, check the box *#
#if (Model != null && Model.Any(x => x.RecommendationTypeId == (byte)rec))
{
#* How do i create a (checked) checkbox here? *#
}
else
{
#* How do i created a checkbox here? *#
}
#rec.ToDescription()
</td>
</tr>
}
</table>
As the comments suggest - i don't know how to use #Html.CheckBoxFor. Usually that takes an expression based on the model, but i'm how sure how to bind to the hook property based on the currently looped enum value. E.g it needs to dynamically do #Html.CheckBoxFor(x => x.RecommendationOne), #Html.CheckBoxFor(x => x.RecommendationTwo), etc.
The current solution i have (which works), involves manually constructing the <input> (including hidden fields).
But as i'm just getting the hang of editor templates, hoping someone with experience can point me in a "strongly-typed" direction.
Or is there a nicer way (HTML Helper) i can do this?
I would start by introducing a proper view model for the scenario:
public enum RecommendationType { One, Two, Three }
public class ReviewViewModel
{
public IEnumerable<RecommendationViewModel> Recommendations { get; set; }
}
public class RecommendationViewModel
{
public RecommendationType RecommendationType { get; set; }
public bool IsChecked { get; set; }
}
Then the controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// TODO: query the repository to fetch your model
// and use AutoMapper to map between it and the
// corresponding view model so that you have a true/false
// for each enum value
var model = new ReviewViewModel
{
Recommendations = new[]
{
new RecommendationViewModel {
RecommendationType = RecommendationType.One,
IsChecked = false
},
new RecommendationViewModel {
RecommendationType = RecommendationType.Two,
IsChecked = true
},
new RecommendationViewModel {
RecommendationType = RecommendationType.Three,
IsChecked = true
},
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(ReviewViewModel model)
{
// Here you will get for each enum value the corresponding
// checked value
// TODO: Use AutoMapper to map back to your model and persist
// using a repository
return RedirectToAction("Success");
}
}
and the corresponding view (~/Views/Home/Index.cshtml):
#model YourAppName.Models.ReviewViewModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
#Html.EditorFor(model => model.Recommendations)
<input type="submit" value="Go" />
}
and finally the editor template (~/Views/Home/EditorTemplates/RecommendationViewModel.cshtml)
#model YourAppName.Models.RecommendationViewModel
<div>
#Html.HiddenFor(x => x.RecommendationType)
#Model.RecommendationType
#Html.CheckBoxFor(x => x.IsChecked)
</div>
Now the view code is cleaned as it should. No ifs, no loops, no LINQ, no reflection, this is the responsibility of the controller/mapper layer. So every time you find yourself writing some advanced C# logic in your view I would recommend you rethinking your view models and adapt them as necessary. That's what view models are intended for: to be as close as possible to the view logic.

Resources