Dynamic Property and Child Model Not Binding - .net-core

I want to build dynamic form using Blazor.
Here is my sample component.
#page "/customform"
#using System.Dynamic
#using System.Text.Json
#inject IJSRuntime JSRuntime;
<div class="card m-3">
<h4 class="card-header">Blazor WebAssembly Form Validation Example</h4>
<div class="card-body">
<EditForm EditContext="#editContext"
OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator></DataAnnotationsValidator>
#foreach (var field in Model.Fields)
{
<div class="form-group">
<label>#field.Name</label>
<input #bind-value="field.Value" class="form-control" />
<ValidationMessage For="(()=> field.Value)" />
<ValidationMessage For="(()=> field.Name)" />
<ValidationMessage For="(()=> field)" />
</div>
}
<div class="form-group">
<label>Address</label>
<input #bind-value="Model.Address" class="form-control" />
<ValidationMessage For="()=> Model.Address" />
</div>
<div class="form-group">
<label>Child</label>
<input #bind-value="Model.ChildModel.ChildName" class="form-control" />
<ValidationMessage For="()=> Model.ChildModel.ChildName" />
</div>
<div class="text-left">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</EditForm>
</div>
</div>
#code{
private SampleModel Model = new SampleModel();
private EditContext editContext;
private ValidationMessageStore _messageStore;
protected override void OnInitialized()
{
editContext = new EditContext(Model);
editContext.OnValidationRequested += ValidationRequested;
_messageStore = new ValidationMessageStore(editContext);
}
private void HandleValidSubmit(EditContext context)
{
var modelJson = JsonSerializer.Serialize(context.Model, new JsonSerializerOptions { WriteIndented = true });
JSRuntime.InvokeVoidAsync("alert", $"SUCCESS!! :-)\n\n{modelJson}");
}
async void ValidationRequested(object sender, ValidationRequestedEventArgs args)
{
_messageStore.Add(editContext.Field("FirstName"), "Test");
_messageStore.Add(editContext.Field("Address"), "Invalid Address");
_messageStore.Add(editContext.Field("ChildModel.ChildName"), "Invalid Child Name");
editContext.NotifyValidationStateChanged();
}
public class SampleModel
{
public string Address { get; set; }
public ChildModel ChildModel { get; set; }
public List<Field> Fields { get; set; }
public SampleModel()
{
this.ChildModel = new ChildModel();
this.Fields = new List<Field>();
this.Fields.Add(new Field()
{
Name = "FirstName",
Value = "",
ControlType = ControlType.Input
});
this.Fields.Add(new Field()
{
Name = "LastName",
Value = "",
ControlType = ControlType.Input
});
}
}
public class ChildModel
{
public string ChildName { get; set; }
}
public enum ControlType
{
Input
}
public class Field
{
public string Value { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public ControlType ControlType { get; set; }
}
}
Currently I am facing too many issues.
If I use For lookup instead of For each it is not working
ChildModel seems to be bind but its validation is not working
Dynamically generated based on Fields collection control does not display validation.
Only address in SimpleModel display validation.
Is there any suggestion or help around this ?

Your profile suggests you know what you're doing, so I'll keep this succinct.
Your for loop needs to look something like this. Set a local "index" variable within the loop to link the controls to. If you don't they point to the last value of i - in this case 2 which is out of range! The razor code is converted to a cs file by the razor builder. You can see the c# file generated in the obj folder structure - obj\Debug\net5.0\Razor\Pages. Note, the linkage of the Validation Message
#for(var i = 0; i < Model.Fields.Count; i++)
{
var index = i;
<div class="form-group">
<label>#Model.Fields[index].Name</label>
<input #bind-value="Model.Fields[index].Value" class="form-control" />
<ValidationMessage For="(()=> Model.Fields[index].Value)" />
</div>
}
Now the message validation store. Here's my rewritten ValidationRequested. Note I'm creating a FieldIdentifier which is the correct way to do it. "Address" works because it's a property of EditContext.Model. If a ValidationMessage doesn't display the message you anticipate, then it's either not being generated, or it's FieldIdentifier doesn't match the field the ValidationMessage is For. This should get you going in whatever project you're involved in - if not add a comment for clarification :-).
void ValidationRequested(object sender, ValidationRequestedEventArgs args)
{
_messageStore.Clear();
_messageStore.Add(new FieldIdentifier(Model.Fields[0], "Value"), "FirstName Validation Message");
_messageStore.Add(new FieldIdentifier(Model.Fields[1], "Value"), "Surname Validation Message");
_messageStore.Add(editContext.Field("FirstName"), "Test");
_messageStore.Add(editContext.Field("Address"), "Invalid Address");
_messageStore.Add(editContext.Field("ChildModel.ChildName"), "Invalid Child Name");
editContext.NotifyValidationStateChanged();
}
If you interested in Validation and want something more that the basic out-of-the-box validation, there's a couple of my articles that might give you info Validation Form State Control or there's a version of Fluent Validation by Chris Sainty out there if you search.

