How to Bind Model with variable at Runtime in mvc 4 - asp.net-mvc-4

I have a Model having more than 50 fields and I want to display list of those fields but not all of them.
So I have created two viewbag having list of display field and all values.
Now I have,
<table>
<tr>
#foreach (var v in #ViewBag.columnList)
{
<th>#v</th>
}
</tr>
#foreach (var u in #ViewBag.userlist)
{
<tr>
#foreach (var c in #ViewBag.columnList)
{
<td>#u.c</td>
}
</tr>
}
I want to assign #u.#c to select column to display in table.

public abstract class UserBaseModel
{
public string Name {get;set;}
public string City {get;set;}
}
public class UserModel : UserBaseModel
{
public string State {get;set;}
}
public class AdminModel : UserBaseModel
{
public string Gender {get;set;}
}
These classes show the different user types having different Properties. You could then have a view stongly typed with the base class
#model UserBaseModel
This way you can pass different models to the view depending on the user. You then have a choice how to access those Properties. It might allow you to select out the Property even though there won't be intelisense, I haven't tried.
You could also have partial views for user type, then depending on type of user in questions, load the relevant partial. Point being, you can pass either User to Admin to the view in the example and both will work.

Related

Instantiating ModelExpression directly

Let's say I have the following input tag which utilizes the built-in tag helper:
#model ProductViewModel
<label asp-for="Product.Id"></label>
In my case, this expands into the following:
<label for="Product_Id">Id</label>
I see that asp-for is expecting a ModelExpression:
In tag helper implementations, I often see a property like the following:
public ModelExpression For { get; set; }
It appears that this is automatically populated when the tag helper is used.
Is there a way to instantiate a ModelExpression directly in C#?
I.e. something like this:
var exp = new ModelExpression("Product.Id",...)
I'd like to be able to generate "Product_Id" and "Id" from Product.Id as the input tag helper did.
As far as I know, you can specify that your property is to be set to the name of some property on the View's Model object by declaring your property with the ModelExpression type. This will enable any developer using your property to get IntelliSense support for entering a property name from the Model object. More importantly, your code will be passed the value of that property through the ModelExpression's Model property.
Sample code as below:
[HtmlTargetElement("employee-details")]
public class EmployeeDetailTagHelper : TagHelper
{
[HtmlAttributeName("for-name")]
public ModelExpression EmployeeName { get; set; }
[HtmlAttributeName("for-designation")]
public ModelExpression Designation { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "EmployeeDetails";
output.TagMode = TagMode.StartTagAndEndTag;
var sb = new StringBuilder();
sb.AppendFormat("<span>Name: {0}</span> <br/>", this.EmployeeName.Model);
sb.AppendFormat("<span>Designation: {0}</span>", this.Designation.Model);
output.PreContent.SetHtmlContent(sb.ToString());
}
}
Code in the View page:
#model WebApplication7.Models.EmployeeViewModel
<div class="row">
<employee-details for-name="Name" for-designation="Designation"></employee-details>
</div>
Code in the Model
public class EmployeeViewModel
{
public string Name { get; set; }
public string Designation { get; set; }
}
From above code, you can see that we could custom the attribute name. More detail information about using the ModelExpression, check the following links:
Creating Custom Tag Helpers With ASP.NET Core MVC
Expression names
I'd like to be able to generate "Product_Id" and "Id" from Product.Id
as the input tag helper did.
Besides, do you mean you want to change the Product. Id to Product_Id, in my opinion, I'm not suggesting you change it, because generally we can use "_" as a separator in the property name. So, if we are using Product.Id, it means the Product's Id property, and the Product_Id means there have a Product_Id property.
To answer the question:
Is there a way to instantiate a ModelExpression directly in C#"
Yes you can, through IModelExpressionProvider and its CreateModelExpression method. You can get an instance of this interface through DI.
Now, if you're already in your view and working with tag helpers, Zhi Lv's answer is all you need, as the functionality is built-in and much easier to use. You only need IModelExpressionProvider for when you're in your Razor Page, Controller, or perhaps some custom middleware. Personally, I find this functionality useful for my Ajax handlers that need to return one of my ViewComponents that has a ModelExpression argument (so that I can easily call it from my Pages/Views too.)
To call CreateModelExpression, you'll need a strongly-typed instance of ViewData. In Razor Pages, this is as easy as casting the ViewData property to the strongly-typed instance of your PageModel's type (presuming you don't have a page model hierarchy):
var viewData = (ViewDataDictionary<IndexModel>)ViewData;
If you're using MVC and you're in the controller, that won't exist yet. Best you can do is make your own instance.
var viewData = new ViewDataDictionary<ErrorViewModel>(new EmptyModelMetadataProvider(),
new ModelStateDictionary());
Once you get your strongly-typed ViewData instance, you can obtain your desired ModelExpression like this, just like using a lambda expression in your views:
var myPropertyEx = _modelExpressionProvider.CreateModelExpression(viewData,
m => m.MyProperty);

Should I have both text and value in my model for a property that is selected from dropdownlist

In ASP.NET MVC application I have a model named CarSearchCriteria:
public class CarSearchCriteria{
public int CarMake {get;set;} // This is selected from a dropdownlist
public int YearOfFirstReg {get;set;}
public string ModelVariant {get;set}
}
I have two views - one for editing and the other one for viewing. In the editing view for the CarMake property I can do the following. I know I could have used DropDownListFor but didn't want to mess with SelectList for the time being:
<select name="CarMake">
<option value="1">BMW</option>
<option value="2">Mercedes</option>
<option value="3">Toyota</option>
</select>
So the model binding mechanism will easily bind the selected value to the appropriate model property. But what about the reading mode. I can't show 1s or 2s. I need to show BMW, Mercedes and so on. My question is what is the preferred way, do I have to have a property name that holds the actual textual information, something like CarMakeText?
You could have both the identifier (which you currently have) as well as the Make object itself. The latter would never need to be accessed when building the model, but can be accessed when reading the model. A lazy-loaded read-only property often works well for that. Something like this:
public int CarMakeID { get; set; }
public Make CarMake
{
get
{
if (CarMakeID == default(int))
return null;
// fetch the Make from data and return it
}
}
Naturally, this depends a lot on what a Make actually is and where you get it. If there's just some in-memory list somewhere then that should work fine. If fetching an instance of a Make is a little more of an operation (say, fetching from a database) then maybe some in-object caching would be in order in case you need to access it more than once:
public int CarMakeID { get; set; }
private Make _carMake;
public Make CarMake
{
get
{
if (CarMakeID == default(int))
return null;
if (_carMake == null)
// fetch the Make from data and save it to _carMake
return _carMake;
}
}
David's solution is just fine but for some reason I find my own solution to better fit my needs and besides that I find it more elegant. So basically what I do is I create a class that holds the textual descriptions of all the properties that keep just ID. For example, I have the following model:
public class EmployeeModel{
public int EmployeeID {get;set;}
public string FullName {get;set}
*public int DepartmentID {get;set}
*public int SpecialityID {get;set;}
public int Age {get;set;}
}
The properties marked with asterisk are the properties that keep ids of possible many predefined options and when showing we're supposed to show the actual descriptions, not the number representations. So for this purpose, we create a separate class:
public class EmployeeTextValues{
public string DepartmentName {get;set;}
public string SpecialityName {get;set;}
}
And then I just add this class as a property to my model:
public EmployeeTextValues TextValues {get;set;}
After that, it's quite easy to access it from anywhere, including Razor.
P.S. I'm sure that a lot of people will tend to do the following before initializing this property:
Employee emp=new Employee;
emp.Age=25;
emp.TextValues.DepartmentName="Engineering";// Don't do this
If you try to access or set Textvalues.Someproperty you'll get Object reference not set to an instance of an object. So do not forget to set TextValues first to some initialized object. Just a kind reminder, that's all.

Call controller of another project from view in MVC4

I have two projects, d2admin and PartyWeb.
d2admin is the actual UI, it will have all necessary css, js and views etc., and also controllers if required.
PartyWeb is having controllers for each table in Party.
Say I have a table called - Organization. This table's controller will be in PartyWe/Controllers folder.
I will have the views in d2admin.
Now my problem is how can I invoke the OrganizationController exists in PartyWeb from the view Organization.cshtml exists in d2admin?
I tried with Html.RenderAction, this is working for the controllers exists in same, when I call the controller of diff project I am getting - missing method exception.
I found your problem interesting and decided to test for myself. I created two MVC projects (but one of them could be a class library as well, I was lazy though). The first MVC project became the main one with routes and views, the second project got the model and the controller. It worked like a charm from start and here is how I did it.
I created the model in the second project, named Car in my example (the name UsersContext is left from the default files because I wanted to change as little as possible).
namespace PartyBiz.Models
{
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<Car> Cars { get; set; }
}
[Table("Cars")]
public class Car
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int CarId { get; set; }
public string CarName { get; set; }
}
}
I then built the project and created a controller with EF connections to Car (by right clicking on the Controller folder and select MVC controller with read/write actions and views, using Entity Framework)
The controller looked like this when done (many lines have been removed to keep the example short)
namespace PartyBiz.Controllers
{
public class CarController : Controller
{
// UsersContext is a left over from the default MVC project
private UsersContext db = new UsersContext();
public ActionResult Index()
{
return View(db.Cars.ToList());
}
// Many other actions follows here...
}
}
The views that were created in the second project (PartyBiz) I copied over to the first project (d2admin) by drag and drop. I then deleted the views from the second project to make sure they weren't used there.
I also had to add a reference from the first project (with the views) to the second project (model and controller). After that it worked just fine to run the first project.
I continued to enable migrations in the model-controller-project and got a database connection without any problems. I could see that the controller managed to save data even though it was located in a different project.
I hope this can help you on the way...
EDIT:
Using the following code in the views from the first project (d2admin) worked fine even though the Car controller referred to exists in the second project. This link was used in the home (controller) / index (view) in the first project.
#Html.ActionLink("Go to the cars", "Index", "Car")
EDIT2:
This is the index view for the Car controller. The view is in d2admin and is referencing a controller in the PartyBiz project.
#model IEnumerable<PartyBiz.Models.Car>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.CarName)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CarName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CarId }) |
#Html.ActionLink("Details", "Details", new { id=item.CarId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CarId })
</td>
</tr>
}
</table>
I acknowledge this is an old question with an already accepted answer; however, I ran into the same problem and was able to solve it and would like to share my experience.
From what I understand the following things are true:
d2admin is the code that handles the front end of the web site, and the controllers are used to drive the views and/or view models.
PartyWeb is used as an API at a domain level to interact with some datasource.
OrganizationController is the controller you're using to get data from the datasource to the d2admin project (and vice-versa)
With all of that in mind, arise the power of partial views!
Let's use the very simple View that would be located in d2admin/Views/SomeController.cshtml where SomeController is the folder that reflects the controller associated with these views.
<h3>A Very Basic View</h3>
#Html.Partial("_SomePartialView", OrganizationController.GetOrganizations())
Notice that this view has no model, and calls a partial and it's model is populated right there... and that's it! Now how would we write _SomePartialView.cshtml?
We will put it in the d2admin/Views/Shared folder, so the full path would be: d2admin/Views/Shared/_SomePartialView.cshtml. The file will look like
#model IEnumerable<PartyWeb.Models.Organization>
<div>
#foreach(var o in Model){
#Html.DisplayFor(modelItem => item.Id)
#Html.DisplayFor(modelItem => item.Name)
<br/>
}
</div>
As we can see this view will display some basic info assuming the following is our model found at PartyWeb/Models/Organization.cs
public class Organization
{
public int Id {get; set;}
public string Name {get; set;}
// some additional properties
}
and for the final bit of magic...
Within OrganizationController.cs we need to add the static action that will enable us to get the data to bind to our partial view's model. So we would add the following:
public class OrganizationController : ApiController
{
// Some Other Actions
[HttpGet]
public static List<Organization> GetOrganizations()
{
var dataSource = GetDataSource(); // Some Method that exposes the datasource
return ReadAllOrganizations(dataSource); // Some method that allows us to read all of the organiztions from the dataSource, i.e. some sql that executes against a database.
}
}

