Unobtrusive javascript adding data to #Html.HiddenFor - asp.net-mvc

I have a hidden field on a form that is created in Razor using the #Html.HiddenFor helper:
#Html.HiddenFor(model => model.BidID, new { id="bidItemID" })
My View model looks like this:
public class BidEditVM
{
[Display(Name = "Bid ID")]
public int BidID { get; set; }
[StringLength(51)]
[Display(Name = "Customer Name")]
public string CustomerName { get; set; }
[StringLength(75)]
[Display(Name = "Bid Name")]
public string BidName { get; set; }
[Display(Name = "Amount")]
public decimal Amount { get; set; }
[Display(Name = "Time")]
public DateTime BidTime { get; set; }
}
When the HTML is rendered, unobtrusive javascript adds it's stuff to the hidden input field even though it will never require validation:
<input id="bidItemID" type="hidden" value="5198" name="BidID" data-val-required="The Bid ID field is required." data-val-number="The field Bid ID must be a number." data-val="true">
What's odder is that the message and validation it adds aren't even part of the view model for this partial view. The view looks like this:
#model AuctionAdmin.Models.ViewModels.BidEditVM
#using (Ajax.BeginForm("UpdateBid", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "modalBidInfo" }))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.BidID, new { id="bidItemID" })
<fieldset>
<legend>Edit Bid</legend>
<div class="display-label">#Html.LabelFor(model => model.CustomerName)</div>
<div class="display-field">
#Html.DisplayFor(model => model.CustomerName)
</div>
<div class="display-label">#Html.LabelFor(model => model.BidName)</div>
<div class="display-field">
#Html.DisplayFor(model => model.BidName)
</div>
<div class="editor-label">#Html.LabelFor(model => model.Amount)</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
</div>
<div class="editor-label">#Html.LabelFor(model => model.BidTime)</div>
<div class="editor-field">
#Html.EditorFor(model => model.BidTime)
</div>
</fieldset>
}
Where is it getting this metadata from and how can I stop it?

It's marked as such since the type in the view model is an int.
It's adding the html due to this line:
#Html.HiddenFor(model => model.BidID, new { id="bidItemID" })
Why is it a problem that the extra attributes are present?
If it really is problematic, try changing the type of BidId to int? (a nullable int).

Related

How to set value to a field in mongodb without using the field in view form

I want to set a value to Status property without using it in the view form
Here is the model class
public class NewsLetterModels
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[Required]
public string NewsLetterName { get; set; }
[Required]
[DataType(DataType.EmailAddress, ErrorMessage = "E-mail is not valid")]
public string AssigneeEmailId { get; set; }
public string Status { get; set; }
public NewsLetterModels()
{
}
public NewsLetterModels(PostNewsLetter postNewsLetter)
{
NewsLetterName = postNewsLetter.NewsLetterName;
AssigneeEmailId = postNewsLetter.AssigneeEmailId;
Status = postNewsLetter.Status;
}
}
I don't want to set value to the field in viewform.In fact I don't want to show this field in the viewform. when I click on initiate News Letter,"Initiated" value should be set to the propert "Status". I am using MongoDb as database
Here is the View Page
<fieldset>
<legend>PostNewsLetter</legend>
<div class="editor-label">
#Html.LabelFor(model => model.NewsLetterName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NewsLetterName)
#Html.ValidationMessageFor(model => model.NewsLetterName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AssigneeEmailId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AssigneeEmailId)
#Html.ValidationMessageFor(model => model.AssigneeEmailId)
</div>
<p>
<input type="submit" value="Initiate NewsLetter" />
</p>
</fieldset>
Any help will be appreciable.
Are you referring to the 'Status' property?
You should set this in the controller or if you are using a repository layer you could in a save method insert values before saving to database.

ModelState does not work properly

