I am building a small Asp.NET MVC web application for document management system where I need to keep track of all the changes that happened with files and folders in jsTree (jQuery implementation of a tree control).
I have found Mvc.GRID Bootstrap control that enables (very easily) a grid representation of data. Let me describe what I did:
This is a model that partial view uses - List<List<HistoryVM>> and HistoryVM looks like this:
public class HistoryViewModel
{
[Display(Name="Name")]
string ItemName { get; set; }
[Display(Name="Original Path")]
public string ItemPath { get; set; }
[Display(Name="Type")]
public string ItemType { get; set; }
[Display(Name="Author")]
public string EventAuthorName { get; set; }
[Display(Name="Comment")]
public string EventActionDesc { get; set; }
[Display(Name="Timestamp")]
public DateTime DateOfEvent { get; set; }
[Display(Name="Action Type")]
public string ActionType { get; set; }
}
Controller looks like this:
[HttpGet]
public ActionResult ShowHistory(string path)
{
OperationService operations = new OperationService();
var historyRecord = operations.FindItemHistory(path);
if (historyRecord != null & historyRecord.Count() > 0)
{
return PartialView("../Shared/Partial/ShowHistory", historyRecord);
}
else
{
return PartialView("../Shared/Partial/ShowHistoryNoHistory");
}
}
And finally this is the view:
#model List<List<LaneWebApp.Models.HistoryViewModel>>
#using GridMvc.Html
#Html.AntiForgeryToken()
<div>
#Html.Grid(Model).Columns(c => {
c.Add(x => x.ElementAt(0).ItemType);
c.Add(x => x.ElementAt(0).ItemName);
c.Add(x => x.ElementAt(0).ItemPath);
c.Add(x => x.ElementAt(0).EventAuthorName);
c.Add(x => x.ElementAt(0).DateOfEvent.ToString());
c.Add(x => x.ElementAt(0).EventActionDesc);
})
</div>
Which gives me only the first element of each list. But I would like to have all history records, not just the first one.
How can I render this List of List in Grid.MVC?
It looks like current version of Bootstrap Grid.MVC does not support complex models - take a look at the official site.
So, I had to come up with my own view and sorting.
Related
EDIT:
Changed a couple of classes compared to what they are below to match the Microsoft guide on Relationships.
Devices.cs
public partial class Devices
{
public int DeviceType { get; set; }
...
public DeviceTypes DeviceTypes { get; set; }
}
DeviceTypes.cs
public partial class DeviceTypes
{
public int DeviceType { get; set; }
public string Name { get; set; }
public Devices Devices { get; set; }
}
I then changed my view to start reading off of DeviceTypes.Name.
Index.cshtml
#Html.DisplayFor(modelItem => item.DeviceTypes.Name)
dbContext.cs
modelBuilder.Entity<Devices>(entity =>
{
entity.HasKey(e => e.DeviceId);
...
entity.Property(e => e.DeviceType).HasComment("ID of the type of devices. Primary key in DeviceTypes table.");
...
entity
.HasOne(p => p.DeviceTypes)
.WithOne(p => p.Devices);
});
....
modelBuilder.Entity<DeviceTypes>(entity =>
{
entity.HasKey(e => e.DeviceType);
entity.HasComment("The types of devices.");
entity.Property(e => e.DeviceType)
.HasComment("Id of the device.")
.ValueGeneratedNever();
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50)
.HasComment("Name of the device.");
entity
.HasOne(b => b.Devices)
.WithOne(i => i.DeviceTypes)
.HasForeignKey("DeviceType");
});
I also added to the model builder, but got stuck again not understanding. I've spent a few more hours trying but I can't figure it out. The current error I get is:
InvalidOperationException: You are configuring a relationship between 'DeviceTypes' and 'Devices' but have specified a foreign key on 'DeviceType'. The foreign key must be defined on a type that is part of the relationship.
This is a question about using related tables in my CRUD editors in .NET Core 3.1. I want to use the Name instead of the ID as a display on the index/details/delete pages. And on the create/edit pages I want to use a dropdown to select the value from a list.
There are quite a few examples I have found, but nothing I have tried yet has worked. I had too many different ideas mixed in my code before so I tossed all changes and I am trying again, but got stuck.
I am starting simple with adding my model again and trying to simply get it to display in my paginated list. Currently, the paged list displays no value at all rather than a name or ID. Obviously I am trying to get the Name to display. Then I will move toward something more complicated like getting this value in a dropdown on the create page.
I know I am not figuring out the part about linking the data from the database back into my view model, but I have spent so many hours on this today and haven't quite found the missing piece. I have had so many variations on this that didn't work and this was trying to start by keeping it as simple as I could and then ask you all for advice.
Here is the code I am working with:
Devices.cs - DeviceType is really the ID
public partial class Devices
{
[MaxLength(10)]
public string DeviceId { get; set; }
public int DeviceType { get; set; }
...
[NotMapped]
public DeviceTypes DeviceTypes { get; set; }
}
DeviceTypes.cs - This is what I want to look up by DeviceType (int) and use the name in the editors
public partial class DeviceTypes
{
public int DeviceType { get; set; }
public string Name { get; set; }
}
DevicesViewModel.cs
public class DevicesViewModel
{
public Devices Devices { get; set; }
public PaginatedList<Devices> DevicesPagedList { get; set; }
}
DevicesController.cs - This is in the Index method where I get the data and then later what I am passing into my view
var devices = from dev in _context.Devices
select dev;
...
return base.View(new DevicesViewModel { DevicesPagedList = await PaginatedList<Devices>.CreateAsync(devices.AsNoTracking(), currentPage, resultsPerPage ?? 10), ResultsPerPageList = SelectListItemHelper.ResultsPerPageList().Where(l => l.Text != (resultsPerPage ?? 10).ToString()) });
}
Finally, here is where I am trying to display the value in Index.cshtml
#model DevicesViewModel
...
#foreach (var item in Model.DevicesPagedList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DeviceTypes.Name)
</td>
...
i have two class as below
Model:-
public class RegisterViewModel
{
public string Email { get; set; }
public AddressPropertyVM AddressProperty { get; set; }
}
public class AddressPropertyVM
{
public string StreetNo { get; set; }
}
Main Form
#model Application.Models.RegisterViewModel
{
#Html.TextBoxFor(m => m.Email)
#Html.TextBoxFor(m => m.FirstName)
#Html.Partial("_AddressPropertyPartial",Model.AddressProperty)
<button type="submit">Register</button>
}
Partial View Form
#model Application.Models.AddressPropertyVM
{
#Html.TextBoxFor(m => m.StreetNo)
}
I am creating asp.net mvc application.
I have create a partial view for AddressPropertyVM.
but we i post form(main form) at that time data of AddressProperty is null.
As per my understanding you want to use one partial view with more than one pages I mean multiple usage of one page in many pages.
Read this article and understand how it works.
Change your code as per article like below.
#Html.Partial("_AddressPropertyPartial",Model.AddressProperty)
To
#Html.Partial("_AddressPropertyPartial", Model.AddressProperty, new ViewDataDictionary() { TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = "AddressProperty" } }
I have a MVC4 application and although I have get parameters for my DropDownList from the database, I encounter some kind of problems while posting the DropDownList value to the database. There is lots of samples for different approach, but I would like to apply a method without using an extra approach i.e. Ajax, Javascript, etc. On the other hand, I have run into "FormCollection" to pass data, but I am not sure if FormCollection is the best way in this scene. Here are some part of the view, controller and model I use:
View:
#using (Html.BeginForm("Add", "Product", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
<p>Product Type : #Html.DropDownListFor(m => m.SelectedLookupId, new SelectList(Model.Lookups.Where(x => x.LookupType == "Product Type"), "LookupID", "LookupValue"), "--- Select ---") </p>
Controller:
[HttpPost]
public ActionResult Add(Product product)
{
if (ModelState.IsValid)
{
product.ProductType = // ??? Cannot get the SelectedLookupId
...
repository.SaveProduct (product);
TempData["message"] = string.Format("{0} has been saved", product.Name);
return View("Completed");
}
else
{
//there is something wrong with the data values
return View(product);
}
}
ViewModel:
public class ProductViewModel
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Lookup> Lookups { get; set; } //Lookup for Product Types
public int SelectedLookupId { get; set; }
public Product Product { get; set; }
}
Thanks in advance for your helps.
Your action method should be receiving the view model, not the Product itself, like so:
[HttpPost]
public ActionResult Add(ProductViewModel productViewModel)
Unless I'm confused. But I assume the view snippet you posted above is from the Add view and that view's model is of type ProductViewModel. In your action method you are returning the Add view when the model state is invalid however you are passing a Product to that view. Again I may be confused because this should give you a runtime error that the types don't match.
Thanks for reply. Actually by using ViewModel rather than View, I have managed to solve the problem. On the other hand, after some research, I have applied another effective method in order to populate Dropdownlist without needing ViewModel. Furthermore with this example, I could use multiple foreign keys on the same Lookup table as shown below. Here is an an Applicant entity having 3 foreign keys and Lookup entity related to these keys. What I wanted to achieve with this example is exactly to use a Lookup table for only several Dropdownlist parameters i.e. Gender, Yes/No, Status,... due to no needing to create a table for the several parameters (these parameters are distinguished LookupType property on Lookup table). Here is the full example (I have shorted unrelated properties for brevity) below:
Applicant Entity:
public class Applicant
{
[Key]
public int ApplicantID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public int? HasDoneAnyProject { get; set; }
public int? IsInterestedAnyProgramme { get; set; }
public int? InterestedProgrammeId { get; set; }
public virtual Lookup PrimaryLookup { get; set; }
public virtual Lookup SecondaryLookup { get; set; }
public virtual Lookup TertiaryLookup { get; set; }
}
Lookup Entity:
public class Lookup
{
[Key]
public int LookupID { get; set; }
public string LookupType { get; set; }
public string LookupValue { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public virtual ICollection<Applicant> PrimaryLookupFor { get; set; }
public virtual ICollection<Applicant> SecondaryLookupFor { get; set; }
public virtual ICollection<Applicant> TertiaryLookupFor { get; set; }
}
DbContext:
public class EFDbContext : DbContext
{
public DbSet<Applicant> Applicants { get; set; }
public DbSet<Lookup> Lookups { get; set; }
//for using "Multiple foreign keys within same table using Fluent API"
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.PrimaryLookup)
.WithMany(a => a.PrimaryLookupFor)
.HasForeignKey(b => b.HasDoneAnyProject)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.SecondaryLookup)
.WithMany(a => a.SecondaryLookupFor)
.HasForeignKey(b => b.IsInterestedAnyProgramme)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.TertiaryLookup)
.WithMany(a => a.TertiaryLookupFor)
.HasForeignKey(b => b.InterestedProgrammeId)
.WillCascadeOnDelete(false);
}
}
Controller:
private void PopulateLookupsDropDownList(string lookupType, string foreignKey, object selectedLookups = null)
{
var lookupsQuery = repository.Lookups
.Select(x => x)
.Where(x => x.LookupType == lookupType)
.OrderBy(x => x.ParentLookupID).ToList();
ViewData[foreignKey] = new SelectList(lookupsQuery, "LookupID", "LookupValue", selectedLookups);
}
and for calling the Method for each of three Dropdownlist:
PopulateLookupsDropDownList("YesNo", "HasDoneAnyProject", applicant.HasDoneAnyProject);
PopulateLookupsDropDownList("YesNo", "IsInterestedAnyProgramme", applicant.IsInterestedAnyProgramme);
PopulateLookupsDropDownList("Programme", "InterestedProgrammeId", applicant.InterestedProgrammeId);
View: : Populating each of three Dropdownlist from the same Lookup table with different LookupType parameter:
<label>Has done any project before?</label>
#Html.DropDownList("HasDoneAnyProject", "---- Select ----")
<label>Are you interested in any programme?</label>
#Html.DropDownList("IsInterestedAnyProgramme", "---- Select ----")
<label>Interested programme name?</label>
#Html.DropDownList("InterestedProgrammeId", "---- Select ----")
I hope this approach will be useful for those who want to populate Dropdownlists from the same Lookup table. On the other hand, it is not only suitable for this, also can be used for populating Dropdownlists from different tables.
Regards.
I want to make a task system where the content data is able to expand dynamically (With different kinds of inputs (Text, Images, Dropdowns etc.). My plan is to create the following model, and with the help from some custom HtmlHelpers generate my view.
Model:
public class Task
{
public int TaskId { get; set; }
public string Name { get; set; }
public virtual List<TaskData> TaskData { get; set; }
}
public enum TaskDataType
{
TextType1, TextType2, E-mail, Image
}
public class TaskData
{
public int TaskDataId { get; set; }
public int TaskId { get; set; }
public TaskDataType Type { get; set; }
public string Name { get; set; }
[Required]
public string Data { get; set; }
public bool Required { get; set; }
public virtual Task Task { get; set; }
}
View:
Validation for the Task, works out of the box with the default HtmlHelpers, but when i try to make something like:
#if (Model.TaskData != null)
{
foreach (var taskData in Model.TaskData)
{
#Html.DisplayTextFor(td => taskData.Name)
#Html.TextBoxFor(td => taskData.Data)
#Html.ValidationMessageFor(td => taskData.Data)
}
}
In my View, the validation starts to go wrong, client site validation only validates the first input in the forech loop, and the ModelState in the controller does not contain any data regarding the TaskData fields.
So I have 2 quetions:
Is this the right path, or do I need to think of this in a different way?
If this is valid method of making what i want, is there a way to make the build in validation work?
You have to use for loop in order to post field values as well as get the in build validation work for you.
#if (Model.TaskData != null)
{
for(int i=0; i< Model.TaskData.Count; i++)
{
#Html.DisplayTextFor(model => model.TaskData[i].Name)
#Html.TextBoxFor(model => model.TaskData[i].Data)
#Html.ValidationMessageFor(model => model.TaskData[i].Data)
}
}
I am trying to find the best way to use MVC for models which are only partially edited.
Below is an simple example.
Model
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
View
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
Controller
using System.Web.Mvc;
public class SimpleController : Controller
{
public ActionResult Edit(int id)
{ return View(Get(id)); }
[HttpPost]
public ActionResult Edit(int id, SimpleModel model)
{
if (model.Name.StartsWith("Child")) //Some test that is not done client-side.
{
Save(model);
//Get the saved data freshly.
//model = Get(id);
}
else
{
ModelState.AddModelError("", "Name should start with 'Child'");
}
//Is this the way to set the Parent property?
//var savedModel = Get(id);
//model.Parent = savedModel.Parent;
return View(model);
}
//Mock a database.
SimpleModel savedModel;
private void Save(SimpleModel model)
{ savedModel = new SimpleModel() { Id = model.Id, Name = model.Name }; }
private SimpleModel Get(int id)
{
if (savedModel == null)
{ return new SimpleModel() { Id = id, Parent = "Father", Name = "Child " + id.ToString() }; }
else
{ return new SimpleModel() { Id = savedModel.Id, Parent = "Father", Name = savedModel.Name }; }
}
}
The Name field is editable. The Parent field is only for reference and should not be updated. Therefore, it is rendered using DisplayFor.
Upon post, I receive a model with property Parent set to null. That's no problem as it will not be saved. However, when I simply return the received model to the view, the Parent field will no longer be displayed. When the model is valid, I can easily get it again from the database and thus get the Parent field's value back.
When the model is not valid, I would like to allow the user to correct input and attempt to save once more. There, I the received model's values that are input should be used, but the displayed values should be shown as well.
In reality, there are many more fields to be shown for reference, most often from different database entities than the one that is being edited.
I have seen suggestions to pass the fields as hidden fields in the view, but I feel very reluctant to read data from the client that should not be updated.
Is there a more elegant way to do this than copying these values into the model manually or passing them as hidden fields?
What about giving those un-editable properties to another model and let it take care of those properties?
View Model
public class PersonViewModel
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public PersonDetailsModel DetailsModel { get; set; }
}
Details Model
public class PersonDetailsModel
{
public string Mother { get; set; }
public string Father { get; set; }
public PersonDetailsModel() { }
public PersonDetailsModel(int personId)
{
// pull required model data from databases
var db = DBParentContext;
Mother = db.Parent.Where(m => m.ChildId == personId)
Father = db.Parent.Where(m => m.ChildId == personId)
}
}
Controller
public class PersonController : Controller
{
[HttpPost]
public ActionResult Edit(PersonViewModel viewModel)
{
viewModel.DetailsModel = new PersonDetailsModel(viewModel.Id)
if (ModelState.IsValid) {
// ~
}
return View(viewModel)
}
}
View
#model PersonViewModel
#Html.DisplayFor(m => m.DetailsModel.Mother)
#Html.DisplayFor(m => m.DetailsModel.Father)
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
Since details like your Mother are un-editable then they're not really part of the "Edit" model, so I'd box like that away and try to let something else take care of them.
If you aren't going to update the Parent field, then it really doesn't matter if it's a hidden or not, since you won't update it on post.
I would use the hidden in this case, just make sure not to update that field.