I want to make dynamic view that show list of entity properties.
I create these models
public class PersonModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class EmployeeModel : PersonModel
{
public string CompanyName { get; set; }
}
public class StudentModel : PersonModel
{
public string SchoolName { get; set; }
}
I want one view that show list, the view dynamically generated
for examples columns and data appear in list.
Example when open employee I will show following:
and when Open student I will show following:
what easiest way to make my view dynamical and contain the columns and data I want?
I hope this makes as much sense as I think it does!
As List<PersonModel>, List<EmployeeModel> and List<StudentModel> are actually considered completely different, you will need a way to overcome that issue. I use a generic container class:
public interface IGenericContainer
{
dynamic Data { get; }
}
public class GenericContainer<T> : IGenericContainer
{
T _Data { get; set; }
public GenericContainer(T data)
{
_Data = data;
}
dynamic IGenericContainer.Data
{
get { return _Data; }
}
}
public class GenericContainer
{
public static GenericContainer<T> Create<T>(T data)
{
return new GenericContainer<T>(data);
}
}
You then need a generic view that uses this. Put this in Shared/DisplayTemplates/GenericGrid.cshtml
#using System.Reflection;
#using System.Text;
#{
Layout = null;
}
#model IGenericContainer
#{
IEnumerable<PropertyInfo> properties = null;
if (Model.Data.Count > 0)
{
properties = Model.Data[0].GetType().GetProperties();
}
}
<div>
#if (properties != null)
{
<table>
<thead>
<tr>
#foreach (var prop in properties)
{
<td>#prop.Name</td>
}
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Data.Count; i++)
{
<tr>
#foreach (var prop in properties)
{
<td>#prop.GetValue(Model.Data[i])</td>
}
</tr>
}
</tbody>
</table>
}
</div>
To use this you will need to add this to your view:
#Html.DisplayFor(m => GenericContainer.Create(Model.PersonList), "GenericGrid")
And PersonList is a property in your model of type List<PersonModel> or a list of any of your models.
I'm not really sure if I have understood your requirements properly, but if you wanted to display each property of your model dynamically as a column header, then you could try the following:
In your view, you can call the GetProperties method on the type and recursively add a column for each property:
#model PersonModel
#if (Model != null)
{
<table style="width:100%">
<tr>
#foreach (string property in Model.GetType().GetProperties().Select(x => x.Name).ToList())
{
<td>#property</td>
}
</tr>
</table>
}
You can use this example to populate the header columns of a table before you populate the rows. To populate the rows you would need a list of PersonModel and do a foreach on this similar to what I have shown you for the column headers.
Hope that helps.
Related
I have the this set up and am trying to get lkup_1_txt to show in the page as either text or the selected item in the dropdown.
I have tried the following
#lkup1
and
#Html.DisplayFor(modelItem => item.lkup1)
and
#foreach (var item in Model)
{#Html.DisplayFor(modelItem => item.lkup1)}
This is my structure -
Model for Look Up Reference drop down selection
public class ListItem
{
public string Value { get; set; }
public string Text { get; set; }
}
public class ModelForDropDown
{
public string SelectedItemText { get; set; }
public List<ListItem> ItemListText { get; set; }
}
public class LkupResultRecord
{
public ModelForDropDown lkup_1_txt { get; set; }
public ModelForDropDown lkup_2_txt { get; set; }
}
Controller for the create new lkup1txt dropdown -
public ActionResult LkupRef_Lkup1()
{
return View(lkuprefdao.getValuesForLkupTxtDropDown());
}
[HttpPost]
public ActionResult LkupRef_Lkup1(string lkup_1_txt)
{ return RedirectToAction("LkupRef_Lkup2", "LkupRef", new
{
lkup_1_txt = lkup_1_txt
});
}
Controller for the create new lkup2txt dropdown -
public ActionResult LkupRef_Lkup2()
{
if (lkuprefdao.IsDataRefreshDowntime() == true) { return RedirectToAction("BadgerWeb_RestrictedAccess", "LkupRef"); }
return View(lkuprefdao.getValuesForLkupTxtDropDown());
}
and the view where I want the lkup_1_txt to display -
#using (Html.BeginForm())
{
<table>
<tr>
<td>lkup_1_txt</td>
<td> #Html.DisplayFor(lkup_1_txt) </td>
</tr>
<tr>
<td> lkup_2_txt </td>
<td>#Html.DropDownList("lkup_2_txt", new SelectList(Model.lkup_2_txt.ItemListText, "Value", "Text"), null, new { style = "width: 650px;" })</td>
</tr>
</table>
}
I
It's all still a bit unclear and doesn't make a great deal of sense, but I think you probably want something like this:
1) define lkup_1_txt as a string in your model, to match with the querystring value you're trying to feed to it. Defining it as a complex type like ModelForDropDown (as you did) doesn't appear to make any sense.
public class LkupResultRecord
{
public string lkup_1_txt { get; set; }
public ModelForDropDown lkup_2_txt { get; set; }
}
2) Add a parameter to allow the LkupRef_Lkup2() action method to receive the input value from the redirect (or from a direct request). Right now it is just ignoring the input. And also modify the action method code so you can add the incoming lookup text to the model before passing it to the view.
public ActionResult LkupRef_Lkup2(string lkup_1_txt)
{
if (lkuprefdao.IsDataRefreshDowntime() == true) { return RedirectToAction("BadgerWeb_RestrictedAccess", "LkupRef"); }
LkupResultRecord model = lkuprefdao.getValuesForLkupTxtDropDown();
model.lkup_1_txt = lkup_1_txt; //add the lookup text to the model
return View(model);
}
3) Modify the view so it can read the lookup text from the model property:
<td>lkup_1_txt</td>
<td> #Html.DisplayFor(m => m.lkup_1_txt) </td>
My Model Class
public class DisplaceModel
{
public string Name { get; set; }
public string Address { get; set; }
public string Type { get; set; }
public string Rating { get; set; }
public string PhotoReference { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public class DisplaceModelInformation
{
public List<DisplaceModel> Dispaylist { get; set; }
public DisplaceModelInformation()
{
Dispaylist = new List<DisplaceModel>();
}
}
And My Controller
var Display = new DisplaceModelInformation();
XElement generalElement = xdoc1.Element("PlaceSearchResponse");
Display.Dispaylist = (from c in xdoc1.Descendants("result")
select new DisplaceModel()
{
Name = Convert.ToString(c.Element("name").Value),
Address = Convert.ToString(c.Element("vicinity").Value),
Type = keyword,
Rating = (c.Element("rating") != null ? Convert.ToString(c.Element("rating").Value) :null),
PhotoReference = (c.Element("photo") != null ? Convert.ToString(c.Element("photo").Element("photo_reference").Value) : null),
Width = (c.Element("photo") != null ? Convert.ToInt16(c.Element("photo").Element("width").Value) : 0),
Height = (c.Element("photo") != null ? Convert.ToInt16(c.Element("photo").Element("height").Value) : 0)
}).ToList<DisplaceModel>();
return View(Display);
Now I try to display my list of object to view, I tried but I really dont know how to display in mvc view(cshtml) file
My MVC View
#model IEnumerable<FindLocation.Models.DisplaceModelInformation>
#using (Html.BeginForm())
{
<table>
<tbody>
<tr>
<th>NAME</th>
<th>ADDRESS</th>
<th>RATING</th>
</tr>
#foreach (var element in Model)
{
if (element.Dispaylist.Count > 0 )
{
<tr>
<#*td>#element.Dispaylist from c </td>
<td>#Display.Address</td>
<td>#Display.Rating</td>*#
</tr>
}
}
</tbody>
</table>
I am new to UI and I try to extract the list of object but i dont know how to list the object. so please help me....and thanks for your help
Part of the trouble might be coming from the type difference between what you're passing to the view from the controller (DisplaceModelInformation) and the #model definition in the view itself (IEnumerable<DisplaceModelInformation>). Changing one to match the other should help.
I think you have the right idea with #foreach. If your model is an IEnumerable, you would need to iterate over that collection first, then iterate over the DisplayList collection in another inner loop.
The answers to the following question seem to have some good examples of what you're trying to accomplish:
Foreach in a Foreach in MVC View
There are two issues in your code:
In your Controller, you need to return View(new List<DisplaceModelInformation> { Display });, because you specified an IEnumerable model in your View: #model IEnumerable<FindLocation.Models.DisplaceModelInformation>
Use foreach to display each table row:
#foreach (var element in Model)
{
if (element.Dispaylist.Count > 0)
{
foreach (var Display in element.Dispaylist)
{
<tr>
<td>#Display.Name</td>
<td>#Display.Address</td>
<td>#Display.Rating</td>
</tr>
}
}
}
I am have a table similar to grid that shows all the fields from table.
This is my controller:
public ActionResult Index()
{
DAL.DataManager dal = new DAL.DataManager();
List<LALegalLicensedata> data = new List<LALegalLicensedata>();
data = dal.get_LA_Get_AllDate();
return View(data);
}
and this is my view:
#model IEnumerable<CSAProject.Models.LALegalLicensedata>
<table width="100%" class="display" id="example" cellspacing="0">
<thead>
<tr>
<th>Entity</th>
<th>License Type</th>
<th>State</th>
<th>Location</th>
<th>Data User</th>
</tr>
</thead>
<tbody>
#foreach(var item in Model)
{
<tr>
<td>#item.Entity</td>
<td>#item.License_Type</td>
<td>#item.State</td>
<td>#item.Location</td>
<td>#item.dataUser</td>
</tr>
}
</tbody>
</table>
also in this page I need to show a optionList with checkbox that contains name of the properties from Model, this is my model:
public class LALegalLicensedata
{
public int dataID { get; set; }
public string dataUser { get; set; }
public DateTime Create_Date { get; set; }
public DateTime Modified_Date { get; set; }
public string Modified_By { get; set; }
public string Status { get; set; }
}
and this is how I get the properties name from Model:
LALegalLicensedata model = new LALegalLicensedata();
List<string> PropertyList = GetPropertiesNameOfClass(model);
public List<string> GetPropertiesNameOfClass(object pObject)
{
List<string> propertyList = new List<string>();
if (pObject != null)
{
foreach (var prop in pObject.GetType().GetProperties())
{
propertyList.Add(prop.Name);
}
}
return propertyList;
}
I need to show a PropertyList in the option list how I can do that?
This is the javascript and text to show and hide the column. Instead of static text I like to have names from properties and have them in the option list with checkbox.
$(document).ready(function () {
var table = $('#example').DataTable({
"paging": true
});
$('a.toggle-vis').on('click', function (e) {
//e.preventdefault();
event.preventDefault ? event.preventDefault() : event.returnValue = false;
//Get the column API object
var column = table.column($(this).attr('data-column'));
// Toggle the visibility
column.visible(!column.visible());
});
});
</script>
<div>
Toggle column: <a class="toggle-vis" data-column="0">Entity</a> -
<a class="toggle-vis" data-column="1">License Type</a> -
<a class="toggle-vis" data-column="2">State</a> -
<a class="toggle-vis" data-column="3">Location</a> -
<a class="toggle-vis" data-column="4">Data User</a> -
<a class="toggle-vis" data-column="5">Create Date</a>
</div>
The model you have shown does not match the view you have shown, so assuming your model is in fact (to match the view you have shown)
public class LALegalLicensedata
{
public string Entity { get; set; }
public string License_Type { get; set; }
public string State { get; set; }
public string Location { get; set; }
public string dataUser { get; set; }
}
Then, in the Index() method, add the property names to a ViewBag property
....
ViewBag.PropertyNames = GetPropertiesNameOfClass(new LALegalLicensedata());
return View(data);
I would however recommend that you use a view model with properties List<LALegalLicensedata> Data and List<string> PropertyNames
Then in the view your can loop through the collection to generate you checkboxes
<div>
#foreach(var name in ViewBag.PropertyNames)
{
<label>
<input type="checkbox" class="toggle-column" checked />
<span>#name</span>
</label>
}
</div>
Then modify your script to handle the click() event of each checkbox
var checkBoxes = $('.toggle-column');
$('.toggle-column').click(function() {
var index = checkBoxes.index($(this));
$('tr th').eq(index).toggle();
$('tr td').eq(index).toggle();
});
Refer this fiddle for how the script works.
Edit
Your current GetPropertiesNameOfClass() method will return the property names which in your case will display "License_Type" for the 2nd property, when I suspect you probably want it to be "License Type" (space instead of underscore). To solve this, add a [Display(Name = "License Type")] to the property, and then you can use the following method
private List<string> GetDisplayNames(object model)
{
Type type = typeof(model);
List<string> displayNames = new List<string>();
foreach (var property in type.GetProperties())
{
var attributes = property.GetCustomAttributes(typeof(DisplayAttribute), true);
if (attributes.Length == 0)
{
displayNames.Add(property.Name);
}
else
{
displayNames.Add((attributes[0] as DisplayAttribute).Name);
}
}
return displayNames;
}
This also means you can use <th>#Html.DisplayNameFor(m => m.License_Type)</th> to generate you table headings rather than hard coding.
In my view model, there is a list of element which contains a table of text inputs.
View model > List of element > Table > lots of text inputs
All of this is generated in the view via EditorTemplates.
Everything is bound perfectly to the view when it load (MVC is creating the right IDs and Names of each input. Now I want to add and delete rows and have the binding updated. This means, that all the right numbers are updated in the IDs and Name.
I have an idea of how to do it with pure JavaScript, but that would be very dirty and a pain to update when things change. I would like to do it with an HTML helpers or a model binder but haven't found a way yet!
Anyone have an idea of how to do that?
EDIT : apparently I have more chance to get answered if I post lots of code detail... but in this complex case, that's going to be long, so hang on!
Here is the view model. (Keep in mind that this code is a dumbed down version)
public class MVCViewModel
{
public List<ActivityElement> ActivityElementList { get; set; }
}
public abstract class ActivityElement
{
}
public class Step : ActivityElement
{
public Step()
{
this.Id = Guid.NewGuid();
this.Label = "Basic Step";
}
public Step(string label, AbstractInput input)
: this()
{
this.Label = label;
this.Input = input;
}
public Guid Id { get; set; }
public string Label { get; set; }
public AbstractInput Input { get; set; }
}
public abstract class AbstractInput
{
public object Value { get; set; }
}
public class GridInput : AbstractInput
{
public List<GridCell> Headerlist { get; set; }
public List<GridRow> RowList { get; set; }
}
public class GridRow
{
public List<GridCell> CellList { get; set; }
}
public class GridCell
{
public string ColumnName { get; set; }
public AbstractInput Input { get; set; }
}
public class TextInput: AbstractInput
{
public TextInput(string value)
{
this.Value = value;
}
}
Alright! If somehow you are still with me, that's good! I have put them in the order that they will be used in this particular case.
To dumb this down, the view model contains a list of Step. The first step contains a GridInput as his AbstractInput property. That grid contains rows and each rows contains cells. For this case, let say each cell contains a TextInput as his AbstractInput.
Now, on the view, when this is displayed with EditorTemplates and HtmlHelpers, all the IDs and Names of the inputs are good (therefor the binding is good), as shown here in this HTML output :
<td>
<input id="ActivityElementList_0__Input_RowList_0__CellList_0__Input_ModelType" name="ActivityElementList[0].Input.RowList[0].CellList[0].Input.ModelType" type="hidden" value="knockout_binding_prototype.Models.TextInput">
<input id="ActivityElementList_0__Input_RowList_0__CellList_0__Input_Value" name="ActivityElementList[0].Input.RowList[0].CellList[0].Input.Value" type="text" value="cell 1 row 1">
</td>
<td>
<input id="ActivityElementList_0__Input_RowList_0__CellList_1__Input_ModelType" name="ActivityElementList[0].Input.RowList[0].CellList[1].Input.ModelType" type="hidden" value="knockout_binding_prototype.Models.TextInput">
<input id="ActivityElementList_0__Input_RowList_0__CellList_1__Input_Value" name="ActivityElementList[0].Input.RowList[0].CellList[1].Input.Value" type="text" value="cell 2 row 1">
</td>
What I am trying to do here, is to be able to add and delete row to this table. This is fairly easy... The hard part is making sure all the binding (IDs and Names) are still good. In the case of this particular HTML output, if I delete the first row, I need the IDs and Names of the second row, to become equivalent to the first row IDs and Name.
I have been trying to find the most generic way to do it, and to try and make it JavaScript less as possible. All ideas will be pondered, so don't afraid to say something!
EDIT 2 : OK, as far as deleting rows go, I decided to just hide the delete row and flag it as deleted, so all the biding remains good. Adding row is simple in JavaScript, but I am searching for a way to do it without JavaScript (or only a little bit).
Here is the update in the view model for the delete flag.
public class GridRow
{
public GridRow()
{
IsDeleted = false;
}
public List<GridCell> CellList { get; set; }
public bool IsDeleted { get; set; }
}
Try this way without buggy JS html modifications. You can do delete this way as well. Also you can decide if you want to save the model on every add/delete call or save it after all changes.
Models:
public class SampleModel
{
public List<ItemModel> Items { get; set; }
public SampleModel()
{
Items = new List<ItemModel>();
}
}
public class ItemModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Controller:
[HttpPost]
public ActionResult Add(SampleModel model)
{
model.Items.Add(new ItemModel { Name = "New Item" });
return PartialView("Items");
}
public ActionResult Index()
{
var model = new SampleModel(); // Or get model
return View(model);
}
[HttpPost]
public ActionResult Index(SampleModel model)
{
// Save model to DB
return RedirectToAction("Index");
}
Index.cshtml
#model SampleModel
#using (Html.BeginForm())
{
<div id="container">
#Html.Partial("Items", Model)
</div>
<a id="add" href="javascript:void(0);">Add</a>
<button type="submit">Save</button>
}
<script src="jquery.js"></script>
<script type="text/javascript">
$(function () {
$('#add').on('click', function () {
$.post('#Url.Action("Add")', $('form').serializeModel(), function(html) {
$('#container').html(html);
});
});
});
$.fn.serializeModel = function () {
var model = {};
$(this).serializeArray().map(function (item) {
model[item.name] = item.value;
});
return model;
};
</script>
Items.cshtml:
#model SampleModel
<table>
#for (var i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(m => Model.Items[i].Id)
#Html.TextBoxFor(m => Model.Items[i].Name)
</td>
</tr>
}
</table>
I'm using mvc and I'm passing the class called DataModel as my model to the view:
public class DataModel
{
public List<ServerStats> statsPerMonthPerServer { get; set; }
}
And here is the class ServerStats:
public class ServerStats
{
public string ServerID { get; set; }
public List<ColumnData> ServerMonthStats { get; set; }
}
And here is the class ColumnData:
public class ColumnData
{
public string Date { get; set; }
public double Sum { get; set; }
}
I have managed to build my ModelData object, if I debug it, it looks like the information I want is in it, but I can not manage to print it out from the view. This is what I've tried to do:
View:
#model IEnumerable<WebApp.Models.DataModel>
#foreach (var server in Model)
{
foreach (var month in server.statsPerMonthPerServer)
{
#month.ServerID
foreach (var column in month.ServerMonthStats)
{
#column.Date <br />
#column.Sum <br />
}
}
}
And the controller where I build the DataModel and send it to the view:
DataModel dm = new DataModel();
dm.statsPerMonthPerServer = new List<ServerStats>();
foreach (var row in serverStats)
{
ServerStats ss = new ServerStats();
ss.ServerID = row.ServerID;
ss.ServerMonthStats = new List<ColumnData>();
ColumnData cd = new ColumnData();
cd.Date = row.Year.ToString() + "-" + row.Month.ToString();
cd.Sum = row.ServerSum;
ss.ServerMonthStats.Add(cd);
dm.statsPerMonthPerServer.Add(ss);
}
return View(dm);
The error I get:
The model item passed into the dictionary is of type 'WebApp.Models.DataModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[WebApp.Models.DataModel]'.
I'm using lists but it says this is a dictionary, but I assume it is talking about my lists. I've read some about the IEnumerable interface. Do I have to implement it on all the classes except DataModel? I thought the model decleration on the top of the view took care of this? Any help would be preciated!
The error message is pretty clear. Your view is strongly typed to IEnumerable<DataModel> but from your controller action you passed only a single DataModel instance to the view. I guess you meant this:
#model WebApp.Models.DataModel
#foreach (var month in Model.statsPerMonthPerServer)
{
<h3>#month.ServerID</h3>
#foreach (var column in month.ServerMonthStats)
{
#column.Date <br />
#column.Sum <br />
}
}