asp.net mvc 5 page show/hide/access based on table value - asp.net-mvc

Please see code below that creates menu items. Now how can I show/hide/access a menu item that is created as below. Also I want to make sure user do not directly access page. I want to do this based on value in one my table( no default table). I know we can use [Authorize(Roles = "Admin")] based on roles, but like I mentioned I want to manually look for a value in a custom table then show/hide/allow access of page.
Thanks.
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>#Html.ActionLink("Roles", "Index", "AspNetRoles", new { }, new { #style = "color:#21ce99;" }) </li>
<li>#Html.ActionLink("Users", "Index", "AspNetUsers", new { }, new { #style = "color:#21ce99;" })</li>
<li>#Html.ActionLink("User Groups", "Index", "UserGroupRoles", new { }, new { #style = "color:#21ce99;" })</li>
</ul>
#Html.Partial("_LoginPartial")
</div>
Note: After getting help from Stephen ,I managed to write below code. But now I am getting error
"The controller for path '/' was not found or does not implement IController."
Controller Code
using System.Web.Mvc;
using .Models;
namespace TestProj.Controllers
{
public class NavigationVMController : Controller
{
[ChildActionOnly]
public ActionResult Navigation()
{
NavigationVM NavItems = new NavigationVM();
NavItems.CanViewRoles = true; //setting all true temporarily for testing
NavItems.CanViewUsers = true;
NavItems.CanViewUserGroups = true;
//return View(Alex);
return View("_Navigation", NavItems);
}
// GET: NavigationVM
public ActionResult Index()
{
return View();
}
}
}
Model Code
namespace TestProj.Models
{
public class NavigationVM
{
public bool CanViewRoles { get; set; }
public bool CanViewUsers { get; set; }
public bool CanViewUserGroups { get; set; }
}
}
Partial view Code
#model TestProj.Models.NavigationVM
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
#if (Model.CanViewRoles)
{
<li>#Html.ActionLink("Roles", "Index", "AspNetRoles", new { }, new { #style = "color:#21ce99;" }) </li>
}
<li>#Html.ActionLink("Users", "Index", "AspNetUsers", new { }, new { #style = "color:#21ce99;" })</li>
<li>#Html.ActionLink("User Groups", "Index", "UserGroupRoles", new { }, new { #style = "color:#21ce99;" })</li>
</ul>
#Html.Partial("_LoginPartial")
</div>
Calling code from _Layout.cshtml
#Html.Action("Navigation", "NavigationVMController")