Related

Custom validation attribute does not show error message

tring to compare two date input in asp.net core, i dont get any error message.
other built-in validators are working. also tried to create custom validator too.
(using tuple model. and named it as 'e')
my Model:
public DateTime? SchoolStart { get; set; }
public DateTime? SchoolEnd { get; set; }
Model Validator (IValidatableObject)
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (SchoolEnd.Value <= SchoolStart.Value)
{
yield return new ValidationResult("End date must be greater than the start date.", new[] { nameof(SchoolEnd) });
}
}
my view
<input asp-for="e.SchoolStart" type="date" class="form-control" data-provider="flatpickr" data-date-format="d F Y" placeholder="Başlangıç Tarihi" />
<input asp-for="e.SchoolEnd" type="date" class="form-control" data-provider="flatpickr" data-date-format="d F Y" placeholder="Bitiş Tarihi" />
This is backend validation, The error message will show after ModelState.IsValid, Please refer to this simple demo:
Model
public class DataModel
{
[Required]
public string Test { get; set; }
public DateTime? SchoolStart { get; set; }
[CustomAdmissionDate]
public DateTime? SchoolEnd { get; set; }
}
validate class
public class CustomAdmissionDate : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = (DataModel)validationContext.ObjectInstance;
if (model.SchoolEnd <= model.SchoolStart)
{
return new ValidationResult("End date must be greater than the start date.");
}
return ValidationResult.Success;
}
}
Backend post method
[HttpPost]
public IActionResult validate(DataModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return View();
}
View
#model DataModel
<form asp-action="Validate">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Test" class="control-label"></label>
<input asp-for="Test" class="form-control" />
<span asp-validation-for="Test" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SchoolStart" class="control-label"></label>
<input asp-for="SchoolStart" class="form-control" />
<span asp-validation-for="SchoolStart" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SchoolEnd" class="control-label"></label>
<input asp-for="SchoolEnd" class="form-control" />
<span asp-validation-for="SchoolEnd" class="text-danger"></span>
</div>
<button type="submit">Submit</button>
</form>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Demo:
=========Update=========
If you wanna achieve client validation, you need to create a client script, Please update CustomAdmissionDate class to:
public class CustomAdmissionDate : ValidationAttribute, IClientModelValidator
{
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-date", "End date must be greater than the start date.");
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//........
}
}
Then create js file named Date.js in wwwroot/js/Date.js
Date.js
jQuery.validator.addMethod("date",
function (value, element, param) {
var starttime = document.getElementById("SchoolStart").value;
var start = Date.parse(starttime);
var end = Date.parse(value)
if (end <= start) {
return false;
}
else {
return true;
}
});
Then refer this js file in your page:
<script src="~/js/Date.js"></script>
Now it can be validated in client site.

How to fix Invalid OperationException: Multiple constructors accepting all given argument types have been found in type 'System.Collections.Generic