ASP.NET MVC 4: table for model's inner list

I have a ViewModel that extends a well known base class in order to allow shared View to read common attributes. This is by design.
I also have pages that display details about lists of entities contained in the model. The following code provides an example:
public abstract class AbstractViewModel {
public Exception lastError; //If not null triggers a big warning
}
public class Cat{
[Display(...)]
public string Name;
}
public class CatListViewModel: AbstractViewModel {
public IEnumerable<Cat> cats;
}
Now my partial view needs to display a list of cats using common MVC ways
#model Org.Zighinetto.CatListViewModel
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Cats.Name) //Doesn't work!!
</th>
</tr>
#foreach (var item in Model.Cats)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</table>
Unfortunately I can't use DisplayNameFor with a collection as argument
The question is
Given the above data model, and given that I don't want to change model nature to IEnumerable<Cat> for design reasons...
How can I bind the display name, which is an attribute of the Cat class, to the DisplayNameFor method or any equivalent when the expression involves a collection especially when this collection is empty? Otherwise asked, how can I fix the above compilation error? What's the correct syntax for DisplayNameFor in this case?
Found by myself by luck.
Basically Html.DisplayNameFor(Model => Model.Cats.FirstOrDefault().Name) works because even if there are no cats the runtime reflects the type Cat correctly
There are a couple of things you need to watch out in your code to prevent null exception.
First, you need to need to initialize you Cats collection so that when your code does not do so, for valid reason and it happens in reality, you are protected from having null exception. So you can write your model as such:
public class CatListViewModel: AbstractViewModel {
public CatListViewModel() {
Cats = new List<Cat>();
}
public IEnumerable<Cat> Cats;
}
So in your view if you do Model.Cats.FirstOrDefault() you won't get a null exception because Cats is null.
Secondly, now that you "can" have an empty collection (not null), because you are initializing it when an instance of your CatListViewModel is created, you still can run into null exception. That is because you are telling the compiler to give you a NULL if there is no item in the list - Model.Cats.FirstOrDefault(). So what you need to do is:
<th>
#if (Model.Cats.Any()) {
#Html.DisplayNameFor(model => model.Cats.First().Name)
}
</th>