Create a partial view for the navigation links and call a ChildActionOnly method that initializes a view model based on some parameters that you can use to conditionally check which links should be displayed
View model
public class NavigationVM
{
public bool CanViewRoles { get; set; }
public bool CanViewUsers { get; set; }
public bool CanViewUserGroups { get; set; }
}
Controller (probably in a BaseController so its available everywhere
[ChildActionOnly]
public ActionResult Navigation
{
NavigationVM model = new NavigationVM();
model.canViewRoles = ?? // call a service which returns the value based on the current user
....
return PartialView("_Navigation", model)
}
Partial View (_Navigation.cshtml)
#model yourAssembly.NavigationVM
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
#if (Model.CanViewRoles)
{
<li>#Html.ActionLink("Roles", "Index", "AspNetRoles", new { }, new { #style = "color:#333;" })</li>
}
.... // ditto for CanViewUsers and CanViewUserGroups
</ul>
</div>
and in the main view or layout
#Html.Action("Navigation") // renders the navigation links
#Html.Partial("_LoginPartial")
This will only display the appropriate links, but of course a user could still type in the address to try and access the pages so your controller methods still need to perform validation
AspNetRolesController
public ActionResult Index()
{
// Check permissions based on user
bool canView = ?? // call a service which returns the value based on the current user
if (!canView)
{
return new HttpUnauthorizedResult();
}
....
}

Related

Problems to send data back to the controller. Parameter is always null MVC

I have problems to send back data from view to controller. I'm quite new to MVC and I can't figure it out what is the problem.
This is the view:
#model IEnumerable<OnlineCarStore.Models.CategoriesVM>
<div class="container">
<div class="row">
#using (Html.BeginForm("SubCategory", "Product"))
{
<div class="list-group col-sm-3" style="width:280px;">
#{var selected = string.Empty;
if (#HttpContext.Current.Session["selectedCar"] == null)
{
selected = string.Empty;
}
else
{
selected = #HttpContext.Current.Session["selectedCar"].ToString();
}
foreach (var c in Model)
{
<a href="#Url.Action("SubCategory", "Product", new { selected = selected, id = #c.ID, category = #c.CategoryName })" id="link" class="list-group-item">
<span> #c.CategoryName</span>
</a>
for (int i = 0; i < c.Childrens.Count; i++)
{
#Html.HiddenFor(x => c.Childrens[i].Item)
#Html.HiddenFor(x => c.Childrens[i].Children)
}
}
}
This is the view model for the data that I need:
public class CategoriesVM
{
public int ID { get; set; }
public int AtpID { get; set; }
public string CategoryName { get; set; }
public List<Helpers.TreeItem<Categories>> Childrens { get; set; }
}
This is in the controller:
public ActionResult SubCategory(IEnumerable<OnlineCarStore.Models.CategoriesVM> Model)
{
. In the Model parameter I need all the data that the CategoriesVM contains, but the Model parameter is always null.
Can you please advise what I'm doing wrong? What I'm missing?
Thanks!
You cannot (and should not) pass all the properties of your Category object via a link click! It works only when your object is lean-flat with very few properties. When your object is complex, this is not ideal!
You should simply pass the unique Id (category id) to the next action method and in which you use this id to query the needed data and use that.
I also noticed that you are setting the same id value to the link in the loop. It will produce multiple anchor tag with same Id value. That is invalid HTML. So let's remove that.
<a href="#Url.Action("SubCategory", "Product", new { id = #c.ID })"
class="list-group-item">
<span> #c.CategoryName</span>
</a>
Now in your SubCategory action method
public ActionResult SubCategory(int id)
{
//using the id, get the data needed (may be the category & subcategories)
// to do : Return something
}

MVC5 ActionResult model being passed as empty

I have read plenty of posts about this issue but I can't seem to find a solution that fits with my implementation. I'm giving MVC another attempt (I'm a webforms guy). The model being passed to my ActionResult is basically empty when it should be populated. I'm starring at the sample that works and I can find no differences. It seems to be something impossible to debug too. Any pointers will be greatfully appreciated.
View:
#model WebApplication1.Models.SiteViewModel
#{
ViewBag.Title = "Delete your site";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<p>Are you sure you want to delete this site?</p>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false)
<ul class="list-group">
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Name)</b></div>
<div class="col-md-6">#Model.Name</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Phase)</b></div>
<div class="col-md-6">#Model.Phase</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Type)</b></div>
<div class="col-md-6">#Model.Type</div>
</div>
</li>
</ul>
<button type="submit" class="btn btn-danger">Delete</button>
#Html.ActionLink("Back to List", "Index", null, new { #class = "btn btn-default" })
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Model:
public class SiteViewModel
{
public SiteViewModel()
{
Services = new List<ServiceModel>();
}
public SiteViewModel(SiteModel site)
{
this.SiteId = site.SiteId;
this.Name = site.Name;
this.Type = site.Type;
this.Phase = site.Phase;
this.Services = site.Services;
}
public int SiteId { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
public SchoolType Type { get; set; }
public SchoolPhase Phase { get; set; }
public DateTime? Deleted { get; set; }
public virtual ICollection<ServiceModel> Services { get; set; }
}
Controller Action:
[HttpPost]
public ActionResult Delete(SiteViewModel model)
{
var site = siteRepository.GetById(model.SiteId);
if (site == null) { throw new ArgumentException(string.Format("Site with Id [{0}] does not exist", model.SiteId)); }
try
{
siteRepository.SoftDeleteAndSubmit(site);
base.SetSuccessMessage("The site has been (soft) deleted.");
return RedirectToAction("Index");
}
catch (Exception ex)
{
base.SetErrorMessage("Whoops! Couldn't delete the site. The error was [{0}]", ex.Message);
}
return View(model);
}
Thanks,
Chris.
If you want a fully-populated model you'll need to use form elements or the form helper functions to post your data.
#using(Html.BeginForm())
{
<input type="text" value="#Model.Name" />
// or
Html.TextBoxFor(m => m.Name)
...
}
The example you linked relies on a URL routing rule to match a model's parameter. So you need to rename SiteId to Id or add/modify a routing rule.
If you only need the id then I would just pass that parameter as it will make your intent more obvious and is less prone to breaking.
#using(Html.BeginForm())
{
#Html.HiddenFor(m => m.Id)
<button type="submit">Delete</button>
}
[HttpPost]
public ActionResult Delete(int id)
{
var site = siteRepository.GetById(id);
...
}

ASP.NET MVC : Generating multi level menu using recursive helpers

I am using this code for generating menu, this menu populates items from database (Category Table) using this technique
Partial View :
#using SarbarzDarb.Helper
#model IEnumerable<SarbarzDarb.Models.Entities.Category>
#ShowTree(Model)
#helper ShowTree(IEnumerable<SarbarzDarb.Models.Entities.Category> categories)
{
foreach (var item in categories)
{
<li class="#(item.ParentId == null && item.Children.Any() ? "dropdown-submenu" : "")">
#Html.ActionLink(item.Name, actionName: "Category", controllerName: "Product", routeValues: new { Id = item.Id, productName = item.Name.ToSeoUrl() }, htmlAttributes: null)
#if (item.Children.Any())
{
ShowTree(item.Children);
}
</li>
}
}
also I'm passing model from controller to above partial view this way :
public IList<Category> GetAll()
{
return _category.Where(category => category.ParentId == null)
.Include(category => category.Children).ToList();
}
public ActionResult Categories()
{
var query = GetAll();
return PartialView("_Categories",query);
}
my Category Model:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Updated :
using recursive helpers are good idea but it doesn't generate sub-menu for me. what's my problem?
Any Idea?
Thanks in your advise
finally I solved my problem by adding <ul class="dropdown-menu"> :
#using SarbarzDarb.Helper
#model IEnumerable<SarbarzDarb.Models.Entities.Category>
#ShowTree(Model)
#helper ShowTree(IEnumerable<SarbarzDarb.Models.Entities.Category> categories)
{
foreach (var item in categories)
{
<li class="#(item.Children.Any() ? "dropdown-submenu" : "")">
#Html.ActionLink(item.Name, actionName: "Category", controllerName: "Product",
routeValues: new { Id = item.Id, productName = item.Name.ToSeoUrl() }, htmlAttributes: null)
#if (item.Children.Any())
{
<ul class="dropdown-menu">
#ShowTree(item.Children)
</ul>
}
</li>
}
}

Retrieving values from partial view during post method

I have a view which contains a dropdown list and on dropdownlist item being selected I load a partial view. And when the form is submitted I want to be able to get both the values from main view and partial view during form submit.
Here is the main view
#model AdminPortal.Areas.Hardware.Models.CreateModule
#{
ViewBag.Title = "Create Module";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
}
#Html.ValidationSummary(true)
<fieldset class="form-horizontal">
<legend>Add a Module <small>Create</small></legend>
#using (Html.BeginForm("CreateModule", "Module", new{id="AddModuleForm"}))
{
#Html.ValidationSummary(true)
<div class ="controls">
<div class="input-block-level">#Html.TextBoxFor(model => model.ModuleId, new {#placeholder = "ModuleID"})</div>
<br/>
<div class ="input-block-level" id="selectedModuleTypeName">#Html.DropDownListFor(model => model.SelectedModuleTypeName, Model.TypeNames,"Select Moduletype", new{id = "ModuleList"})</div>
<br/>
<div id="partialDiv"></div>
</div>
<div class="form-actions" id="buttons">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
<div>
#Html.ActionLink("Back to List", "ModuleList")
</div>
<script>
$("#buttons").hide();
$("#ModuleList").on("change", function() {
var modId = $(this).val();
$.get('#Url.Action("GetModulePropertyName", "Module")', { moduleTypeValue: modId }, function(result) {
$("#partialDiv").html(result);
});
//uncomment following section to check if the partial view is working properly
/*.done(function() { alert("done"); })
.fail(function() { alert("fail"); })
.always(function() { alert("completed"); });*/
});
$("#buttons").show();
</script>
and here is the partial view
#model IEnumerable<string>
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})</div>
<br/>
}
Here is my model
public class CreateModule
{
//Empty form to handle form serialization
public CreateModule()
{
}
[Required]
public string ModuleId { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateEntered { get; set; }
[Required]
public string SelectedModuleTypeName { get; set; }
public IEnumerable<SelectListItem> TypeNames { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
Here is the method that script in main view forwards to
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
var modulePropertyNames = _repository.GetModuleKindPropertyNames(moduleTypeValue);
return PartialView("GetModulePropertyName",modulePropertyNames);
}
and finally here is httppost method for the main view
[HttpPost]
public ActionResult CreateModule(CreateModule moduleV)
{
var module = new Module
{
ModuleTypeId = Convert.ToInt64(moduleV.SelectedModuleTypeName),
ModuleId = moduleV.ModuleId,
DateEntered = moduleV.DateEntered,
};
if (ModelState.IsValid)
{
_repository.AddModule(module);
Success("Module added successfully!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Something went wrong!");
return RedirectToAction("CreateModule", "Module", new { area = "Hardware" });
}
Current situation:
When the form is posted, the properties value of the model that is being passed via partial view is null. I get other values, like typename, Module ID.
What I'd want:
I also want to get the value of properties that is being passed via partial view.
You don't have any input field for the Properties property anywhere in your form. So it will always be null. That's normal.
Here's how you could proceed. Start by setting the correct navigational property so that the helper generates correct names of the corresponding input fields.
Also make sure that you are passing an IEnumerable<Property> model to the partial if you want to be able to get them back correctly:
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
IList<Property> model = ...
return PartialView("GetModulePropertyName", model.ToList());
}
and in your partial view use an editor template:
#model IList<Property>
#{
// This indicates the current navigational context to the helpers
ViewData.TemplateInfo.HtmlFieldPrefix = "Properties";
}
#Html.EditorForModel()
and the last step is to define a custom editor template for the Property class: ~/Views/Shared/EditorTemplates/Property.cshtml (note that the name and location of the template is important)
#model Property
<div class="input-block-level">
#Html.HiddenFor(m => m.Name)
#Html.TextBoxFor(m => m.Value, new { placeholder = Model.Name })
</div>
<br />
Try using the
List<Property>
as a model in your partial view and pass the CreateModule.Properties as model from your View
The problem is model binder can not figure out there
#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})
belongs to as the "names" is not a property on your model class. If you need to bind to the CreateModule.Properties you need to change the partial view to emit textboxes with aproprate names, like this one:
#model IEnumerable<string>
#
{
int i=0;
}
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBox("Properties[" + i + "].Value")</div>
<br/>
}

