I am a newbie at ASP.net MVC5, my problem is this:
I am creating a partial view "AgregaGuia", in which I make a query to TblGuias model of a row that do not yet have "fechaRecepcionGuia", these guides are filled in a combobox and when selected this the guide fills all textbox in that view. However when running the application it generated the following error: DataBinding : 'System.String' does not Contain a property with the name 'numeroGuia'.
Could anyone help me please??
this is my model:
public partial class TblGuias
{
public TblGuias()
{
this.TblFactIC = new HashSet<TblFactIC>();
}
public string numeroGuia { get; set; }
public string companiaEnvios { get; set; }
public string destino { get; set; }
public decimal pesoGuia { get; set; }
public System.DateTime fechaEnvioGuia { get; set; }
public Nullable<System.DateTime> fechaRecepcionGuia { get; set; }
public string comprobante { get; set; }
public virtual ICollection<TblFactIC> TblFactIC { get; set; }
}
this is my controller:
public class vueInveEntrsController : Controller
{
public ActionResult AgregaGuia()
{
ViewData["guia"] = new SelectList(db.TblGuias.Where(g => g.fechaRecepcionGuia == null).Select((g => g.numeroGuia)),"numeroGuia", "companiaEnvios","destino","pesoGuia","fechaEnvioGuia");
return PartialView(db.TblGuias.ToList());
}
[HttpPost]
public ActionResult Action(string numero)
{
var query = from c in db.TblGuias
where c.numeroGuia == numero
select c;
return Json(query);
}
}
and my view is as follows:
#using (#Html.BeginForm("Action", "vueInveEntrs", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.Label("Seleccione Guia", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("numero", (SelectList)ViewData["guia"], new { onchange = "Action(this.value);", #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("Compañia Envios", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("transporte", null, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("Destino", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("destino", null, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("Peso", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("peso", null, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.Label("Fecha Envio", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBox("fechaenvio", null, new { #class = "form-control" })
</div>
</div>
}
<script type="text/javascript">
function Action(numero) {
$.ajax({
url: '#Url.Action("Action", "vueInveEntrs")',
type: "POST",
data: { "numero": numero },
"success": function (data) {
if (data != null) {
var vdata = data;
$("#transporte").val(vdata[0].companiaEnvios);
$("#destino").val(vdata[0].destino);
$("#peso").val(vdata[0].pesoGuia);
$("#fechaenvio").val(vdata[0].fechaEnvioGuia);
}
}
});
}
</script>
The problem is this line in your controller:
ViewData["guia"] = new SelectList(
db.TblGuias.Where(g => g.fechaRecepcionGuia == null).Select((g => g.numeroGuia)),
"numeroGuia", "companiaEnvios","destino","pesoGuia","fechaEnvioGuia");
You are not specifying the constructor parameters for SelectList properly. There are several different overloads, but I think the one you want is this one:
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
The first parameter, items, represents the list of items that you want be rendered into <option> tags inside the <select>.
The second parameter, dataValueField, is the name of the property on the items in the enumerable which will become the value attribute inside each <option> tag.
Similarly, the third parameter, dataTextField, is the name of the property which will become the text displayed for each <option>.
So, if you change your code to the following, I think it should work:
ViewData["guia"] = new SelectList(
db.TblGuias.Where(g => g.fechaRecepcionGuia == null), "numeroGuia", "numeroGuia");
If you want different text to display in the dropdown list, change the third parameter to a different property from your TblGuias class.
Related
I am trying to append an item to the SQL table but when I press the save button nothing happens. The reset button works but not the Save. Nothing is showing in the database either. Basically, I don't understand why the buttons act as if there was no code attached to it. No Error message or crash shows up either.
View
#model BudgetAmazon.ViewModel.ItemViewModel
#{
ViewBag.Title = "Item";
}
<h2>Index</h2>
<script type="text/javascript">
$(document).ready(function () {
$("#btnSave").click(function () {
SaveItem();
});
$("#btnReset").click(function () {
ResetItem();
});
});
function ResetItem() {
$("#CategoryId").val("1");
$("#ItemCode").val("");
$("#ItemName").val("");
$("#Description").val("");
$("#ItemPrice").val("0");
$("#ImagePath").val("");
}
function SaveItem() {
var formData = new FormData();
formData.append("CategoryId", $("#CategoryId").val());
formData.append("ItemCode", $("#ItemCode").val());
formData.append("ItemName", $("#ItemName").val());
formData.append("Description", $("#Description").val());
formData.append("ItemPrice", $("#ItemPrice").val());
formData.append("ImagePath", $("#ImagePath").get(0).files[0]);
$.ajax({
async: true,
type: 'POST',
contentType: false,
processDate: false,
data: formDate,
url: '/Item/Index',
success: function (data) {
if (data.success) {
alert(data.Message);
ResetItem();
}
},
error: function (data) {
alert('There is a problem adding items.');
}
});
}
</script>
<div class="container">
<div class="col-md-4">
<div class="form-group">
Category :
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.CategorySelectListItem, "Value", "Text"),
new { #class = "form-control"})
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Code :
#Html.TextBoxFor(model => model.ItemCode, new { #class = "form-control", autocomplete = "Off"})
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Name :
#Html.TextBoxFor(model => model.ItemName, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Description :
#Html.TextBoxFor(model => model.Description, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Price :
#Html.TextBoxFor(model => model.ItemPrice, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Image Path :
#Html.TextBoxFor(model => model.ImagePath, new { type = "file", #class = "form-control"})
</div>
</div>
<div>
<input type="button" value="Save" name="save" class="btn btn-primary" id="btnSave"/>
<input type="button" value="Reset" name="reset" class="btn btn-danger" id="btnReset"/>
</div>
</div>
Controller
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BudgetAmazon.Models;
using BudgetAmazon.ViewModel;
namespace BudgetAmazon.Controllers
{
public class ItemController : Controller
{
private BudgetAmazonEntities3 objBudgetAmazonEntities;
public ItemController()
{
objBudgetAmazonEntities = new BudgetAmazonEntities3();
}
// GET: Item
public ActionResult Index()
{
ItemViewModel objItemViewModel = new ItemViewModel();
objItemViewModel.CategorySelectListItem = (from objCat in objBudgetAmazonEntities.Categories select new SelectListItem()
{
Text = objCat.CategoryName,
Value = objCat.CategoryId.ToString(),
Selected = true
});
return View(objItemViewModel);
}
[HttpPost]
public JsonResult Index(ItemViewModel objItemViewModel)
{
string NewImage = Guid.NewGuid() + Path.GetExtension(objItemViewModel.ImagePath.FileName);
objItemViewModel.ImagePath.SaveAs(Server.MapPath("~/Images/" + NewImage));
Item objItem = new Item();
objItem.ImagePath = "~/Images/" + NewImage;
objItem.CategoryId = objItemViewModel.CategoryId;
objItem.Description = objItemViewModel.Description;
objItem.ItemCode = objItemViewModel.ItemCode;
objItem.ItemId = Guid.NewGuid();
objItem.ItemName = objItemViewModel.ItemName;
objItem.ItemPrice = objItemViewModel.ItemPrice;
objBudgetAmazonEntities.Items.Add(objItem);
objBudgetAmazonEntities.SaveChanges();
return Json(new {Success = true, Message = "Item is added Successfully."}, JsonRequestBehavior.AllowGet);
}
}
}
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BudgetAmazon.ViewModel
{
public class ItemViewModel
{
public Guid ItemId { get; set; }
public int CategoryId { get; set; }
public string ItemCode { get; set; }
public string ItemName { get; set; }
public string Description { get; set; }
public decimal ItemPrice { get; set; }
public HttpPostedFileBase ImagePath { get; set; }
public IEnumerable<SelectListItem> CategorySelectListItem { get; set; }
}
}
I think the problem is you have made a typo mistake while giving the forma data. Try changing "formDate" to "formData". I have attached the picture to show where to.
enter image description here
As far as I know, your url may require something like .html file extension. Try to revise like url: "/Item/index.html, and see if the request success or not.
I have a form that the user should enter the Owners details. The user is allowed to enter more than one owner and then submit the form. I created a jQuery array to push the values that the user entered and then I rendered these values in a table. Now I want to map the html tables values to a view model List<OwnersViewModel> when the user submits the form to send it to the server.
Any ideas please how can I do it?
My view:
#using (Html.BeginForm(Html.BeginForm(null, null, FormMethod.Post, new { id = "WebsiteLicenseForm", enctype = "multipart/form-data" })))
{
<div class="row">
<div class="col-md-6">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.WebsiteLink, "الرابط الإلكتروني للمنصة ", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.WebsiteLink, new { #class = "form-control " })
#Html.ValidationMessageFor(model => model.WebsiteLink, "", new { #class = "text-danger" })
</div>
</div>
<div class="col-md-6">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.ApplicationLink, "الرابط الإلكتروني للتطبيق", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.ApplicationLink, new { #class = "form-control " })
#Html.ValidationMessageFor(model => model.ApplicationLink, "", new { #class = "text-danger" })
</div>
</div>
</div>
<br />
<h3>معلومات المالك</h3>
<div class="row">
<div class="col-md-4">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.First_Name, "الاسم الأول", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.First_Name, new { #class = "form-control ", #required = "required", #id = "First_Name" })
#Html.ValidationMessageFor(model => model.First_Name, "", new { #class = "text-danger", #id = "First_NameError" })
</div>
</div>
<div class="col-md-4">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.Second_Name, "اسم الأب", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.Second_Name, new { #class = "form-control ", #required = "required", #id = "Second_Name" })
#Html.ValidationMessageFor(model => model.Second_Name, "", new { #class = "text-danger", #id = "Second_NameError" })
</div>
</div>
<div class="col-md-4">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.Third_Name, "اسم الجد", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.Third_Name, new { #class = "form-control ", #required = "required", #id = "Third_Name" })
#Html.ValidationMessageFor(model => model.Third_Name, "", new { #class = "text-danger", #id = "Third_NameError" })
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.Last_Name, "العائلة", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.Last_Name, new { #class = "form-control ", #required = "required", #id = "Last_Name" })
#Html.ValidationMessageFor(model => model.Last_Name, "", new { #class = "text-danger", #id = "Last_NameError" })
</div>
</div>
<div class="col-md-4">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.OwnerNid, "الهوية الوطنية", new { #class = "control-label main-lable", })
#Html.TextBoxFor(model => model.OwnerNid, new { #class = "form-control ", #required = "required", #id = "OwnerNid", #type = "number" })
#Html.ValidationMessageFor(model => model.OwnerNid, "", new { #class = "text-danger", #id = "OwnerNidError" })
</div>
</div>
<div class="col-md-4">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.OwnerMobile, "رقم الهاتف", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.OwnerMobile, new { #class = "form-control ", #required = "required", #id = "OwnerMobile" })
#Html.ValidationMessageFor(model => model.OwnerMobile, "", new { #class = "text-danger", #id = "OwnerMobileError" })
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="main-form-group main-form-input">
#Html.LabelFor(model => model.OwneEmail, "البريد الإلكتروني", new { #class = "control-label main-lable" })
#Html.TextBoxFor(model => model.OwneEmail, new { #class = "form-control ", #required = "required", #id = "OwneEmail" })
#Html.ValidationMessageFor(model => model.OwneEmail, "", new { #class = "text-danger", #id = "OwneEmailError" })
</div>
</div>
<div class="col-md-4">
<br /><br />
<input type="button" value="add another owner " class="main-submit" id="addOWner" name="AddOwner" />
</div>
</div>
<br />
<table class="table main-table" id="OwnersTable">
<thead>
<tr>
<th>الاسم الأول</th>
<th>اسم الأب</th>
<th>اسم الجد</th>
<th>العائلة</th>
<th>رقم الهوية</th>
<th>الهاتف</th>
<th>البريد الإلكتروني</th>
<th>حذف</th>
</tr>
</thead>
</table>
<div class="main-form-submit">
<input type="submit" value="حفظ" class="main-submit" id="submit" name="submit" />
</div>
}
My jQuery function :
$("#addOWner").click(function () {
var Owners = [];
Owners.push({
'First_Name': $("#First_Name").val(),
'Second_Name': $("#Second_Name").val(),
'Third_Name': $("#Third_Name").val(),
'Last_Name': $("#Last_Name").val(),
'OwnerNid': $("#OwnerNid").val(),
'OwnerMobile': $("#OwnerMobile").val(),
'OwneEmail': $("#OwneEmail").val(),
});
for (i = 0; i < Owners.length; i++) {
var content = "<tr>"
for (i = 0; i < Owners.length; i++) {
content += '<td>' + Owners[i].First_Name + '</td>';
content += '<td>' + Owners[i].Second_Name + '</td>';
content += '<td>' + Owners[i].Third_Name + '</td>';
content += '<td>' + Owners[i].Last_Name + '</td>';
content += '<td>' + Owners[i].OwnerNid + '</td>';
content += '<td>' + Owners[i].OwnerMobile + '</td>';
content += '<td>' + Owners[i].OwneEmail + '</td>';
content += "<td><div><button id='" + Owners[i].OwnerNid + "' class='delete' name='delete' type='button'>DELETE</button></div></td>";
}
content += "</tr>"
$('#OwnersTable').append(content);
};
});
Submit form function:
$('#WebsiteLicenseForm').on('submit', function (event) {
event.preventDefault();
$(this).ajaxSubmit({
type: "Post",
url: '/Licenses/SubminWebSiteLicense', // the file to call
success: function (response) {
alert("Success");
}
});
return false;
});
My main model class:
public class WebsiteLicenseViewModel
{
public string WebsiteName { get; set; }
public string WebsiteLink { get; set; }
public string ApplicationLink { get; set; }
public string CrNumber { get; set; }
public string OwnerNid { get; set; }
public string OwnerMobile { get; set; }
public string OwnerEmail { get; set; }
public string First_Name { get; set; }
public string Second_Name { get; set; }
public string Third_Name { get; set; }
public string Last_Name { get; set; }
public string AttachmentUrl { get; set; }
public List<OwnersViewModel> OwnersList { get; set; }
}
My OwnersViewModel:
public class OwnersViewModel
{
public string OwnerNid { get; set; }
public string OwnerMobile { get; set; }
public string OwnerEmail { get; set; }
public string First_Name { get; set; }
public string Second_Name { get; set; }
public string Third_Name { get; set; }
public string Last_Name { get; set; }
}
You can push table values to array and pass it to controller as additional data where you can convert it to list. For example:
$('#WebsiteLicenseForm').on('submit', function (event) {
event.preventDefault();
//get array from table
var owners = [];
$('#OwnersTable').eq(0).find('tr').each((r, row) => owners.push($(row).find('td,th').map((c, cell) => $(cell).text()).toArray()))
//define form
var form = $('#WebsiteLicenseForm');
$.ajax({
type: "Post",
url: '/Licenses/SubminWebSiteLicense', // the file to call
data: form.serialize() + '&owners=' + JSON.stringify(owners),
dataType : 'json'
success: function (response) {
alert("Success");
}
});
return false;
})
Or you can add owners array to model and then append additional data to form before passing it to controller.
I have a view in one of my controller which have list of items. When user clicks each item, browser must navigate to page which brings details about this item.
Controller for Details accepts values through post method and have complex input object as its input.
Here is sample method for to navigate to details page using GET method to send values:
function openDetailsPage(commodityID, commodityName) {
dateFrom = convertNumbers2English('#(((FilterViewModel)ViewBag.ViewModel).dateValue_1)');
dateTo = convertNumbers2English('#(((FilterViewModel)ViewBag.ViewModel).dateValue_2)');
dateFrom = changeDateSeparator(dateFrom);
dateTo = changeDateSeparator(dateTo);
if (dateTo == null || dateTo == undefined)
dateTo = "0";
if (dateFrom == null || dateFrom == undefined)
dateFrom = "0";
#{
string reportTypes = "0";
if (((FilterViewModel)ViewBag.ViewModel).purchaseReportTypes != null)
{
reportTypes = String.Join(",", ((FilterViewModel)ViewBag.ViewModel).purchaseReportTypes);
}
}
alert('#reportTypes');
var url = '#Url.Action("ReportDetailed","BuyReport",new {
commodityType =(((FilterViewModel)ViewBag.ViewModel).commodityType),
commodityName="dummyCommodityName",
department=((FilterViewModel)ViewBag.ViewModel).department,
repository=((FilterViewModel)ViewBag.ViewModel).repository,
commodity ="dummyCommodityID",
purchaseReportTypes=reportTypes,
dateValue_1="dummyDate1",
dateValue_2="dummyDate2"
})';
alert(url);
#*var url = '#Url.Action("ReportDetailed","BuyReport",
new RouteValueDictionary
{
{"commodityType",((FilterViewModel)ViewBag.ViewModel).commodityType},
{"commodityName","dummyCommodityName" },
{"department",((FilterViewModel)ViewBag.ViewModel).department },
{"repository",((FilterViewModel)ViewBag.ViewModel).repository },
{"commodity","dummyCommodityID"},
{"purchaseReportTypes",((FilterViewModel)ViewBag.ViewModel).purchaseReportTypes },
{"dateValue_1",((FilterViewModel)ViewBag.ViewModel).dateValue_1 },
{ "dateValue_2",((FilterViewModel)ViewBag.ViewModel).dateValue_2 }
})';*#
url = url.replace("dummyCommodityID", commodityID);
url = url.replace("dummyCommodityName", commodityName);
url = url.replace("dummyDate1", dateFrom);
url = url.replace("dummyDate2", dateTo);
alert(url);
openLink(url);
}
I have some difficulties with this type of routing for values:
Input object is complex so route would be so complex. E.g. /BuyReport/ReportDetailed?commodityType=0&commodityName=dummyCommodityName&department=1&repository=2&commodity=dummyCommodityID&dateValue_1=dummyDate1&dateValue_2=dummyDate2 or /BuyReport/ReportDetailed/0/itemName/1/2/1/123/
Any special characters in get parameters such as / will break routing
I cannot pass stuff like arrays so I should convert them before sending
So I'm looking for a method to send parameters using 'Post' method like what form submit button does with below constraints:
I have no forms in my view
I want to post values to controller and page must navigate to details view
Each item in first page, have different row and different ID so I think creating a form for each row is not reasonable.
I want to know are there any ways to implement Post parameters according to my requirements? I would not care if it would be a mixture of C#, JS and jQuery.
More Details:
Here is a sample row in my list page which calls openDetailsPage js function:
<a onclick="openDetailsPage(#item.CommodityId,'#Html.DisplayFor(modelItem => item.CommodityName)')">
<div class="ios-content-box px-4 py-1 mb-3 ios-hover-box">
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 f-w-600 color-orange text-right">#Html.DisplayFor(modelItem => item.CommodityName)</div>
<div class="col-6 text-left"> <i class="fas fa-chevron-left fa-fw color-orange "></i></div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">type</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.TypesName)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Code</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.CommodityCode)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Barcode</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.CommodityBarcode)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Unit Price</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.UnitPrice)</div>
</div>
<div class="row font-12 my-2 ios-divider-line">
<div class="col-6 text-gray-600 text-right">Total Price</div>
<div class="col-6 text-gray-600 text-left">#Html.DisplayFor(modelItem => item.SumPrice)</div>
</div>
</div>
</a>
Currently my controller is as below:
[Route("BuyReport/ReportDetailed/{commodityType}/{commodityName}/{department}/{repository}/{commodity}/{purchaseReportTypes}/{dateValue_1}/{dateValue_2}")]
public async Task<ActionResult> ReportDetailed(
string commodityType,
string commodityName,
string department,
string repository,
string commodity,
string purchaseReportTypes,
string dateValue_1,
string dateValue_2
)
{
}
But I want to change it to something like this:
[HttpPost]
public async Task<ActionResult> ReportDetailed(DetailedViewModel detailedviewmodel){
string commodity = detailedviewmodel.commodity;
string commoditytype = detailedviewmodel.commoditytype;
string department = detailedviewmodel.department;
string purchasereporttypes = detailedviewmodel.purchasereporttypes;
string repository = detailedviewmodel.repository;
string startdate = detailedviewmodel.datevalue_1;
string enddate = detailedviewmodel.datevalue_2;
string commdoityname = detailedviewmodel.commodityname;
}
Where DetailedViewModel is defined as below:
public class DetailedViewModel
{
public string commodityType { get; set; }
public string commodityName { get; set; }
public string department { get; set; }
public string repository { get; set; }
public string commodity { get; set; }
public string[] purchaseReportTypes { get; set; }
public string dateValue_1 { get; set; }//start date
public string dateValue_2 { get; set; }//end date
}
This is not the right way to meet your purpose. Your code looks vulnerable for exploiters too. Don't use solutions which break the normal web application behavior.
Instead, send the parameters to the corresponding controller method and then make an internal redirection with model passing (controller side). If your data is stored in database just send CommodityId and find details in controller side instead of sending entire details as form (HTTPPOST). In this way, you have a well designed project without unwanted crashes which come from breaking the behaviors and your code looks simple and clear as you want.
One quick simple solution is to post via Ajax:
Let's imagine this as your controller:
[HttpGet]
public ActionResult ReportDetailed()
{
return View();
}
[HttpPost]
public JsonResult ReportDetailed(DetailedViewModel detailedviewmodel)
{
var status = "error";
var message = "";
try
{
string commodity = detailedviewmodel.commodity;
string commoditytype = detailedviewmodel.commodityType;
string department = detailedviewmodel.department;
List<string> purchasereporttypes = detailedviewmodel.purchaseReportTypes;
string repository = detailedviewmodel.repository;
string startdate = detailedviewmodel.dateValue_2;
string enddate = detailedviewmodel.dateValue_1;
string commdoityname = detailedviewmodel.commodityName;
// your code here ...
status = "success";
return Json(new { status, detailedviewmodel } , JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
message = ex.Message;
return Json(new { status, message }, JsonRequestBehavior.AllowGet);
}
}
Assuming you have defined DetailedViewModel inside the Models folder:
public class DetailedViewModel
{
public string commodityType { get; set; }
public string commodityName { get; set; }
public string department { get; set; }
public string repository { get; set; }
public string commodity { get; set; }
public List<string> purchaseReportTypes { get; set; }
public string dateValue_1 { get; set; }//start date
public string dateValue_2 { get; set; }//end date
}
In your View, I copy the whole Html and Javascript for you, just grab it and tweak it to your needs:
#model Your_Proj_Namespace.Models.DetailedViewModel
#{
ViewBag.Title = "ReportDetailed";
}
<h2>ReportDetailed</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DetailedViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.commodityType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.commodityType, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.commodityType, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.commodityName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.commodityName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.commodityName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.department, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.department, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.department, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label>purchaseReportTypes: 3 inputs for example</label>
<div class="col-md-10">
<input type="text" name="purchaseReportTypes[0]" class="form-control inputPurchaseReportTypes " value="" />
<input type="text" name="purchaseReportTypes[1]" class="form-control inputPurchaseReportTypes " value="" />
<input type="text" name="purchaseReportTypes[2]" class="form-control inputPurchaseReportTypes " value="" />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.repository, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.repository, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.repository, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.commodity, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.commodity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.commodity, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.dateValue_1, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.dateValue_1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.dateValue_1, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.dateValue_2, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.dateValue_2, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.dateValue_2, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" id="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
#section scripts{
<script>
$('#submit').click(function (e) {
e.preventDefault();
alert('form submitted');
var list = [];
$('.inputPurchaseReportTypes').each( function (key, value) {
list.push($(this).val());
});
const DATA = {
commodityType: $('#commodityType').val(),
commodityName: $('#commodityName').val(),
department: $('#department').val(),
repository: $('#repository').val(),
commodity: $('#commodity').val(),
purchaseReportTypes: list,
dateValue_1: $('#dateValue_1').val(),
dateValue_2: $('#dateValue_2').val(),
};
console.log(DATA);
$.ajax({
url: '/YourControllerName/ReportDetailed',
type: "POST",
contentType: "application/json",
dataType: "json",
data: JSON.stringify(DATA),
success: function (result) {
alert('success');
console.log(result);
// your code here
}
});
});
</script>
}
If you prefer not to use Ajax, comment the javascript code above (all the code inside <script>), to post the form directly.
I built and debugged the above code. Feel free to test it.
Hope this helped.
Finally, You might find the following links useful:
https://www.tutorialsteacher.com/mvc/model-binding-in-asp.net-mvc
This link clearly shows how to handle the list of Authors inside a book viewmodel, similar to yours: http://codebuckets.com/2016/09/07/asp-net-mvc-and-binding-complex-objects-magic/
https://stackoverflow.com/a/16326290/4687359
I agree with the solution by #A. Nadjar
one more note
Use HttpGet if you want user to share the url and show same data as he see it to another user ,
if not? use HttpPost with one Object parameter,
because maybe there's a Nullable parameters the user won't search by so the url will be like this
BuyReport/ReportDetailed/dummyCommodityName/null/null/null/dummyCommodityID/null/2/0
or don't use this custom route [Route("BuyReport/ReportDetailed/{commodityType}/{commodityName}/{department}/{repository}/{commodity}/{purchaseReportTypes}/{dateValue_1}/{dateValue_2}")]
so he can use the Query String and pass only the keys => values he want
I have simple form a dropdown list and a textbox. When the page is loaded textbox is disabled. If 'Other' is selected from dropdown list, then textbox is enabled and I add 'required' rule to textbox as I don't want user to enter something in the text box. When 'Other' is selected and if Create button is clicked, I can see validation error message but I cannot remove that message after user has typed something in the empty textbox. Here is how it looks:
Here is my model:
using System.ComponentModel.DataAnnotations;
namespace validatetextbox.Models
{
public class Manager
{
public int Id { get; set; }
[Required]
public string City { get; set; }
[Display(Name = "Other City")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string OtherCity { get; set; }
}
}
My Controller method is
// GET: Managers/Create
public ActionResult Create()
{
//populate Gender dropdown
List<SelectListItem> c = new List<SelectListItem>();
c.Add(new SelectListItem { Text = "-- Please Select --", Value = "" });
c.Add(new SelectListItem { Text = "New York", Value = "New York" });
c.Add(new SelectListItem { Text = "Miami", Value = "Miami" });
c.Add(new SelectListItem { Text = "Other", Value = "Other" });
ViewBag.Cities = c;
return View();
}
My view is as follows:
#model validatetextbox.Models.Manager
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Manager</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.City, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.City, ViewBag.Cities as List<SelectListItem>, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.City, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.OtherCity, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.OtherCity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OtherCity, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(document).ready(function () {
$('#OtherCity').prop('disabled', true);
$('#City').change(function () {
if ($('#City option:selected').text() != 'Other') {
$("#OtherCity").rules("remove", "required");
$('#OtherCity').prop('disabled', true);
} else {
$('#OtherCity').prop('disabled', false)
$("#OtherCity").rules("add", "required");
}
});
});
</script>
}
To keep your existing code - you can remove the rules from the text box on document ready. Also - side note - keep quotation marks consistent.
<script type="text/javascript">
$(document).ready(function () {
$('#OtherCity').prop('disabled', true);
// Remove any rules
$('#OtherCity').rules("remove");
$('#City').change(function () {
if ($('#City option:selected').text() != 'Other') {
//Also tidy up `"` to `'`
$('#OtherCity').rules("remove", "required");
$('#OtherCity').prop('disabled', true);
} else {
$('#OtherCity').prop('disabled', false)
//Also tidy up `"` to `'`
$('#OtherCity').rules("add", "required");
}
});
});
</script>
Another thing you could try is - add the class optional to the editor - the label will display and the visibility and validation requirement will be controlled by the JS:
<div class="form-group">
#Html.LabelFor(model => model.OtherCity, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10 optional">
#Html.EditorFor(model => model.OtherCity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OtherCity, "", new { #class = "text-danger" })
</div>
</div>
$(document).ready(function () {
// This will hide the field
$('.optional').hide();
// This makes the value optional
$('#OtherCity').attr("data-val", "false");
$('#City').change(function () {
if ($('#City option:selected').text() == 'Other') {
// Show the text editor
$('.optional').show();
// This makes the value required
$('#OtherCity').attr("data-val", "true");
} else {
$('.optional').hide();
$('#OtherCity').attr("data-val", "false");
}
});
});
I want to create a field object in ASP.NET MVC and store it to a database. The field should contain its coordinates which the user can select with the google maps api.
And now, how can I pass the coordinates of the markers, which are stored in a javascript array, to the Model´s List?
This is my Controller for this:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using BL;
using MvcAgrarTest.Models;
namespace MvcAgrarTest.Controllers
{
public class FieldsController : Controller
{
private MvcAgrarContext db = new MvcAgrarContext();
// GET: Fields
public ActionResult Index()
{
var field = db.CreateFieldViewModel.Include(f => f.FieldType);
return View(field.ToList());
}
// GET: Fields/Create
public ActionResult Create()
{
var model = new CreateFieldViewModel();
ViewBag.FieldTypeId = new SelectList(db.FieldType, "FieldTypeId", "Name");
return View(model);
}
// POST: Fields/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateFieldViewModel model, CreateFieldViewModel field, string[] markers)
{
if (ModelState.IsValid)
{
PostCoordinates(markers);
db.CreateFieldViewModel.Add(field);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.FieldTypeId = new SelectList(db.FieldType, "FieldTypeId", "Name", field.FieldTypeId);
return Json(true);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
Here my Create View with the Google Maps script:
#model MvcAgrarTest.Models.CreateFieldViewModel
#{
ViewBag.Title = "Create";
}
<h2>Feld anlegen</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #placeholder = "Feldname", #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FieldTypeId, "Feldtyp", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("FieldTypeId", null, "--Feldart auswählen--", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.FieldTypeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Size, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Size, new { htmlAttributes = new { #placeholder = "In Hektar", #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Size, "", new { #class = "text-danger" })
</div>
</div>
<div id="google">
<script src="https://maps.googleapis.com/maps/api/js?sensor=false" type="text/javascript"></script>
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.validate/1.5.5/jquery.validate.min.js" type="text/javascript"></script>
<script type="text/javascript">
var map;
function initialize() {
var myLatLng = new google.maps.LatLng(50.617109, 8.065738);
var myOptions = {
zoom: 5,
center: myLatLng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
// array to store markers that has been drawn
var markers = [];
// event listener draw a marker
google.maps.event.addListener(map, 'click', function (e) {
var marker = new google.maps.Marker();
marker.setPosition(e.latLng);
marker.setMap(map);
marker.setVisible(true);
markers.push(marker);
// draw polygon on marker click
// once user clicks on on of the markers
google.maps.event.addListener(marker, 'click', function (e) {
document.getElementById("label").innerHTML = "";
drawPoints(markers);
label.style.borderStyle = "dotted"
for (i = 0; i < markers.length; ++i) {
document.getElementById("label").innerHTML += markers[i].position;
}
// empty the markers array
markers = [];
});
});
}
function drawPoints(markers) {
var poly = new google.maps.Polygon;
var points = [];
for (var i = 0; i < markers.length; i++) {
points.push(markers[i].getPosition());
}
poly.setMap(map);
poly.setPath(points);
poly.setVisible(true);
}
</script>
<body onload="initialize()">
<div id="map_canvas" style="width: 500px; height: 300px"></div>
</body>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Erstellen" class="btn btn-default" />
</div>
</div>
</div>
}
and finally my ViewModel to create the field:
public class CreateFieldViewModel
{
[Key]
[Display(Name="ID")]
public int FieldId { get; set; }
[Required]
[Display(Name="Fieldname")]
public string Name { get; set; }
[Required]
[Display(Name="Fieldtype")]
public int FieldTypeId { get; set; }
[Required]
[Range(0.01, int.MaxValue, ErrorMessage = "The Size can´t be 0 or less")]
[Display(Name="Fieldsize")]
public float Size { get; set; }
[Display(Name="Coordinates")]
public List<string> Coordinates { get; set; }
public virtual FieldType FieldType { get; set; }
}
/edit: Id did it like this now, but it still doesn´t work
changed part from the google maps API:
var passdata = [];
google.maps.event.addListener(marker, "click", function(e){
for (y=0; y<markers.length; ++y){
passdata[y] = markers[y].position
}
drawpoints(markers);
$.ajax({
url: "#Url.Action("Create","Fields")",
typ: "POST",
datatype: "json",
data: JSON.stringify({coordinates: passdata}),
contentType: "application/json; charset=utf-8",
traditional: true,
success: function (data) {
alert(data);
},
});
And the Controller Function:
// GET: Fields/Create
public ActionResult Create()
{
var model = new CreateFieldViewModel();
ViewBag.FieldTypeId = new SelectList(db.FieldType, "FieldTypeId", "Name");
return View(model);
}
// POST: Fields/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateFieldViewModel field, string[] coordinates)
{
if (ModelState.IsValid)
{
field.Coordinates = coordinates;
db.CreateFieldViewModel.Add(field);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.FieldTypeId = new SelectList(db.FieldType, "FieldTypeId", "Name", field.FieldTypeId);
return View(field);
}
I understood, (looking by you code) that you want to go with submitting form, and not ajax?
If so, you have two choices (known to me):
Create html hidden input and paste there as value serialized JSON of your markers array - but you will have to deserialize this value later. In this example you need to change Coordinates type in your model to string.
JavaScript
// some sample coordinates, you need to extract them from markers
var coordinates = ["12,21","213,231"];
var dataToSend = JSON.stringify(coordinates);
$("#coordinatesInput").val(dataToSend);
HTML
<input hidden id="coordinatesInput" name="model.Coordinates" value=""/>
Create dynamically, using JavaScript (for example append function) many hidden html inputs with name model.Coordinates[i] and value of single coordinate (use some loop).
JavaScript
var coordinates = ["231,2132","312,231","231,321"];
var container = $("#container");
//container, where you will be adding inputs, in you example it could be form-horizontal class
coordinates.forEach(function(val, i){
container.append('<input hidden name="model.Coordinates['+i+']" value="'+val+'">');
});
Of course using AJAX is much better way, and AlexB answer is way to go, but mechanism of it is a little different. It is good to use only AJAX (not form submitting), because in your example you need to use two Actions, one for AJAX and one for form submitting.
You can pass your Javascript array to your controller using an Ajax call :
$.ajax({
url : "#Url.Action("MethodName", "ControllerName")",
contentType : "application/json; charset=utf-8",
dataType : "json",
type : "POST",
data : JSON.stringify({coordinates: markers})
})
(where markers is your JS array to pass to the controller, I guess)
And your controller will look like
public ActionResult MethodName(Single[] coordinates)
{
// ...
}