Mvc rules and concepts

A view can have only one #model Folder.ModelName but what if I want to show data from two different tables in one view? How should I do this and what is the right way? For example, if I want to select all profits of the current user and to select all costs of the current user in the one view, then obviously this is 2 tables with 2 different models. Basically my question is, where can I find some rules and concepts for the asp.net mvc pattern?
You would create something known as ViewModel. ViewModel can contain one or more entities, methods, additional fields, etc. You then pass this ViewModel to your View. For example,
namespace Sample
{
public class ProfitCostViewModel
{
public Profit Profit { get; set; }
public Cost Cost { get; set; }
public decimal DoSomeCalculations()
{
// Do something
}
}
}
In your Action, create an instance of this ViewModel class and initialize properties Profit and Cost. Then pass this object to the View. Inside of your view your can declare model like:
#model Sample.ProfitCostViewModel
and use it as
<p>Current profit or something: #Model.Profit.SomeProfitProperty</p>
<p>Sample cost: #Model.Profit.SomeCostProperty</p>
And that's how you would pass two or more entities as a model to your view.
UPDATE: You action could be something like:
public ActionResult YourAction()
{
var profitCostVm = new ProfitCostViewModel();
profitCostVm.Profit = LoadProfitFromSomewhere();
profitCostCm.Cost = LoadCostFromSomewhere();
return View(profitCostCm);
}