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
Related
In my application,based on the selection of a field on the UI,I am loading a list of fields to be displayed from the database on the UI.Based on the selection,it is configured in the database each field is required or optional.After the UI is built,I do a post to the same view model(In the controller action).
What is the best way to do this?
I thought about doing this using reflection and add attributes on the properties based on the IsRequired flag against each field in the database,but i guess i have to eliminate the fields not needed from the UI.
So should i create a class as below as the best option or do i have something else?
Public Class ViewModelTicket
{
Public string EmailAddress{get;set}
Public bool IsRequired{get;set}
Public bool ShouldDisplay{get;set}
}
and throw a bunch of if else statements on the View ?
Thanks
I would go with what you started, but I would put it in a collection so that your model is a collection or has a collection of that class you started with. This way, you can easily expand your model to have more fields.
UPDATE
I still think you could use the collection to eliminate the need for ShouldDisplay in your model, and your collection will simply contain fields you want to display or get input for.
Alternatively, you could put your ShouldDisplay value in the class of the containing div.
So you view would have something like this:
<div class="show-#Model.ShouldDisplay">
#Html.LabelFor(m => m.EmailAddress)
#Html.TextBoxFor(m => m.EmailAddress)
</div>
Which would require this css:
.show-false { display: none; }
As for the IsRequired, you could use something like the RequiredIfTrue attribute in your model.
So your model would be:
Public Class ViewModelTicket
{
[RequiredIfTrue(IsRequired)]
Public string EmailAddress{get;set}
Public bool IsRequired{get;set}
Public bool ShouldDisplay{get;set}
}
Here's my situation: I've got a number of specialized object types in my application, and I'm following the standard convention for displaying them with custom templates in the /Shared/DisplayTemplates folder, and editing them with templates in /Shared/EditorTemplates. But I also want to be able to display a custom filter template for each type, so I'd like to add a /Shared/FilterTemplates folder, and implement my own #Html.FilterFor method, so that showing a Filter template is exactly like showing a Display or Editor template.
Does this seem like the best way to handle this situation, or is there a more correct/elegant way to do this in MVC? Thanks in advance.
I'm always using EditorTemplates when data is sent back to server. I assume the user can submit the filter to the server to perform the actual filtering.
When creating filters I prefer to create a model for the filter like:
public class UserListFilterModel
{
public string Username { get; set; }
public bool IsEnabled { get; set; }
}
The view for UserListFilterModel goes into EditorTemplates/UserListFilterModel.ascx.
And then add it as a property on my view model for the page.
public class MyPageViewModel
{
public UserListFilterModel Filter { get; set; }
}
Then I add the filter model to the model for the page and displays it like this:
<%= Html.EditorFor(x => x.Filter)%>
You are probably wrapping the filter in a form to allow the user to submit the values so I think it belongs in EditorTemplates. The users is in fact editing the filter model.
(If you really want to separate them ing you could use the UIHintAttribute but I wouldn't)
Edit: I added some sample code.
I think you misunderstand how Templates work. Templates do not make sense in the context you are describing.
Templates work on a SINGLE data item (although that data item can contain multiple data items, which in turn have their own templates).
The concept of a Filter is to control multiple data items, thus they do not map well to a template.
What you could do is create a DisplayTemplate for your collection class that adds filtering, thus no need to create a custom type of template. Just use DisplayTemplates.
I Posted the background to this question a few days ago.. but the answer is yet incomplete, so re-Posting with a summary.
I'm developing a MVC 3 Razor Web App, in which details of multiple categories of objects are stored. (Vehicles, Houses, Instruments etc) . All the objects share some common data (Title, Description etc) And some details which are specific to the category in which it belongs to. The Category list is expected to grow and in view of reducing maintainability we hope to reuse the same Add Object wizard . The wizard is based on the following implementation.
http://afana.me/post/create-wizard-in-aspnet-mvc-3.aspx
In the Multiple step wizard process , the final step allows the user to enter the category specific details (Model, Make, VIN etc for a Vehicle). The view page is bound to the "AssetView" Model, which is defined as follows
public class AssetView
{
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set;}
public SpecificAsset AssetDetails { get; set; }
}
the AssetDetails property will be dynamically changed at runtime according to the category type that is selected at a previous stage. the view looks like this
#model AssetView
....
<div class="wizard-step">
...
</div>
<div class="wizard-step">
...
</div>
<div class="wizard-step">
#Html.EditorFor(model => model.AssetDetails)
</div>
in the controller, based on category selection . i intialize assetdetails as follows
_thisAsset.AssetDetails = new MotorAsset();
I then override the views with type specific Editor templates.
The wizard works fine, but At the Save step in the controller the "AssetDetails" property has no values and it is shown to be of type "SpecificAsset" which is the base type. i'm unable to cast it to the specific type as well. However the formsCollection shows all values, the "AssetDetails" specific properties like "Transmission", "Make" are in the forms collection prefixed by "AssetDetails_" .
Keeping in mind that i need to handle over 20 types of specific categories (Motor, House etc) at the controller save step, how can i do this without some kind of hack?
If you know the type of the AssetDetails you could do it like this:
Take the AssetView as parameter to
the action(binds the common
properties)
Create a new instance of the
specific AssetDetails(for example
CarDetails)
Use one of the overloads of
TryUpdateModel to bind the values.
There are some overloads that let
you specify prefix so it should
always work
Add this object to the
AssetView.SpecificAsset
You need some way to know the specific type. But I guess you would have a variable that tracks the type so you could save it properly later anyway. If you don't it would be easy to add. Remember that it needs to be AssetView as AssetDetails are not being bound though.
If you need validation there is a TryValidateModel you could try too.
As I understood, number of models will grow and you want to reuse action and view for all models. One option I see is to make a custom ModelBinder. You would examine FormCollection and than create a specific asset model.
The problem with your solution is that SpecificAsset has no properties, so default model binder doesn't bind any of fields to it.
I have a question regarding how to get the white- and black-listing feature of the MVC controller's UpdateModel/TryUpdateModel to work on individual properties of child objects. For example, lets say I have a questionnaire collecting details about the person filling out the form and about his or her company.
My [simplified] form fields would be named, for example:
YourName
YourEmail
Company.Name
Company.Phone
Now in my model, lets say I don't want Company.ID or Company.IsPremiumMember to be tampered with, so I'd like to exclude them from the model-binding. I have tried a combination of whitelisting, blacklisting, and both in order to get this to work. I have not had any success. Here is what I am running into:
When I explicitly include in my whitelist the same four fieldnames I wrote above, the entire Company does not get bound (i.e., questionnaire.Company is left null) unless I also include "Company" in my whitelist. But then this has the undesirable effect of binding the ENTIRE company, and not just the two properties I want.
So, I then tried to include Company.ID and Company.IsPremiumMember in my blacklist, but this seems to be trumped by the whitelist and does not filter out these properties "after the fact" I suppose.
I know that there are other ways to express the "bindability", such as via the [Bind] attribute on members, but this is not ideal as I would like to have the same model classes used in other situations with different binding rules, such as allowing an admin to set whatever properties she would like.
I expect an obvious answer is that I should write my own model binder, and I've already starting trying to look into how to perhaps do this, but I was really hoping to use an "out-of-the-box" solution for what (in my opinion) seems like a very common scenario. Another idea I'm pondering is to fabricate my own ValueProvider dictionary to hand to the UpdateModel method, but again, something I'd rather avoid if there is an easier way.
Thanks for any help!
-Mike
Addendum #1
Here are the fields I present on my form:
YourName
YourEmail
Company.Name
Company.Phone
And here is what a black hat sends my way:
YourName=Joe+Smith&YourEmail=joe#example.com&Company.Name=ACME+Corp&Company.Phone=555-555-5555&Company.CreditLimit=10000000
(be sure you notice the extra parameter tacked on there at the end!)
And here is the problem:
As I originally posted, it doesn't seem possible (using the default model binder) to prevent CreditLimit from being set---it's either the entire Company or nothing---without some big workaround. Am I wrong?
Addendum #2
I'm pretty much convinced now that the simple objective I have is not possible "out of the box." My solution has been to walk through the posted form fields and construct my own ValueProvider dictionary, thus whitelisting the fields I want to allow, and handing that to UpdateModel.
Addendum #3
I still have not yet checked out AutoMapper, but with something like that at hand, the solution of creating some ViewModels/DTOs to handle this type of complex whitelisting---plus the ability to easily attach the same server-side validation (FluentValidation) I'm already using on my domain objects---seems a viable solution. Thank you everyone!
In general, the best way to go is to create view-models, models built specifically for your views. These models are not domain objects. They are data-transfer objects, built to transfer data from your controller actions to your view templates. You can use a tool like AutoMapper painlessly to create/update a domain model object from your view-model object or to create/update a view-model object from your domain model.
I have worked around this problem by making the action accepting two objects (the parent and the child object)
Example:
Suppose we have the following model:
public class Employee
{
public string Name { get; set; }
public int Age { get; set; }
public Company Comapany { get; set; }
}
public class Company
{
public int Phone { get; set; }
}
You build your form like this:
<form action="Home/Create" method="post">
<label for="Employee.Name">Name</label>
<%=Html.TextBox("Employee.Name") %><br />
<label for="Employee.Name">Age</label>
<%=Html.TextBox("Employee.Age") %><br />
<label for="Employee.Name">Comapany Phone</label>
<%=Html.TextBox("Company.Phone") %><br />
<input type="submit" value="Send" />
</form>
Then build a "Create" action that accept two objects one of type Employee and the other of type Comapny and assign the Company object to the Employee.Company property inside the action:
public ActionResult Create(Employee employee,Company company)
{
employee.Comapany = company;
UpdateModel(employee);
return View();
}
I hope this help you.
Edit:
public ActionResult Create(Employee employee,Company company)
{
employee.Comapany = company;
UpdateModel(employee,new[] {"Name","Email","Phone"});
return View();
}
Have you tried using the following?:
public ActionResult Create([Bind(Exclude="PropertyToExclude1, PropertyToExclude2")] Employee employee)
{
//action code here
}
or use Include instead of Exclude to List what fields can be bound rather than which can't
Given the following class, what is your opinion on the best way to handle create/edit where Attributes.Count can be any number.
public class Product {
public int Id {get;set;}
public string Name {get;set;}
public IList<Attribute> Attributes {get;set;}
}
public class Attribute {
public string Name {get;set;}
public string Value {get;set;}
}
The user should be able to edit both the Product details (Name) and Attribute details (Name/Value) in the same view, including adding and deleting new attributes.
Handling changes in the model is easy, what's the best way to handle the UI and ActionMethod side of things?
Look at Steve Sanderson’s blog post Editing a variable length list, ASP.NET MVC 2-style.
Controller
Your action method receives your native domain model Product and stays pretty simple:
public ActionResult Edit(Product model)
View
Edit.aspx
<!-- Your Product inputs -->
<!-- ... -->
<!-- Attributes collection edit -->
<% foreach (Attribute attr in Model.Attributes)
{
Html.RenderPartial("AttributeEditRow", attr);
} %>
AttributeEditRow.ascx
Pay your attention to helper extension Html.BeginCollectionItem(string)
<% using(Html.BeginCollectionItem("Attributes")) { %>
<!-- Your Attribute inputs -->
<% } %>
Adding and editing of new attributes is possible too. See the post.
Use the FormCollection and iterate through the key/value pairs. Presumably you can use a naming scheme that will allow you to determine which key/value pairs belong to your attribute set.
[AcceptVerbs( HttpVerb.POST )]
public ActionResult Whatever( FormCollection form )
{
....
}
Use a custom Model Binder, and write the Action methods as you would normally:
ActionResult Edit(
int id,
[ModelBinder(typeof(ProductModelBinder))] Product product
) ...
In your ProductModelBinder, you iterate over the Form Collection values and bind to a Product entity. This keeps the Controller interface intuitive, and can help testing.
class ProductModelBinder : IModelBinder ...
Depends on the experience you are looking to create for the user. I have implemented something similar for tagging content. In the model, Tags are represented as IList, but the UI shows a comma delimited list in a single text field. I then handle merging the items in the list into a string to populate the text field, and I split the input to put items back into the IList in the model.
In my DAL, I then deal with converting the List into LINQ entities, handle inserts and deletes, etc.
It isn't the most straight forward code, but it isn't too difficult to manage and it gives the user an expected interface.
I'm sure there are other ways to handle it but I would focus on what would work best for the user and then work out the mapping details based on that.
Andrew,
I'm thinking something a little more difficult than tags. In this simple case a name / value pair .. color: Red; size: 10; material: cotton.
I think anything that could be used on that could extend to more complex. I.e. Adding a category and adding all its items on the same page. It's relatively easy to add another line using some jQuery, but what's the consensus on sending the info to the ActionMethod?
You can't code:
public ActionResult Whatever(stirng attr1Name, string attr2Name, string attr3Name ...
Also I don't think accepting this would work either:
public ActionResult Whatever(ILIst<Attribute> attributes, string productName ...