I have seen variations to this question asked. But none of the answers seem to help me.
When trying to view a list of roles in my view after hitting the AddOrRemoveUsers button(see picture)
I get the following error message.
I cant find where the multiple constructors error seem to be. Am I missing something.
#page
#using ThreeTierAdvisementApp.Areas.Identity.Pages.Account.Administration
#using ThreeTierAdvisementApp.Data
#model List<UserRole>
<form method="post">
<div class="card">
<div class="card-header">
<h2>Add or remove users from this role</h2>
</div>
<div class="card-body">
#for(int i = 0; i<Model.Count; i++){
<div class="form-check m-1">
<input asp-for="#Model[i].IsSelected" class="form-check-input" />
<label class="form-check-label">
#Model[i].UserName
</label>
</div>
}
</div>
<div class="card-footer">
<input type="submit" value="Update" class="btn btn-primary"
style="width:auto" />
<a asp-action="EditRole" asp-route-id="UserId"
class="btn btn-primary" style="width:auto">Cancel</a>
</div>
</div>
</form>
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using ThreeTierAdvisementApp.Data;
namespace ThreeTierAdvisementApp.Areas.Identity.Pages.Account.Administration
{
public class EditUsersInRoleModel : PageModel
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<DefaultUser> _userManager;
public EditUsersInRoleModel(RoleManager<IdentityRole> roleManager, UserManager<DefaultUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
[BindProperty]
public UserRole RoleView { get; set; }
public async Task<IActionResult> OnGet(string roleId)
{
RoleView = new UserRole { UserId = roleId };
var role = await _roleManager.FindByIdAsync(roleId);
if (role == null)
{
return NotFound();
}
var model = new List<UserRole>();
foreach (var user in _userManager.Users)
{
var userRoleViewModel = new UserRole
{
UserId = user.Id,
UserName = user.UserName,
};
if (await _userManager.IsInRoleAsync(user, role.Name))
{
userRoleViewModel.IsSelected = true;
}
else
{
userRoleViewModel.IsSelected = false;
}
model.Add(userRoleViewModel);
}
return Page();
}
}
}
using Microsoft.AspNetCore.Identity;
using System.Security.Principal;
namespace ThreeTierAdvisementApp.Data
{
public class UserRole
{
public string UserId { get; set; }
public string UserName { get; set; }
public bool IsSelected { get; set; }
}
}
I am using asp.net 6 Razor page pattern but all the examples online are using the MVC pattern. Would appreciate some feedback on how to handle this.

Populating data from last row in form when creating a new entry