How to pass an object from one view to another in ASP.MVC

I still don't quite grasp how one can use information in the one cshtml file, gotten from another cshtml file. My program consists out of a gallery. When the user clicks on one of the pictures, instead of simply directing to a page containing only the image url, I would like to redirect to another view that displays the picture and information pertaining to that picture. Here is the relevant code of my faulty attempt:
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var client = new WebClient();
var response = client.DownloadString(Url.Action("gallery", "photo", null, Request.Url.Scheme));
var jss = new JavaScriptSerializer();
var result = jss.Deserialize<List<Photo>>(response);
return View();
}
public ActionResult FullImage(Photo m)
{
return View();
}
}
View:
#section mainContent {
<ul class="thumbnails">
#foreach (var photo in Model)
{
<li class="item">
<a href='#Url.Action("FullImage", "Home", new {imageUrl="~/photos/" + photo.FileName, title= photo.Title, description= photo.Description})'>
<img alt="#photo.Description" src="#Url.Content("~/photos/" + photo.FileName)" class="thumbnail-border" width="180" />
</a>
<span class="image-overlay">#photo.Title</span>
</li>
}
</ul>
}
Model:
namespace PhotoGallery.Models
{
public class Photo
{
public string Title { get; set; }
public string FileName { get; set; }
public string Description { get; set; }
public string imageUrl { get; set; }
}
}
You can't bind to a model by using an action link. You need to pass a primary key or a unique key to your Action, and find your Model in the Action based on the key.
As an example, you can do this in your View:
#section mainContent {
<ul class="thumbnails">
#foreach (var photo in Model)
{
<li class="item">
<a href='#Url.Action("FullImage", "Home", new { fileName = photo.FileName})'>
<img alt="#photo.Description" src="#Url.Content("~/photos/" + photo.FileName)" class="thumbnail-border" width="180" />
</a>
<span class="image-overlay">#photo.Title</span>
</li>
}
</ul>
}
Then, in your Action, you'll have:
public ActionResult FullImage(string fileName)
{
// Example of some code to get the photo from the repository. It's better to use a photoID instead of the fileName.
var photo = db.Photos.FindPhoto(fileName);
return View();
}

Resources