I have a very simple application. Here's my view code;
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Kategori Modeli</legend>
<div class="editor-label">
Category Name
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CategoryName)
#Html.ValidationMessageFor(model => model.CategoryName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>}
And here's my model code;
public int ID { get; set; }
[Required(ErrorMessage = "Category Name cannot be null.")]
[Range(3, 25, ErrorMessage = "Category Name must have 3-25 characters")]
public string CategoryName { get; set; }
And the Insert Code;
[HttpPost, ValidateInput(false)]
public ActionResult Insert(Category _category)
{
if (ModelState.IsValid)
{
---
}
}
ModelState.IsValid is always false even if it has 4 characters. Please help me. It always shows "Category Name must have 3-25 characters" error message.
Range does not validate a string length. It checks a numeric value is between the specified numbers.
MaxLength and MinLength should be used.

Create record using Model List

I am trying to create an invoice. I have two models, Invoice & InvoiceItems.
I am able to insert using hardcoded values, but I want to be able to use TextBoxes to create an invoice on the fly. How do I insert a record that takes the data for the invoice and the dynamic data from the invoice items and inserts into both tables, using the same view? I'd like to have an add more button eventually where I can stay on the same page and keep adding items to the same invoice. You can see what I've tried so far below.
Invoice Model:
public class Invoice
{
[Key]
public int InvoiceId { get; set; }
public int ClientId { get; set; }
[Display(Name = "Amount")]
public decimal Amount { get; set; }
[Display(Name = "Invoice Creation Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime CreationDate { get; set; }
[Display(Name = "Invoice Due Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime DueDate { get; set; }
[Display(Name = "Notes")]
public string InvoiceNotes { get; set; }
public List<InvoiceDetails> InvoiceDetails { get; set; }
public List<Clients> Clients { get; set; }
}
InvoiceItem Model:
public class InvoiceDetails
{
[Key]
public int InvoiceDetailsId { get; set; }
public int InvoiceId { get; set; }
[DisplayName("Item Name")]
public string Name { get; set; }
[DisplayName("Item Note")]
public string Note { get; set; }
[DisplayName("Qty")]
public decimal? Quantity { get; set; }
[DisplayName("Rate/Hour")]
public decimal? Price { get; set; }
[DisplayName("Item Total")]
public decimal? Total { get; set; }
}
Invoice Controller:
private NovaDb _db = new NovaDb();
public ActionResult InvoiceInformation()
{
var invoice = new Invoice();
invoice.InvoiceDetails = new List<InvoiceDetails>();
return View(invoice);
}
[HttpPost]
public ActionResult InvoiceInformation(Invoice model)
{
if (ModelState.IsValid)
{
var invoices = new Invoice()
{
Amount = model.Amount,
CreationDate = model.CreationDate,
DueDate = model.DueDate,
InvoiceNotes = model.InvoiceNotes,
InvoiceId = model.InvoiceId,
ClientId = model.ClientId
};
_db.Invoices.Add(invoices);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
Invoice View:
#model NovaFinancial.Models.Invoice
#{
ViewBag.Title = "InvoiceInformation";
}
<h2>InvoiceInformation</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Invoice</legend>
#Html.HiddenFor(model => model.InvoiceId)
<div class="editor-label">
#Html.LabelFor(model => model.ClientId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ClientId)
#Html.ValidationMessageFor(model => model.ClientId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Amount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => model.Amount)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreationDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreationDate)
#Html.ValidationMessageFor(model => model.CreationDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DueDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DueDate)
#Html.ValidationMessageFor(model => model.DueDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InvoiceNotes)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InvoiceNotes)
#Html.ValidationMessageFor(model => model.InvoiceNotes)
</div>
<div>
<table>
<tr>
<th>Name</th>
<th>Notes</th>
<th>Qty</th>
<th>Price</th>
<th>Total</th>
</tr>
#for (int i = 0; i < Model.InvoiceDetails.Count; i++)
{
#Html.HiddenFor(m=>m.InvoiceDetails[i].Name)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Note)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Quantity)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Price)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Total)
<tr>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Name) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Name)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Note) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Note)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Quantity) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Quantity)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Price) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Price)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Total) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Total)</td>
</tr>
}
</table>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
I worked out "a" solution that might not be the best, but it works. You'd still have to add to the code to make it more robust, but the general framework is there.
In your view, keep all the text boxes for the invoice details (Name, Quantity, Price) the same value for the name attribute, but do keep the id's unique. I used a bit of jQuery and JavaScript to generate extra rows as needed per a button that the user would click. For example,
<input type="text" name="Name" id="Name"> <!--first line item for Item Name-->
<input type="number" name="Quantity" id="Quantity"> <!--first for Quanitity -->
<input type="text" name="Name" id="Name2"> <!--second line item for Item Name-->
<input type="number" name="Quantity" id="Quantity2"> <!-- second for Quanitity -->
The values for the InvoiceDetail lines will pass back to the server as comma-delimited strings (better make certain that your item names don't have commas!). On the server-side,
var Names = Request["Name"]; // this would yield something like "Labor,Parts"
In the controller, you'll need to parse the strings into arrays and create the instances of your InvoiceDetail from them. I wrote a private method to split the strings and return a list of InvoiceDetail objects to the action method. The onus is on you to validate this data: both client-side and server-side need validation.
I did a few experiments. You can see all the code here: http://mefixme.blogspot.com/2014/10/aspnet-mvc-how-to-add-model-with.html
I hope that this helps you.

Validations not show up using EF Code First with complex types

This is a continuation of this question Model class and Mapping
I had my Client class now working fine and it's defined as
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using DataAnnotationsExtensions;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;
namespace CardNumbers.Objects
{
[ComplexType]
public class PhoneInfo
{
[DataType(DataType.PhoneNumber)]
[StringLength(10)]
[DisplayName("Phone")]
public virtual string Phone { get; set; }
[StringLength(5)]
[DisplayName("Ext")]
public virtual string Ext { get; set; }
public bool HasValue
{
get
{
return (Phone != null || Ext != null);
}
}
}
[ComplexType]
public class ContactDetail
{
//Constructor
public ContactDetail()
{
phoneInfo = new PhoneInfo();
}
[StringLength(100)]
[DisplayName("Contact Name")]
[DisplayFormat(NullDisplayText = "")]
public virtual string Contact { get; set; }
[Email]
[StringLength(100)]
[DisplayName("Email")]
public virtual string Email { get; set; }
public virtual PhoneInfo phoneInfo { get; set; }
public bool HasValue
{
get
{
return (Contact != null || Email != null || phoneInfo.HasValue);
}
}
}
/// <summary>
/// Client class (Client No, Client Name, Address, Contact1, Contact2 info, Created By, Modified By (operator and date)
/// </summary>
public class Client
{
public Client()
{
Contact1 = new ContactDetail();
Contact2 = new ContactDetail();
}
[Key]
[Column("ClientId",TypeName = "int")]
public virtual int Id { get; set; }
[Required]
[DisplayName("Client No")]
[Column("client_no", TypeName = "smallint")]
public virtual Int16 Number { get; set; }
[Required]
[Column("client_name", TypeName = "varchar")]
[DisplayName("Client Name")]
[MaxLength(30, ErrorMessage = "Client Name should not be longer than 30 characters" )]
[MinLength(3, ErrorMessage = "Client Name is too short")]
public virtual string Name { get; set; }
[DataType(DataType.MultilineText)]
public virtual string Address { get; set; }
public virtual ContactDetail Contact1 {get; set;}
public virtual ContactDetail Contact2 {get; set;}
[ForeignKey("EnteredByOperator")]
public string EnteredBy { get; set; }
[InverseProperty("ClientsEnteredBy")]
public virtual Operator EnteredByOperator { get; set; }
[ForeignKey("ModifiedByOperator")]
public string ModifiedBy { get; set; }
[InverseProperty("ClientsUpdatedBy")]
public virtual Operator ModifiedByOperator { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Created on")]
public DateTime EnteredOn { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Modified on")]
public DateTime? ModifiedOn { get; set; }
public virtual ICollection<ClientOrder> ClientOrders { get; set; }
public virtual ICollection<Reorder> Reorders { get; set; }
}
}
I mapped column names using Fluent API and I also re-defined my original "repository" classes to be very similar to defined in this tutorial http://code.msdn.microsoft.com/ASPNET-MVC-Application-b01a9fe8
This is my current partial view for the client form called _ClientForm:
#using WebDemo.Helper
#model CardNumbers.Objects.Client
<fieldset>
<legend>Client Info</legend>
#Html.ValidationSummary(true)
<input type="hidden" id="fntype" name="fntype">
#Html.HiddenFor(model => model.Id)
#Html.EditorFor(model => model.Number, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Name, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Address, EditorTemplate.EditBox)
<div id="ContactsInfo">
#*Contact 1*#
<div id="Contact1">
#*#Html.EditorFor(model=>model.Contact1)*#
#Html.EditorFor(model=>model.Contact1.Contact, EditorTemplate.TextBox)
#Html.EditorFor(model=>model.Contact1.Email, EditorTemplate.TextBox)
</div>
#*Contact2*#
<div id="Contact2">
#* #Html.EditorFor(model => model.Contact2)*#
</div>
</div>
#*<div class="clear"></div>*#
<div id="SaveCancel" class="float-right">
<button type="Submit" id="btnSave">Save</button>
<button type="reset" id="btnCancel">Cancel</button>
</div>
</fieldset>
I already tried to revert to original way of only one level and I also commented the second Contact2 info but still the e-mail validation doesn't work and all other validations also don't seem to work.
The EditorFor textboxes are defined based on this blog post http://fusionovation.com/post/2010/02/15/adding-a-rich-text-editor-to-asp-net-mvc-using-strongly-typed-helpers-dataannotations-amp-jquery.aspx
And these are two of the new EditorFor I added:
PhoneInfo.cshtml
#using WebDemo.Helper
#model CardNumbers.Objects.PhoneInfo
<div id="PhoneInfo">
<div class="float-left">
#Html.EditorFor(model => model.Phone, EditorTemplate.TextBox)
</div>
<div class="float-right">
#Html.EditorFor(model => model.Ext, EditorTemplate.TextBox)
</div>
</div>
And ContactDetail.cshtml
#using WebDemo.Helper
#model CardNumbers.Objects.ContactDetail
#Html.EditorFor(model => model.Contact, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Email, EditorTemplate.TextBox)
#Html.EditorFor(model=>model.phoneInfo)
So, as you can see, the code of the views is now very compact.
However, with all of these in place the validations don't see to fire anymore. I used to test validation on EMail by typing some garbage. It used to provide a validation message near the textbox. Now I observe that the email textbox takes the red border, but there is no message.
Do you see what I am missing now and if it's possible to use complex type and validations?
To clarify, the _ClientForm is called from this Client view:
#model CardNumbers.Objects.Client
#{
ViewBag.Title = "Client";
}
#section scripts {
<script src="#Url.Content("~/Scripts/Clients.js")" type="text/javascript" ></script>
}
<form id="frmClientsSearch">
<label for="clientNo">Client No: </label>
<input type="number" name="searchClientNo" class="numericOnly" /><br />
<label for="clientName">Client Name: </label>
<input type = "text" size =25 value ="Please enter the search value" class="SelectOnEntry"
name ="searchClientName" />
<input type="button" id="btnClientsSearch" value ="Find / Refresh" />
</form>
<div style="padding-left: 150px; padding-top: 50px; padding-bottom: 50px;" id="ClientsResults">
<table id="flexClients" style="display: none">
</table>
</div>
<div style="display: none">
<form id="sform" title="Client Info">
#{Html.RenderPartial("_ClientForm", Model) ;}
</form>
</div>
Thanks.
I don't see a form anywhere on your page. A form context is required for validation to work. You need to wrap the Editor attributes in BeginForm block.
After some trials and error I found that the TextBox EditorFor view was the culprit. I documented what I found in my answer here http://forums.asp.net/t/1855963.aspx/1?Validation+messages+don+t+show+up+what+is+missing+
Basically, as long as I use this EditorFor
#*#using WebDemo.Helper*#
#model CardNumbers.Objects.PhoneInfo
<div id="PhoneInfo">
<div class="float-left">
#* #Html.EditorFor(model => model.Phone, EditorTemplate.TextBox)*#
<div class="editor-label">
#Html.LabelFor(model => model.Phone)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Phone)
#Html.ValidationMessageFor(model => model.Phone)
</div>
</div>
<div class="float-right">
#*#Html.EditorFor(model => model.Ext, EditorTemplate.TextBox)*#
<div class="editor-label">
#Html.LabelFor(model => model.Ext)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Ext)
#Html.ValidationMessageFor(model => model.Ext)
</div>
</div>
</div>
All seems to work OK. But if I try to switch to a shorter syntax and use this EditorFor for the textbox:
<div class="editor-label">
#Html.Label((ViewData.ModelMetadata.DisplayName??ViewData.ModelMetadata.PropertyName),
new Dictionary<string, object>
{
{ "for", ViewData.ModelMetadata.PropertyName }
})
</div>
<div class="editor-field">
#Html.TextBox("", (object)Model,
new Dictionary<string, object>
{
{ "id", ViewData.ModelMetadata.PropertyName },
{ "name", ViewData.ModelMetadata.PropertyName },
{ "class", "text-box single-line"},
{ "data-bind", "value: " + ViewData.ModelMetadata.PropertyName },
})
#Html.ValidationMessage(ViewData.ModelMetadata.PropertyName,
new Dictionary<string, object>
{
{ "data-valmsg-for", ViewData.ModelMetadata.PropertyName }
})
</div>
Validation messages do not show anymore.
Hopefully this answer will help someone or you may see what I am missing here.

