submit form as object parameter - asp.net-mvc

So I have this form on my Index.cshtml view (Some data removed to shorten length),
I want to be able to submit it to the action "/Estimate/Index" defined as,
public ActionResult CreateEstimate(Estimate Est);
It creates the object just fine by serializing the data and submitting, my problem is that sub-object data is not inserted (and I understand that it doesn't cause it doesn't know how), I was curious if there was a way to tell it how to correctly create the objects in a simple/automated manner.
I've tried giving the NetUplift a different name,
Ex. name="NetUplift.Value" but this results in an internal server error (code 500).
Form in razor view - Index.cshtml
<form id="form-create-estimate">
<table id="table-create-estimate" class="table">
<tbody>
<tr>
<td class="td-header">ID</td>
<td id="td-id"><input name="ID" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header">Name</td>
<td id="td-name"><input name="Name" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header">Job Difficulty</td>
<td id="td-jobdifficulty"><input name="JobDifficulty" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header">Type</td>
<td id="td-type"><input name="Type" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header Unit">Net Uplift</td>
<td id="td-netuplift">
<input name="NetUplift" class="Unit" title="in PSF" type="number" value="" />
#*<input name="NetUplift.DataType" type="hidden" value="System.Int32" />
<input name="NetUplift.UnitOfMeasure" type="hidden" value="psf" />*#
</td>
</tr>
<tr>
<td class="td-header Unit">Joist Spacing</td>
<td id="td-joistspacing"><input name="JoistSpacing" class="FeetInch" title="in Feet' Inch''" type="text" value="" /></td>
</tr>
</tr>
<tr>
<td class="td-header">Drawing Location</td>
<td id="td-drawinglocation"><input name="DrawingLocation" type="text" value="" /></td>
</tr>
<tr>
<td><input id="button-submit-estimate" type="button" value="Create" /></td>
<td><input id="button-cancel-estimate" type="button" value="Cancel" /></td>
</tr>
</tbody>
</table>
</form>
Ajax Submit Script
// Do an Ajax Post to Submit the newly created Estimate.
function CreateEstimate() {
// Serialize the Form.
var Estimate = $("#form-create-estimate").serialize();
// Send the Serialized Form to the Server to be processed and returned
// as an updated page to asynchronously update this page.
// I guess instead of returning this entire page we could make the action
// return just a table row with the Estimate's Data, then append it to the table
// of estimates. It'll be saved to the list of estimates too so there won't be
// a chance to the lists on the Client and list on the server to be different.
$.ajax({
url: "/Estimate/CreateEstimate/",
datatype: "json",
data: Estimate,
contenttype: "application/json",
success: function (Data, Success) {
if (Data.Estimate !== undefined) {
// Create a Html Table Row from the Estimate.
// Append the Row to the Table.
// Hide the Dialog.
} else {
alert("No Error Occured; however, the Creation of the Estimate was unsuccessful. Please Try Again.");
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert("An Error Occured. \n" + thrownError + "\n" + xhr.status);
}
});
}
Estimate Class
public class Estimate
{
public Int32 ID { get; set; }
public String JobDifficulty { get; set; }
public String Type { get; set; }
public String Name { get; set; }
public Unit NetUplift { get; set; }
public Unit JoistSpacing { get; set; }
public String DrawingLocation { get; set; }
}
Unit Class
public class Unit
{
public String Value { get; set; }
public Type DataType { get; set; }
public String UnitOfMeasure { get; set; }
public Unit(Type DataType, String Value, String UnitOfMeasure)
{
this.Value = Value;
this.DataType = DataType;
this.UnitOfMeasure = UnitOfMeasure;
}
}
Currently the action CreateEstimate(Estimate Est) recieves the data submitted except for sub-object values such as Unit (Estimate.NetUplift). How do I tell it to map NetUplift to Estimate.NetUplift.Value, and the two commented out input fields to NetUplift.DataType/NetUplift.UnitOfMeasure?
Is there a way to have the server just know that it should be mapped that way or do I have to do it before sending the form to the server?

You could try this:
$.fn.serializeToObject = function () {
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
var estimate = $("#form-create-estimate").serializeToObject();

Related

Input box as “date” on web when model is “int” (again)

I decided to create a new post here instead of the other post which was a bit confusing. (Input box as "date" on web when model is "int")
In this post I have added the code I use.
The problem is that in the SQL server the field FrDr is of type int and I need the user on the webpage to enter in a input field of type "date"
If I add [DataType(DataType.Date)] to the model i get the input date type autmoatically. but not if I add it to the Page Model
How can I change my code so that the user get an input field of type date that save the date value as an int to SQL.
MODEL AGR.CS
namespace DateTest.VismaModels {
public partial class Agr {
[Key]
[Display(Name = "Actor")]
public int AgrActNo { get; set; }
public int AgrNo { get; set; }
[DataType(DataType.Date)]
[Display(Name = "From Date")]
public int FrDt { get; set; }
}
}
PAGE MODEL
namespace DateTest.Pages {
public class IndexModel : PageModel {
private readonly DateTest.VismaModels.F0001Context _context;
public IndexModel(DateTest.VismaModels.F0001Context context) {
_context = context;
}
public bool ShowMessage => !string.IsNullOrEmpty(Message);
[TempData]
public string Message { get; set; }
[BindProperty]
public Agr AgrRow { get; set; }
public async Task<IActionResult> OnGetAsync(int? id) {
if (id == null) {
Console.WriteLine("New");
AgrRow = new Agr();
AgrRow.AgrNo = _context.Agr.Select(q => q.AgrNo).DefaultIfEmpty(0).Max() + 1;
//AgrRow.FrDt = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
}
if (!ModelState.IsValid) {
//var errors = ModelState.Select(x => x.Value.Errors).Where(y => y.Count > 0).ToList();
var errors = string.Join(" | ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage));
Message += "Model not valid : " + errors;
return Redirect("~/Activities/index");
}
return Page();
}
public async Task<IActionResult> OnPostSaveNewRowAsync() {
if (!ModelState.IsValid) {
//var errors = ModelState.Select(x => x.Value.Errors).Where(y => y.Count > 0).ToList();
var errors = string.Join(" | ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage));
Message += "Model not valid : " + errors;
return Redirect("~/index");
}
_context.Add(AgrRow);
try {
await _context.SaveChangesAsync();
} catch (DbUpdateConcurrencyException) {
throw;
}
Message += "Saved";
return RedirectToPage("/index");
}
}
}
RAZOR PAGE
#page
#model DateTest.Pages.IndexModel
#{
ViewData["Title"] = "Activities";
}
<h2>Activities</h2>
#if (Model.ShowMessage) {<div class="alert alert-info alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"></button>
#Model.Message
</div>
}
<p>
<a asp-page="Create">Create New</a>
</p>
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="AgrRow.AgrNo" />
<table class="table">
<tbody>
<tr>
<td><input asp-for="#Model.AgrRow.AgrActNo" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.FrDt" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.ToDt" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.FrTm" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.ToTm" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.CustNo" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.ProdNo" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Descr" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.NoInvoAb" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Price" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Am" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Fin" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Invo" class="form-control" /></td>
<td>
<div class="form-group">
<button type="submit" value="Save New" asp-page-handler="saveNewRow" asp-route-id="#Model.AgrRow.AgrNo" class="btn btn-default">
<i class="fa fa-save"></i>Create
</button>
</div>
</td>
</tr>
</tbody>
</table>
</form>
UPDATE
If I add the following to the Page Model
public DateTime FrDt { get; set; }
And print out the result in the post method like this
Console.WriteLine("FRDT = " + FrDt);
Console.WriteLine("AGRROW FRDT = " + AgrRow.FrDt);
I get this
FRDT = 0001-01-01 00:00:00
AGRROW FRDT = 0
I figured it out.
In my Agr Model I add a temporary field FrDt2 which is of DateTime and use the [NotMapped] annotation so it does not send it to sql
[NotMapped]
public DateTime FrDt2 { get; set; }
And in my Page model I convert FrDt2 to int before adding it to FrDt.
AgrRow.FrDt = int.Parse(AgrRow.FrDt2.ToString("yyyyMMdd"));
And, to show an existing item from SQL in the input date box I convert it back to int like this
AgrRow.FrDt2 = DateTime.ParseExact(AgrRow.FrDt.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);

Sending table datas to Controller without using ajax

I have a table in view which is static in nature.I am trying to post data to Controller without using Jquery Ajax. Is it possible?How can I retrieve all these data in Action "bulk_insert". I am able to send this bulk data to controller using jquery ajax but I am unable to send it directly.
My view :
#using entity_framework.Models
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container">
<h1>Static data</h1>
<div>
#using (Html.BeginForm("bulk_insert", "Form", FormMethod.Post, new { enctype = "multipart/form-data", id = "postForm" }))
{
<table id="myTable" class="table table-striped">
<thead>
<tr>
<td>Menu Item Id</td>
<td>Menu Item Name</td>
<td>Menu Qty</td>
<td>Menu Rate</td>
<td>Total</td>
</tr>
</thead>
<tbody>
<tr>
<td><input type="hidden" name="menu_item_id[0]" value="1" /><span>1</span></td>
<td><input type="hidden" name="menu_item_name[0]" value="chaumin" /><span>chaumin</span></td>
<td><input type="hidden" name="data[0].rate" value="100" /><span>100</span></td>
<td><input type="hidden" name="data[0].qty" value="2" /><span>2</span></td>
<td><input type="hidden" name="data[0].total" value="200"><span>200</span></td>
</tr>
<tr>
<td><input type="hidden" name="data[1].menu_item_id" value="2" /><span>2</span></td>
<td><input type="hidden" name="data[1].menu_item_name" value="Mo:Mo" /><span>Mo:Mo</span></td>
<td><input type="hidden" name="data[1].rate" value="100" /><span>100</span></td>
<td><input type="hidden" name="data[1].qty" value="2" /><span>2</span></td>
<td><input type="hidden" name="data[1].total" value="200"><span>200</span></td>
</tr>
</tbody>
</table>
<input type="submit" value="Submit" />
}
</div>
I am just testing with static data right now,but I need to do it with dynamic data i.e, When a user clicks a button, a row gets added using javascript .So,I didn't use Htmlhelper. My model:
public class tabledata
{
public int menu_item_id { get; set; }
public string menu_item_name { get; set; }
public string rate { get; set; }
public string qty { get; set; }
public string total { get; set; }
}
I don't know how can I get table datas from form but my Controller method:
[HttpPost]
public ActionResult bulk_insert(IEnumerable<tabledata> data)
{
//How can I get table datas here
}

How to get client side generated class list to MVC post action argument?

I have one form where I am generating some control dynamically (Which is list some class . in image add product artwork section) When I click on submit button how can i get this values in post Action method's argument so that I can use this value in collection.
For reference I have attached the image where i have option for multiple Artwork oprion
[HttpPost]
public ActionResult Add(Graphic graphicToAdd,Enumerable<GraphicArtwork> artworkOption)
{
// I want value in artworkOption
}
public class GraphicArtwork
{
[Key]
public int GraphicArtworkId { get; set; }
public int GraphisId { get; set; }
[Required(ErrorMessage = "The {0} is required.")]
[DisplayName("Option Text")]
[StringLength(500)]
public string ArtOptionText { get; set; }
public decimal Price { get; set; }
[DisplayName("Active")]
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
[NotMapped]
public int TotalRecordCount { get; set; }
}
This is my view :
<div class="editor-field PrintOptions">
<table data-bind="visible :customArtworks().length > 0">
<thead>
<tr>
<td class="editor-label">Option</td>
<td class="editor-label">Price</td>
<td></td>
</tr>
</thead>
<tbody data-bind="foreach: customArtworks">
<tr>
<td>
<input placeholder="Enter Artwork Text" type="text" data-bind="value: ArtOptionText'}" />
</td>
<td>
<input placeholder="Enter Price" type="text" data-bind="value: Price" />
</td>
<td>
<img title="Add" src="~/Content/images/icon_add.png">
<img title="Delete" style="margin-left:10px;" src="~/Content/images/icon_delete.png">
</td>
</tr>
</tbody>
</table>
</div>
For more detail : I am generating my dynamical control using knockout.js
// Ko Implemntation
function GraphicArtwork(ArtOptionText, Price) {
var self = this;
self.ArtOptionText = ArtOptionText;
self.Price = Price;
self.formattedPrice = ko.computed(function () {
var price = self.Price;
return price ? "$" + price.toFixed(2) : "0.00";
})
}
function GraphicArtworkViewModel() {
var self = this;
self.customArtworks = ko.observableArray([GraphicArtwork(null, null)]);
self.add = function () {
self.customArtworks.push(new GraphicArtwork(null, null));
};
self.remove = function (GraphicArtwork) {
self.customArtworks.remove(GraphicArtwork);
};
}
ko.applyBindings(new GraphicArtworkViewModel());
Add This binding :
<td>
<input placeholder="Enter Artwork Text" type="text" data-bind="value: ArtOptionText,attr:{name: '['+$index()+'].ArtOptionText'}" />
</td>
<td>
<input placeholder="Enter Price" type="text" data-bind="value: Price,attr:{name: '['+$index()+'].Price'}" />
</td>
AND in Code Behind
public ActionResult Add(Graphic graphicToAdd,IEnumerable<GraphicArtwork> listOfGraphicArtwork)
{
// listOfGraphicArtwork will hold all the required data
}

fiil a list with values of a table

I'm new in learning asp.net MVC. I am writing because I am stubborn to a problem. Indeed, I have an application that should allow me to create an XML file that will be added to a database. At this point, I created my Model, and my view that allows me to create my XML tags.
I saw on this site that could add lines in my table via Javascript. What I have done just as you can see in the code.
I can not recover what is the value of each line that I can insert. Passing my view a list I created myself. I can recover both inputs I inserted in my controller.
My question is, there's another way to create a dynamic lines via javascript, then all the entries that the user has entered the recover and fill in my list? Then I know myself how I can play with my list. But I just want to recover all the different lines that my user has inserted. I am new in ASP.NET MVC. Any help , please
This is my code.
Model
public class XMLFile
{
public string TypeDoc { get; set; }
public string Type { get; set; }
public string Contenu { get; set; }
public string DocName { get; set; }
}
This is my controller :
public class XMLFileController : Controller
{
List<XMLFile> file = new List<XMLFile>();
[HttpGet]
public ActionResult Save()
{
file.AddRange( new XMLFile[] {
new XMLFile (){Type = "Titre", Contenu = "Chef de Service"},
new XMLFile (){Type = "Item", Contenu="Docteur Joel"}
});
return View(file);
}
[HttpPost]
public ActionResult Save(List<XMLFile> formCollection)
{
try
{
if (formCollection == null)
{
return Content("la liste est nulle");
}
else
{
return RedirectToAction("Create", "Layout");
}
}
catch
{
return View();
}
}
}
My view with a script for adding a new Row :
#using (Html.BeginForm("Save", "XMLFile", FormMethod.Post,new { #class = "form-horizontal", #role = "form", #id = "FormCreateXML" }))
{
<table class="table table-bordered" id="XMLFileTable">
<thead>
<tr>
<th>Type</th>
<th>Contenu</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i<Model.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(model=>model[i].Type, new {#class="form-control help-inline", #placeholder="type" })</td>
<td> #Html.TextBoxFor(model=>model[i].Contenu, new {#class="form-control help-inline", #placeholder="contenu" })</td>
<td> <input type="button" class="BtnPlus" value="+" /> </td>
<td> <input type="button" class="BtnMinus" value="-" /> </td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td> <button type="submit" class="btn btn-success" >Save</button> </td>
</tr>
</tfoot>
</table>
}
</body>
<script type="text/javascript">
$(document).ready(function () {
function addRow() {
var html = '<tr>' +
'<td><input type="text" class="form-control" placeholder="type"></td>' +
'<td> <input type="text" class="form-control" placeholder="contenu"></td>' +
'<td> <input type="button" class="BtnPlus" value="+" /> </td>' +
'<td> <input type="button" class="BtnMinus" value="-" /></td>' +
'</tr>'
$(html).appendTo($("#XMLFileTable"))
};
function deleteRow() {
var par = $(this).parent().parent();
par.remove();
};
$("#XMLFileTable").on("click", ".BtnPlus", addRow);
$("#XMLFileTable").on("click", ".BtnMinus", deleteRow);
});
</script>

ASP.NET with Knockout variable length list with combobox - how to bind?

With the following ASP.NET models
public class User
{
public string Name { get; set; }
public LEmail LEmail { get; set; }
}
public class LEmail
{
public IList<CLabel> Labels;
public IList<CEmail> Emails;
}
public class CLabels
{
public IList<CLabel> Labels { get; set; }
}
public class CLabel
{
public string Name { get; set; }
}
public abstract class CEmail
{
public string SelectedLabel { get; set; }
public string Name { get; set; }
}
Filling it out with dummy data and sending to appropriate view as User object, I have the following knockout definitions in the view:
#using (Html.BeginForm("MyAction", "MyController", FormMethod.Post, new { id = "MyEditor" }))
{
#Html.EditorFor(m => #Model.LEmail)
<p>
<input type="submit" value="Save" data-bind="enable: Emails().length > 0" />
Cancel
</p>
<p data-bind="visible: saveFailed" class="error">A problem occurred saving the data.</p>
<div id="debug" style="clear: both">
<hr />
<h2>Debug:</h2>
<div data-bind="text: ko.toJSON(viewModel)"></div>
</div>
}
<script type="text/javascript">
$(function() {
ko.applyBindings(viewModel);
$("#profileEditorForm").validate({
submitHandler: function(form) {
if (viewModel.save())
window.location.href = "/";
return false;
}
});
});
var viewModel = {
Name: ko.observable("#Model.Name"),
Labels: ko.observableArray(#Html.Json(Model.LEmail.Labels) || []),
Emails: ko.observableArray(#Html.Json(Model.LEmail.Emails) || []),
addEmail: function() {
viewModel.Emails.push(#Html.Json(new CEmail()));
},
removeEmail: function(eml) {
viewModel.Emails.remove(eml);
},
saveFailed: ko.observable(false),
// Returns true if successful
save: function() {
var saveSuccess = false;
viewModel.saveFailed(false);
// Executed synchronously for simplicity
jQuery.ajax({
type: "POST",
url: "#Url.Action("MyAction", "MyController")",
data: ko.toJSON(viewModel),
dataType: "json",
contentType: "application/json",
success: function(returnedData) {
saveSuccess = returnedData.Success || false;
viewModel.saveFailed(!saveSuccess);
},
async: false
});
return saveSuccess;
}
};
</script>
And finally the editor template that is actually supposed to take care of variable length list that look like this:
#model MyDomain.ViewModels.LEmail
<table>
<tbody data-bind="template: { name: 'EmailsTemplate', foreach: Emails }" />
</table>
<button data-bind="click: addEmail">Add Email</button>
<script id="EmailsTemplate" type="text/html">
<tr>
<td>
#* PROBLEM IS HERE!! Labels won't show (they will it this code taken out of template) *#
<select data-bind="options: Labels"></select></td>
<td>
<input type="text" data-bind="value: Name, uniqueName: true" class="required" /></td>
<td>
Delete</td>
</tr>
</script>
Essentially I
cannot make it work in the EditorTemplate for combobox (or
dropdownlist). It won't attach to Labels no matter what I do. If I
take it outside the template somewhere else - it works as expected.
Also, based on selection to fill out the "SelectedValue" inside the Email - how to do that.
After everything is selected, (this must be simple) how to post it
all back without losing values on the way (its a super nested model
as you see).
Thank you very much in advance!
Labels is on your view model, not each email. Since the template is rendered within the context of a Knockout foreach binding, the binding context has changed to an email.
Here's how I'd write your view:
#model FiveW.ViewModels.LabeledEmail
<table>
<tbody data-bind="foreach: Emails">
<tr>
<td>
<select data-bind="options: $root.Labels, value: SelectedLabel"></select>
</td>
<td>
<input type="text" data-bind="value: Name, uniqueName: true" class="required" />
</td>
<td>
Delete
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addEmail">Add Labeled Email</button>
The fix is in $root.Labels: we need to tell Knockout to use $root (your view model), since Labels is actually on your view model, and not on an individual email.
Also notice I didn't use an named template. This is preferable. Unless you are using the template in more than one place in your view, you should use anonymous, inline templates like I did above.

Resources