I have a form to create new data entries for comments. Creating completely new entries works fine. However, when I have already created one entry for my entity I want to populate the data from the last entry in my form.
I have tried to modify the OnGet action to include the data from the last entry. I copied the OnGet code from the Edit view into the Create view. However, if I do this, the Create page is not displayed anymore.
I have the following model:
public class ProjectComment
{
public int Id { get; set; }
public int? ProjectId { get; set; }
public Project Project { get; set; }
public int RAGStatusId { get; set; }
public RAGStatus RAGStatus { get; set; }
public string StatusComment { get; set; }
public string EscalationComment { get; set; }
public string GeneralComment { get; set; }
public double? EOQ { get; set; }
public DateTime LastUpdateDate { get; set; }
public ProjectComment ()
{
this.LastUpdateDate = DateTime.UtcNow;
}
The create form Create.cshtml:
#page
#model SimpleProjectReporting.Pages.ClientDetails.CreateModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>ProjectComment</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProjectComment.ProjectId" class="control-label"></label>
<select asp-for="ProjectComment.ProjectId" class="form-control" asp-items="ViewBag.ProjectId"><option value="" default="" selected="">-- Select --</option></select>
</div>
<div class="form-group">
<label asp-for="ProjectComment.RAGStatusId" class="control-label"></label>
<select asp-for="ProjectComment.RAGStatusId" class="form-control" asp-items="ViewBag.RAGStatusId"><option value="" default="" selected="">-- Select --</option></select>
</div>
<div class="form-group">
<label asp-for="ProjectComment.StatusComment" class="control-label"></label>
<input asp-for="ProjectComment.StatusComment" class="form-control" />
<span asp-validation-for="ProjectComment.StatusComment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectComment.EOQ" class="control-label"></label>
<input asp-for="ProjectComment.EOQ" class="form-control" />
<span asp-validation-for="ProjectComment.EOQ" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
The original Create.cshtml.cs action:
[BindProperty]
public ProjectComment ProjectComment { get; set; }
public IActionResult OnGet()
{
ViewData["ProjectId"] = new SelectList(_context.Project.Where(a => a.IsArchived == false), "Id", "ProjectName");
ViewData["RAGStatusId"] = new SelectList(_context.RAGStatus.Where(a => a.IsActive == true), "Id", "RAGStatusName");
return Page();
}
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.ProjectComment.Add(ProjectComment);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
The modified Create.cshtml.cs OnGet action:
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
ProjectComment = await _context.ProjectComment
.Include(p => p.Project)
.Include(p => p.RAGStatus).FirstOrDefaultAsync(m => m.Id == id);
if (ProjectComment == null)
{
return NotFound();
}
When modifying the action the way I did it, the page is not displayed anymore (404 error).
I would like to populate the create form with the data from the last entry in the database. If there is no comment, the create page would only populate the name of the project.
You are not sending the "id" parameter to your post action I guess.
So could you please try to adding this line under your form tag:
<form method="post">
<input type="hidden" id="ProjectComment.Id" name="id" value="ProjectComment.Id" />
You are trying to reach the last record of your ProjectComment table.
There are more than one methods to find the last record of your data table. But lets keep it simple.
You have an integer based identity column, which is Auto Increment. So you can simply use below methods to reach out the last created data of your table.
In your OnGetAsync() method:
//maxId will be the maximum value of "Id" columns. Which means that the maximum value is the last recorded value.
int maxId = _context.ProjectComment.Max(i => i.Id);
//And this line will bring you the last recorded "ProjectComment" object.
var projectComment = _context.ProjectComment.Find(maxId);
//You can assign it to your above 'ProjectComment' property if you want to..
ProjectComment = projectComment
Now, since you've find the last recorded data in your database, you can use that object.
Firstly, thanks to Burak for providing the above solution, which works when you want to display the last row in the table. This helped me solving my issue by using the same approach and finding the record based on the Id of the record.
I have amended the code from the Create.cshtml.cs file as follows:
public async Task<IActionResult> OnGetAsync(int? id, int projectid)
{
//This will find the "ProjectComment" object.
var projectComment = _context.ProjectComment.Find(id);
//This will display the 'ProjectComment' on the page
ProjectComment = projectComment;
if (id == null)
{
ProjectComment = projectComment;
ViewData["ProjectId"] = new SelectList(_context.Project, "Id", "ProjectName", projectid);
return Page();
}
ViewData["ProjectId"] = new SelectList(_context.Project, "Id", "ProjectName");
return Page();
}
I am using the int projectid to populate the drop down menu of the project when there is no comment create yet.

.net core 2.1 validation state: invalid

The following simple .NET Core 2.1 MVC code reports "Validation State: Invalid" when I submit to create. Everything works fine without the Owner property; and it works if Owner property is not required.
The Owner is the current user which is in the context of the server side, and it shouldn't be submitted from a client side, so the Create.cshtml doesn't have a Owner input in the form.
The error:
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Executing action method AnnouncementApp.Controllers.AnnouncementsController.Create (AnnouncementApp) with arguments (AnnouncementApp.Models.Announcement) - Validation state: Invalid
The model:
using System;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using AnnouncementApp.Models.Attributes;
using Microsoft.AspNetCore.Identity;
//using System.Security.Claims;
namespace AnnouncementApp.Models
{
public class Announcement
{
public int ID { get; set; }
[Required]
public string Content { get; set; }
[Display(Name = "Start Date and Time")]
public DateTime StartDate { get; set; }
[StartEndDate("End Date and Time must be after Start Date and Time")]
[Display(Name = "End Date and Time")]
public DateTime EndDate { get; set; }
[Required]
[BindNever]
public IdentityUser Owner { get; set; }
}
}
The controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Content,StartDate,EndDate")] Announcement announcement)
{
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(this.User);
announcement.Owner = user;
_context.Add(announcement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(announcement);
}
The Create.cshtml
#model AnnouncementApp.Models.Announcement
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Announcement</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Content" class="control-label"></label>
<textarea asp-for="Content" class="form-control"></textarea>
<span asp-validation-for="Content" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StartDate" class="control-label"></label>
<input asp-for="StartDate" class="form-control" />
<span asp-validation-for="StartDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="EndDate" class="control-label"></label>
<input asp-for="EndDate" class="form-control" />
<span asp-validation-for="EndDate" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
For Announcement, it will apply [Required] for both client validation and database table.
As the comments indicates, you could consider split Announcement to Db Model and ViewModel, you could define a new AnnouncementViewModel for client validation.
For another option, you could try configure the [Required] in the fluent api instead of attribute.
Here are the detail steps.
Change Announcement
public class Announcement
{
public int ID { get; set; }
[Required]
public string Content { get; set; }
[Display(Name = "Start Date and Time")]
public DateTime StartDate { get; set; }
public string OwnerId { get; set; }
//[Required]
[BindNever]
[ForeignKey("OwnerId")]
public IdentityUser Owner { get; set; }
}
Fluent api in ApplicationDbContext
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Announcement>()
.Property(a => a.OwnerId)
.IsRequired();
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Content,StartDate")] Announcement announcement)
{
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(User);
announcement.Owner = user;
_context.Add(announcement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(announcement);
}
I am not 100 % sure, what you define as the issue, but if you want to supress the "Model Invalid" error, since you are always setting the Owner property through the HttpContext, you can use the following before validating the model:
ModelState["Owner"].ValidationState = ModelValidationState.Valid
I think your issue is that you tell the router to never bind "Owner", but you still tells it is required, and therefore the ModelState would potentially invalidate it.
As long as the "Required" annotation is used, I do not think the ModelState will validate without it being set correctly.
Example:
ModelState["Owner"].ValidationState = ModelValidationState.Valid
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(this.User);
announcement.Owner = user;
_context.Add(announcement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(announcement);

editor template return null value

I have created an editor template base on this article ASP.NET MVC: Annotated for Input by Dino Esposito
Everything works fine until i press the submit button. I find out that my POST function return model is NULL, its like the model is not bind to the view. I have been trying all trick that I know and I found from the internet but I still can't fix it.
This is my controller
// GET: /Asset/New
public ActionResult New()
{
ViewBag.typeID = new SelectList(db.Ref_Asset_Types, "ID", "name");
return View(new AssetViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult New(AssetViewModel vm)
// vm.asset should contain new value but currently return null
{
if (ModelState.IsValid)
{
db.Assets.Add(vm.asset);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.typeID = new SelectList(db.Ref_Asset_Types, "ID", "name", vm.asset.typeID);
return View("New", vm);
}
this is my view
#using (Html.BeginForm("New","Asset","POST")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.EditorFor(m=>m.asset, "InputTemplate" )
// note : the code works if i don't use my own template ==> #Html.EditorFor(m=>m.asset)
<div class="form-actions btn pull-right">
#Html.ActionLink("Back to List", "Index", null, new { #class = "btn btn-sm"})
<button type="reset" class="btn btn-sm" value="Index">
Reset
</button>
<button type="submit" class="btn btn-sm btn-success">
<i class="glyphicon glyphicon-plus"></i>
Tambah
</button>
</div>
}
and this is my InputTemplate
#inherits System.Web.Mvc.WebViewPage
#if (Model == null)
{
<span>#ViewData.ModelMetadata.NullDisplayText</span>
}
else
{
foreach (var prop in ViewData
.ModelMetadata
.Properties
.Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm)))
{
if (prop.DisplayName != null) { // only display prop not of ComplexType
// note : using bootstrap for css styling
<div class="form-group col-xs-6">
<label class="col-xs-4 control-label text-right">
<span style="color:red"> #(prop.IsRequired ? "*" : "") </span>
<span>#prop.GetDisplayName()</span>
</label>
<div class="col-xs-8">
#if(prop.IsReadOnly)
{
<span class="readonly-field">#Html.Display(prop.PropertyName)</span>
}
else if (prop.TemplateHint == "DropDown")
{
<span>#Html.DropDownList(prop.PropertyName,(IEnumerable<SelectListItem>) ViewData[prop.PropertyName], new { #class = "form-control" })</span>
<span>#Html.ValidationMessage(prop.PropertyName)</span>
}
else
{
<div class="editor-field">
<span>#Html.Editor(prop.PropertyName, new { #class = "text-box single-line form-control" })</span>
<span>#Html.ValidationMessage(prop.PropertyName, new { #class = "label-danger" } )</span>
</div>
}
</div>
</div>
} // if
} // foreach
}
This is my viewmodel
using System;
using SIGMA.Models;
namespace SIGMA.ViewModels
{
public class AssetViewModel
{
public AssetViewModel()
{
asset = new Asset();
}
public Asset asset { get; set; }
}
}
This is my model
public class Asset
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[DisplayName("No. Siri")]
[StringLength(45)]
public string serial_num { get; set; }
[DisplayName("Model")]
[Required(ErrorMessage = "Model perlu diisi!")]
[StringLength(45)]
public string model { get; set; }
[DisplayName("Harga Seunit")]
[RegularExpression(#"^\d{0,6}(\.\d{2})?$", ErrorMessage = "Sila gunakan format harga yang betul.")]
public float? unit_cost { get; set; }
[UIHint("DropDown")]
[DisplayName("Jenis Aset")]
[Required(ErrorMessage = "Jenis aset perlu dipilih!")]
[DisplayFormat(NullDisplayText = "Belum didaftar")]
public int? typeID { get; set; }
public virtual Ref_Asset_Type type { get; set; }
}
Sorry guys for the trouble.. i think i solve it.
My biggest mistake is using reserved word 'model' and 'type' as my property name. This some how cause problem to asp.net in interpreting my model using the user define editor template.
Once I change my property name - model to model_name and type to asset_type, i can see the my entry in my return model already.
Thanks to all
.... spends the whole day and night for this silly mistake but the lesson learn is worth it

Resources