Adding a Number of Items to a Model list?

I have a page with the same input box added a number of times.
#Html.TextBoxFor(m => m.Product)
#Html.TextBoxFor(m => m.Product)
#Html.TextBoxFor(m => m.Product)
How to I bind this to the Model.
I've tried:
public class Shop
{
public string ShopName { get; set; }
[Remote("ProductExists", "Account", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public List<String> Product { get; set; }
}
But I can only ever see the data in the first field. Also I tried:
#Html.TextBoxFor(m => m.Product[0])
#Html.TextBoxFor(m => m.Product[1])
#Html.TextBoxFor(m => m.Product[2])
But remote validation doesn't work so I'm a little stumped here. Essential what I would like to achieve is to send the list of products with the shop so that it can be validated via a remote call to a function. I tried putting the products within there own public class but then I wasn't able to access the shop name from within that class.
This is the Controller Action I'm trying to use:
public JsonResult ProductExists(List<String> Product, string ShopName)
Any Ideas how I could solve this would be so much appreciated?
EDIT
This Semi works but remote validation still isn't passing ShopName:
public class Shops
{
[Required]
public string ShopName { get; set; }
public List<Products> Product { get; set; }
}
public class Products
{
[Required]
[Remote("ProductExists", "Home", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public String Product { get; set; }
}
Controller Action:
public JsonResult ProductExists(List<String> Product, string ShopName)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
View:
#model Shop.Models.Shops
#{
ViewBag.Title = "Shop";
}
<h2>Shop</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Shop</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ShopName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ShopName)
#Html.ValidationMessageFor(model => model.ShopName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[0])
#Html.ValidationMessageFor(model => model.Product[0])
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[1])
#Html.ValidationMessageFor(model => model.Product[1])
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[2])
#Html.ValidationMessageFor(model => model.Product[2])
</div>
<input type="submit" value="Create" />
</fieldset>
}
look at the following answer. I would make product a class on its own like you tried. Loot at the rendered html code for the page and check out the field name for the ShopName textbox. I think it should be ShopName, if so you dont need to change the AdditionalFields attribute if not change it to the name rendered.
So something like this:
public class Shop
{
public string ShopName { get; set; }
public List<Products> Product { get; set; }
}
public class Products
{
[Remote("ProductExists", "Account", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public String Product { get; set; }
}
in your view do something like this:
foreach(var item in Model.products)
{
#Html.TextBoxFor(item => item.product) // not sure if the syntax is right
}

Resources