POSTing KnockoutJS model to MVC controller, List<T> in List<T> is empty - asp.net-mvc

I have little experience with KnockoutJS so please bear with me.
I have a basic example that I want to get working so I can expand it to my project.
For this example you click the button and the AddSku method is called to return QuoteLine data with List.
However, as the diagram shows, BomDetails is empty:
Models:
public class QuoteViewModel
{
public int Id { get; set; }
public string QuoteName { get; set; }
public IList<QuoteLine> QuoteLines { get; set; }
}
public class QuoteLine
{
public string Description { get; set; }
public string Sku { get; set; }
public IList<BomDetail> BomDetails = new List<BomDetail>();
}
public class BomDetail
{
public string Name { get; set; }
}
Controller methods:
[HttpGet]
public ActionResult CreateQuote()
{
QuoteViewModel quote = new QuoteViewModel();
quote.Id = 10;
quote.QuoteName = "Test Create Quote";
quote.QuoteLines = new List<QuoteLine>();
return View(quote);
}
[HttpPost]
public ActionResult CreateQuote(QuoteViewModel viewModel)
{
if (ModelState.IsValid)
{
}
return RedirectToAction("CreateQuote");
}
[HttpGet]
public JsonResult AddSku()
{
QuoteLine line = new QuoteLine();
line.BomDetails = new List<BomDetail>();
line.Sku = "TestSku";
line.Description = "TestDescription";
line.BomDetails.Add(new BomDetail
{
Name = "BOM Detail 1"
});
line.BomDetails.Add(new BomDetail
{
Name = "BOM Detail 2",
});
return Json(line, JsonRequestBehavior.AllowGet);
}
The view:
#model EngineeringAssistantMVC.ViewModels.QuoteViewModel
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<div class="container-fluid">
<h2>Create Quote</h2>
#using (Html.BeginForm("CreateQuote", "Test", FormMethod.Post, new { #id = "createQuoteForm", #class = "form-horizontal", role = Model, enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.QuoteName)
<h3>Quote Lines</h3>
<table class="table master-detail-table" id="receiving-table">
<thead>
<tr>
<th>SKU</th>
<th>Description</th>
</tr>
</thead>
<tbody data-bind="foreach: QuoteLines">
<tr>
<td>
<input class='form-control' data-bind='value: $data.Sku, attr: { name: "QuoteLines[" + $index() + "].Sku" } ' type='text' readonly='readonly' />
</td>
<td>
<input class='form-control' data-bind='value: $data.Description, attr: { name: "QuoteLines[" + $index() + "].Description" } ' type='text' readonly='readonly' />
</td>
</tr>
<tr class="detail-row">
<td colspan="7">
<table class="table">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody data-bind="foreach: BomDetails">
<tr>
<td>
<input class='form-control' data-bind='value: $data.Name, attr: { name: "BomDetails[" + $index() + "].Name" } ' type='text' readonly='readonly' />
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h3>Add Sku from Db</h3>
<div class="row">
<div class="col-sm-2">
<input type="button" value="Add Sku" id="btnAddSku" class="btn btn-satcom-primary btn-wider" />
</div>
</div>
<h3>Submit</h3>
<div class="row">
<div class="col-sm-1">
<input type="submit" value="Submit" class="btn btn-satcom-primary btn-wider" id="btnSubmit" />
</div>
</div>
}
</div>
<script type="text/javascript">
$(function () {
quoteViewModel = new QuoteViewModel();
ko.applyBindings(quoteViewModel);
$('#btnAddSku').off().on('click', function () {
AddFromDb();
});
});
function QuoteViewModel() {
var self = this;
self.Id = ko.observable('#Model.Id');
self.QuoteName = ko.observable('#Model.QuoteName');
self.QuoteLines = ko.observableArray([]);
self.AddQuoteLine = function (sku, description, bomDetails) {
self.QuoteLines.push(new QuoteLineViewModel(sku, description, bomDetails));
}
}
function QuoteLineViewModel(sku, description, bomDetails) {
var self = this;
self.Sku = sku;
self.Description = description;
self.BomDetails = ko.observableArray([]);
$.each(bomDetails, function (index, item) {
self.BomDetails.push(new BomDetailViewModel(item.Name));
});
}
function BomDetailViewModel(name) {
var self = this;
self.Name = name;
}
function AddFromDb() {
$.ajax({
type: "GET",
url: '#Url.Action("AddSku", "Test")',
success: function (line) {
window.quoteViewModel.AddQuoteLine(line.Sku, line.Description, line.BomDetails);
}
});
}
I have tried so many things to get it populated but can't figure out where the problem lies, but I hope it is just something silly that I'm doing or not doing.
I have also tried using ko.mapping but I can't get that working either.

I managed to get this working so hopefully it will help somebody else in the future.
I removed the #Using (Html.BeginForm)
I changed the submit button to a normal button and added data-bind to a fucntion
<input type="button" value="Submit" class="btn btn-satcom-primary btn-wider" id="btnSubmit" data-bind="click:SaveToDatabase" />
The SaveToDatabase function:
self.SaveToDatabase = function () {
var dataToSend = ko.mapping.toJSON(self);
$.ajax({
type: "POST",
url: '#Url.Action("CreateQuote", "Test")',
contentType: 'application/json',
data: dataToSend,
success: function (data) {
},
error: function (err) {
console.log(err.responseText);
}
});
}
This correctly sends all the data to the controller.

Related

How to get items in cascade selectList after posting in ASP Core MVC

I have a cascade list for Country/Province/City and it works fine in the create and edit action except for one thing, it always becomes empty in the edit get, here is my code:
public class LocationController : Controller
{
public List<Country> countries = new List<Country>
{
new Country(){Id=1,Name="Country1"},
new Country(){Id=2,Name="Country2"}
};
public List<Province> provinces = new List<Province>()
{
new Province() { Id = 1,CountryId = 1,Name = "Province1"},
new Province() { Id = 2,CountryId = 2,Name = "Province2"},
};
public List<City> cities = new List<City>()
{
new City() { Id = 1,ProvinceId = 1,Name = "City1" },
new City() { Id = 2,ProvinceId = 2,Name = "City2" },
new City() { Id = 3,ProvinceId = 2,Name = "City3" },
};
public IActionResult Province(int value)
{
var l = provinces.Where(x => x.CountryId == value).ToList();
return Json(l);
}
public IActionResult City(int value)
{
var c = cities.Where(c => c.ProvinceId == value).ToList();
return Json(c);
}
}
the Edit view:
<div class="form-group row">
<div class="col-2">
<label asp-for="Country" class="col-form-label"></label>
</div>
<div class="col-sm-5">
<select id="CountryList" asp-for="Country" asp-items="#new LocationController().countries.Select(c=> new SelectListItem() {Text=c.Name,Value=c.Id.ToString() }).ToList() as IEnumerable<SelectListItem>" class="form-control">
<option selected disabled value="">--- Choose ---</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Province" class="col-form-label"></label>
</div>
<div class="col-sm-5">
<select id="ProvinceList" asp-for="Province" data-url="#Url.Action("Province","Location")" class="form-control">
<option selected disabled value="">--- Choose ---</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="City" class="col-form-label"></label>
</div>
<div class="col-sm-5">
<select id="CityList" asp-for="City" data-url="#Url.Action("City","Location")" class="form-control">
<option selected disabled value="">--- Choose ---</option>
</select>
</div>
</div>
This is the Javascript:
#section Scripts {
<script>
$(function () {
$("#CountryList").change(function () {
$("#CityList").empty();
var v = $(this).val();
var url = $("#ProvinceList").data("url") + '?value=' + v;
$.getJSON(url, function (data) {
$("#ProvinceList").empty();
$("#ProvinceList").append('<option selected disabled value="">--- Choose ---</option>');
$.each(data, function (i, item) {
$("#ProvinceList")
.append($("<option>").text(item.name).val(item.id));
});
});
});
$("#ProvinceList").change(function () {
var v = $(this).val();
var url = $("#CityList").data("url") + '?value=' + v;
$.getJSON(url, function (data) {
$("#CityList").empty();
$("#CityList").append('<option selected disabled value="">--- Choose ---</option>');
$.each(data, function (i, item) {
$("#CityList")
.append($("<option>").text(item.name).val(item.id));
});
});
});
});
$('#formId').submit(function () {
$('#CountryList option').val(function () {
return $(this).text();
});
$('#ProvinceList option').val(function () {
return $(this).text();
});
$('#CityList option').val(function () {
return $(this).text();
});
});
</script>
}
and of course in the get action I tried to get the user's location from the database, and its working in the get:
[HttpGet]
public async Task<IActionResult> Edit(string id)
{
var user = await _userManager.FindByIdAsync(id);
EditUserViewModel modelVM = new EditUserViewModel
{
Country = user.Country,
Region = user.Region,
City = user.City,
};
return View(modelVM);
}
but in the view the province/region and city are empty:
If I click update province and city will be null.
Here is a working demo about how to pass the selected item to the action:
Model:
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Province
{
public int Id { get; set; }
public string Name { get; set; }
public int CountryId { get; set; }
}
public class City
{
public int Id { get; set; }
public string Name { get; set; }
public int ProvinceId { get; set; }
}
Update:
It seems you want to edit one user and the edit view would display the user's default city,province and country.So I think your js is no need in edit view.
Here is a working demo like below:
Model:
public class UserProfile
{
public string Id { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Province { get; set; }
}
public class EditUserViewModel
{
public string City { get; set; }
public string Country { get; set; }
public string Province { get; set; }
}
Index.cshtml(display the user data):
#model IEnumerable<UserProfile>
<table>
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Country)
</th>
<th>
#Html.DisplayNameFor(model => model.Province)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Country)
</td>
<td>
#Html.DisplayFor(modelItem => item.Province)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.Id">Edit</a>
</td>
</tr>
}
</tbody>
</table>
Edit.cshtml:
#model EditUserViewModel
<form id="formId" asp-controller="Location" asp-action="Edit">
<div class="form-group row">
<div class="col-2">
<label asp-for="Country" class="col-form-label"></label>
</div>
<div class="col-sm-5">
//change here....
<select id="CountryList" asp-for="Country" asp-items="#ViewBag.Country" class="form-control">
<option selected disabled value="">--- Choose ---</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Province" class="col-form-label"></label>
</div>
<div class="col-sm-5">
//change here....
<select id="ProvinceList" asp-for="Province" asp-items="#ViewBag.Province" data-url="#Url.Action("Province","Location")" class="form-control">
<option selected disabled value="">--- Choose ---</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="City" class="col-form-label"></label>
</div>
<div class="col-sm-5">
//change here....
<select id="CityList" asp-for="City" asp-items="#ViewBag.City" data-url="#Url.Action("City","Location")" class="form-control">
<option selected disabled value="">--- Choose ---</option>
</select>
</div>
</div>
<input type="submit" value="aaa" />
</form>
#section Scripts
{
<script>
$(function () {
$("#CountryList").change(function () {
$("#CityList").empty();
var v = $(this).val();
var url = $("#ProvinceList").data("url") + '?value=' + v;
$.getJSON(url, function (data) {
$("#ProvinceList").empty();
$("#ProvinceList").append('<option selected disabled value="">--- اختر ---</option>');
$.each(data, function (i, item) {
$("#ProvinceList")
.append($("<option>").text(item.name).val(item.id));
});
});
});
$("#ProvinceList").change(function () {
var v = $(this).val();
var url = $("#CityList").data("url") + '?value=' + v;
$.getJSON(url, function (data) {
$("#CityList").empty();
$("#CityList").append('<option selected disabled value="">--- اختر ---</option>');
$.each(data, function (i, item) {
$("#CityList")
.append($("<option>").text(item.name).val(item.id));
});
});
});
});
$('#formId').submit(function () {
$('#CountryList option').val(function () {
return $(this).text();
});
$('#ProvinceList option').val(function () {
return $(this).text();
});
$('#CityList option').val(function () {
return $(this).text();
});
});
</script>
}
HomeController:
public class HomeController : Controller
{
private List<UserProfile> users = new List<UserProfile>()
{
new UserProfile(){Id="1",Province="1",Country="1",City="1"},
new UserProfile(){Id="2",Province="2",Country="2",City="3"},
};
public IActionResult Index()
{
return View(users);
}
[HttpGet]
public async Task<IActionResult> Edit(string id)
{
var user = users.Where(a => a.Id == id).FirstOrDefault();
ViewBag.Province = new SelectList(new LocationController().provinces,"Id","Name", user.Province);
ViewBag.City = new SelectList(new LocationController().cities,"Id","Name", user.City);
ViewBag.Country = new SelectList(new LocationController().countries,"Id","Name", user.Country);
EditUserViewModel modelVM = new EditUserViewModel
{
Country = user.Country,
Province = user.Province,
City = user.City,
};
return View(modelVM);
}
[HttpPost]
public IActionResult Edit(string city, string province, string country)
{
return RedirectToAction("Index");
}
}
Result:

Mvc 5 pagination using view model

Hi i am newbie to Mvc i have a json service which returns a list of walletstatementlogs based on fromdate and todate. I have a controller TopUpReqLogController every time when i hit the action index of the controller it will go to service and fetch the data and returns to view as Ipagedlist and genrates pagelinks. How do i prevent servicecall everytime in TopUpReqLogController index action i just want to load service data once and pass it to index and display data in pages using int ? page please suggest
public class WalletTopUpRequest
{
public string SlNo { get; set; }
public string Sequence { get; set; }
public string Merchant { get; set; }
public string CustomerCode { get; set; }
public string CustomerName { get; set; }
public string BankName { get; set; }
public string TransactionDate { get; set; }
public string Reference { get; set; }
public string Amount { get; set; }
public string ApprovalStatus { get; set; }
public string ApproveUser { get; set; }
public string ApprovalDate { get; set; }
public string RemarKs { get; set; }
}
public ViewResult Index(int? page)
{
int pageSize = 3;
int pageNumber = (page ?? 1);
List<WalletTopUpRequest> wallettoprq = new List<WalletTopUpRequest>();
if (page == null)
{
AgentBusiness business = new AgentBusiness();
var result = business.Topuprequestlog("99910011010", "99810001110", "jBurFDoD1UpNPzWd/BlK4hVpV8GF+0eQT+AfNxEHHDKMB25AHf6CVA==", "25052017000000", "01062017000000");
wallettoprq = result.wallettopuprequest.ToList();
var viewmodel = wallettoprq.ToPagedList(pageNumber, pageSize);
return View(viewmodel);
}
return View(wallettoprq.ToPagedList(pageNumber, pageSize));
}
#using PagedList;
#using PagedList.Mvc;
#model IPagedList<HaalMeer.MVC.Client.Models.WalletTopUpRequest>
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<html>
<head>
</head>
<body>
<div id="page-wrapper">
<div class="page-title-container">
#*<div class="container-fluid">*#
<div class="page-title pull-left">
<h2 class="entry-title">Topup Request Log</h2>
</div>
<ul class="breadcrumbs pull-right">
<li>Home</li>
<li class="active">Topup Request Log</li>
</ul>
</div>
</div>
<section id="content" class="gray-area">
<div class="container">
<div class="row">
<div class="col-md-3">
</div>
#using (Html.BeginForm("Index", "TopUpReqLog", FormMethod.Get))
{
<div class="col-md-3">
<div class="form-group">
<label>From Date</label>
<div class="datepicker-wrap blue">
#*<input type="text" name="date_from" class="input-text full-width" placeholder="mm/dd/yy" style="background-color: #fff" />*#
#Html.TextBox("Fromdate", ViewBag.fromdate as string, new { #class = "input-text full-width", #placeholder = "mm/dd/yyy",#style = "background-color: #fff" }) <br />
</div>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label>To Date</label>
<div class="datepicker-wrap blue">
#*<input type="text" name="date_from" class="input-text full-width" placeholder="mm/dd/yy" style="background-color: #fff" />*#
#Html.TextBox("Todate", ViewBag.todate as string, new { #class = "input-text full-width", #placeholder = "mm/dd/yyy", #style = "background-color: #fff" }) <br />
</div>
</div>
<button type="submit">Submit</button>
</div>
}
<div class="col-md-3">
</div>
</div>
<div class="row">
<div class="col-md-12 col-sm-12">
<div class="table-responsive">
<table class="table">
<tr class="info" style="text-align: center; font-weight: bold; color: #000">
<td class="col-md-1">Sl</td>
<td class="col-md-2">Date</td>
<td class="col-md-1">Bank Ref.</td>
<td class="col-md-1">Bank Name</td>
<td class="col-md-2">Remarks</td>
<td class="col-md-1">Amount</td>
<td class="col-md-1">Status</td>
<td class="col-md-2">Action Date</td>
</tr>
#foreach (var item in Model)
{
<tr>
<td class="hmcenter">#Html.DisplayFor(modelItem => item.SlNo)</td>
<td class="hmcenter">#Html.DisplayFor(modelItem => item.TransactionDate)</td>
<td class="hmcenter">#Html.DisplayFor(modelItem => item.Reference)</td>
<td class="hmcenter">#Html.DisplayFor(modelItem => item.BankName)</td>
<td class="hmleft">#Html.DisplayFor(modelItem => item.RemarKs)</td>
<td class="hmright">#Html.DisplayFor(modelItem => item.Amount) </td>
<td class="hmcenter">#Html.DisplayFor(modelItem => item.ApprovalStatus)</td>
<td class="hmcenter">#Html.DisplayFor(modelItem => item.ApprovalDate)</td>
</tr>
}
</table>
<br/>
Page #(Model.PageCount<Model.PageNumber? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("Index",new { page
}))
#*<div class="form-group">
<ul class="pagination">
<li>1</li>
<li class="active">2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>*#
</div>
</div>
</div>
</div>
</section>
So, from what I understand you want just make it work only on client side. If your model is not empty this code should work. If you want to load data as one result and make pagination on client side, then IPageList is not what you are looking for. Because, it is used only on the server side, and always returns ONE page of data to brake large results. You also can try to pass list of data to the view and turn it to IPageList result in the view and display each
page in tab, but is not a good practice. I would use datatables in this situation to make pagination only on the client side using regular data list:
https://datatables.net/.
Hint to improve current code:
Controller:
public ViewResult Index(int? page = 1)
{
AgentBusiness business = new AgentBusiness();
var result = business.Topuprequestlog("99910011010", "99810001110", "jBurFDoD1UpNPzWd/BlK4hVpV8GF+0eQT+AfNxEHHDKMB25AHf6CVA==", "25052017000000", "01062017000000");
return View(result.wallettopuprequest.ToPagedList(pageNumber, 3));
}
View:
#Html.PagedListPager(Model, page => Url.Action("Index", new { page }), PagedListRenderOptions.ClassicPlusFirstAndLast)
Below example shows the paging to be done at server side and Client Side :
Here is my Model :
public partial class Employee
{
public int Id { get; set; }
public string FName { get; set; }
public string Lname { get; set; }
}
Action:
public ActionResult Index(int? Page)
{
return View();
}
/// returns Partial View
public ActionResult _PartialIndex(int? Page)
{
return PartialView(db.Employees.ToList().ToPagedList(Page ?? 1, 10));
}
Views :
1.Index View :Index.cshtml
#{
ViewBag.Title = "Index";
}
<script src="https://cdn.jsdelivr.net/jquery.ajax.unobtrusive/3.2.4/jquery.unobtrusive-ajax.min.js"></script>
<script>
$(document).ready(function () {
$('#loading').show();
debugger;
var Page = '';
$.ajax({
url: '/Employees/_PartialIndex',
contentType: "application/json; charset=utf-8",
type: 'get',
datatype: 'html'
}).success(function (result) {
$('#main').html(result);
$('#loading').hide();
});
});
</script>
<h2>Index</h2>
<div class="col-md-8 col-md-offset-2">
<center>
<div id="loading" style="display:none; z-index:200; position:absolute; top:50%; left:45%;">
<img src="~/Content/loading.gif" />
</div>
</center>
<div id="main">
</div>
</div>
2.Partial View :_PartialIndex.cshtml
#using PagedList.Mvc
#using PagedList;
#model IPagedList<samplePaging.Models.Employee>
#{
ViewBag.Title = "Index";
}
<h2>Employee List</h2>
<table class="table">
<tr>
<th>
First Name
</th>
<th>
Last Name
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Lname)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
#Html.ActionLink("Details", "Details", new { id = item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</table>
<center>
#Html.PagedListPager(Model, page => Url.Action("_PartialIndex", new { page }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(new PagedListRenderOptions() { DisplayPageCountAndCurrentLocation = true }, new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "main", LoadingElementId = "loading" }))
</center>
If you want to do the paging at client side then follow below steps:
Action:
public ActionResult JsonIndex(int? Page)
{
return Json(db.Employees.ToList(), JsonRequestBehavior.AllowGet);
}
View:
#{
ViewBag.Title = "Index2";
}
<h2>Index2</h2>
<link rel="stylesheet" type="text/css" href="//cdn.datatables.net/1.10.10/css/jquery.dataTables.min.css">
<script type="text/javascript" language="javascript" src="//cdn.datatables.net/1.10.10/js/jquery.dataTables.min.js"></script>
<script>
$(document).ready(function () {
//Call EmpDetails jsonResult Method
$.getJSON("/Employees/JsonIndex",
function (json) {
var tr;
//Append each row to html table
for (var i = 0; i < json.length; i++) {
tr = $('<tr/>');
tr.append("<td>" + json[i].FName + "</td>");
tr.append("<td>" + json[i].LName + "</td>");
$('table').append(tr);
}
$('#EmpInfo').DataTable();
});
});
</script>
<hr />
<div class="form-horizontal">
<table id="EmpInfo" class="table table-bordered table-hover">
<thead>
<tr>
<th>Fname</th>
<th>LName</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
Hope this help you !

Unable to process binding "event: function(){return {change:flagSalesOrderItemAsEdited} }"

I am currently doing a Pluralsight course on Knockout and MVC (called Parent-Child Data with EF, MVC, Knockout, Ajax, and Validation) which I have been very impressed with, but suddenly I get this bug which has so far been a show stopper for me.
Not only is it a problem in my code, it is also a bug in the very code downloaded from Pluralsight that I saw working on their video!
So in the Edit Partial View I have:
<h2>#ViewBag.Title</h2>
<p data-bind="text: MessageToClient"></p>
<div>
<div class="form-group">
<label class="control-label" for="CustomerName">Customer Name:</label>
<input class="form-control" name="CustomerName" id="CustomerName"
data-bind="value: CustomerName, event: {change: flagSalesOrderAsEdited}"/>
</div>
<div class="form-group">
<label class="control-label" for="PONumber">P.O. Number:</label>
<input class="form-control" name="PONumber" id="PONumber"
data-bind="value: PONumber, event: {change: flagSalesOrderAsEdited}"/>
</div>
</div>
<table class="table table-stripe">
<tr>
<th>Product Code</th>
<th>Quantity</th>
<th>Unit Price</th>
<th><button data-bind="click: addSalesOrderItem" class="btn btn-info btn-xs">Add</button></th>
</tr>
<tbody data-bind="foreach: SalesOrderItems">
<tr>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: Quantity, event: {change: flagSalesOrderItemAsEdited}" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: UnitPrice, event: {change: flagSalesOrderItemAsEdited}" />
</td>
<td class="form-group">Delete</td>
</tr>
</tbody>
</table>
<p><button data-bind="click: save" class="btn btn-primary">Save</button></p>
<p>
« Back to List
</p>
and I apply bindings;
<script type="text/javascript">
var salesOrderViewModel = new SalesOrderViewModel(#Html.Raw(data));
ko.applyBindings(salesOrderViewModel);
</script>
In my javascript file I have
var ObjectState = {
Unchanged: 0,
Added: 1,
Modified: 2,
Deleted: 3
};
var salesOrderItemMapping = {
'SalesOrderItems': {
key: function(salesOrderItem) {
return ko.utils.unwrapObservable(salesOrderItem.salesOrderItemId);
},
create: function(options) {
return new SalesOrderViewModel(options.data);
}
}
};
SalesOrderItemViewModel = function(data) {
var self = this;
ko.mapping.fromJS(data, salesOrderItemMapping, self);
self.flagSalesOrderItemAsEdited = function() {
if (self.ObjectState() !== ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
return true;
};
}
SalesOrderViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, salesOrderItemMapping, self);
self.save = function() {
$.ajax({
url: "/Sales/Save",
type: "POST",
data: ko.toJSON(self),
contentType: "application/json",
success: function (data) {
if (data.salesOrderViewModel !== null) {
ko.mapping.fromJS(data.salesOrderViewModel, {}, self);
}
if (data.newLocation !== undefined && data.newLocation !== null) {
window.location.href = data.newLocation;
}
}
});
}
self.flagSalesOrderAsEdited = function () {
if (self.ObjectState() !== ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
return true;
}
The mappings are derived from the server side viewModels:
namespace SolutionName.Web.ViewModels
{
public class SalesOrderViewModel : IObjectWithState
{
public SalesOrderViewModel()
{
this.SalesOrderItems = new ListStack<SalesOrderItemViewModel>();
}
public int SalesOrderId { get; set; }
public string CustomerName { get; set; }
public string PONumber { get; set; }
public string MessageToClient { get; set; }
public ObjectState ObjectState { get; set; }
public List<SalesOrderItemViewModel> SalesOrderItems { get; set; }
}
}
and
namespace SolutionName.Web.ViewModels
{
public class SalesOrderItemViewModel : IObjectWithState
{
public int SalesOrderItemId { get; set; }
public string ProductCode { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public int SalesOrderId { get; set; }
public ObjectState ObjectState { get; set; }
}
}
The error occurs in the table where I have data-binded the flag field:
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
I get 'flagSalesOrderItemAsEdited' is undefined
And it falls over the in the knockout script.
Unable to process binding "foreach: function(){return SalesOrderItems }"
Message: Unable to process binding "event: function(){return {change:flagSalesOrderItemAsEdited} }"
Message: 'flagSalesOrderItemAsEdited' is undefined
ex.message = "Unable to process binding \"" + bindingKey + ": " + bindings[bindingKey] + "\"\nMessage: " + ex.message;
Line 3326 Exception
So how do I fix this?
EDIT
One suggested solution is to use $parent as a prefix in the HTML.
So I tried:
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: $parent.flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: Quantity, event: {change: $parent.flagSalesOrderItemAsEdited}" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: UnitPrice, event: {change: $parent.flagSalesOrderItemAsEdited}" />
</td>
This stopped the exception being thrown. However the method:
self.flagSalesOrderAsEdited = function () {
if (self.ObjectState() !== ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
was NOT invoked. It was as though the class it is in was not instantiated.
try the following.use $root while calling a function inside a loop
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: $root.flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
We can also use $parent which is the immeditely outside the current context.
More info on binding context

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>

error to get value from controller

i try to create new room, but roomTypeID always return 1, whats wrong with my code?
i can make a new room type, but i cant insert room facility in my database, because RoomType ID always return 1
this my code..
my controller
public ActionResult NewRoom()
{
ViewBag.hotel = _hotelService.GetByID(_HotelID).HotelName;
List<ShowEditRoomViewModel> showEditRoomViewModel = _roomTypeService.showNewRooms();
return View(showEditRoomViewModel.FirstOrDefault());
}
[HttpPost]
public ActionResult NewRoom(FormCollection typeRoom)
{
_roomTypeService.NewRoom(_HotelID, typeRoom["RoomTypeName"], typeRoom["RoomTypeDescription"]);
List<string> IDs = typeRoom["FacilityIDs"].Split(',').ToList();
List<int> FacilityIDs = new List<int>();
foreach (string ID in IDs)
{
FacilityIDs.Add(Convert.ToInt32(ID));
}
_roomTypeService.UpdateFacilityInRooms(FacilityIDs, Convert.ToInt32(typeRoom["RoomTypeID"]));
return NewRoom();
}
my service
public void UpdateFacilityInRooms(List<int> FacilityIDs, int RoomTypeID)
{
List<HotelRoomFacility> hotelRoomFacilities = _HotelRoomFacilityRopository.AsQueryable().Where(f => f.RoomTypeID == RoomTypeID).ToList();
foreach (int newRoomFacility in FacilityIDs)
{
if (hotelRoomFacilities.Where(h => h.RoomFacilityID == newRoomFacility).Count() == 0)
{
HotelRoomFacility facility = new HotelRoomFacility
{
RoomFacilityID = newRoomFacility,
RoomTypeID = RoomTypeID
};
_HotelRoomFacilityRopository.Add(facility);
}
}
_HotelRoomFacilityRopository.CommitChanges();
}
my view model
public class ShowEditRoomViewModel
{
public int RoomTypeID { get; set; }
public string RoomTypeName { get; set; }
public string RoomTypeDescription { get; set; }
public List<FaciliyInRoom> facilityinRoom { get; set; }
}
my view
#model XNet.Repository.Model.ShowEditRoomViewModel
#{
ViewBag.Title = "NewRoom";
}
<h2>New Room</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Isikan Data</legend>
<div>
#Html.Label("Hotel Name")
</div>
<div>
#ViewBag.hotel
</div>
<br />
<div>
#Html.HiddenFor(model => model.RoomTypeID)
</div>
<br />
<div>
#Html.Label("Room Type Name")
</div>
<div>
#Html.EditorFor(model => model.RoomTypeName)
#Html.ValidationMessageFor(model => model.RoomTypeName)
</div>
<br />
<div>
#Html.Label("Room Type Description")
</div>
<div>
#Html.TextAreaFor(model => model.RoomTypeDescription)
#Html.ValidationMessageFor(model => model.RoomTypeDescription)
</div>
<br />
<table>
<thead>
<tr>
<th>Facility Name</th>
<th> is available</th>
</tr>
</thead>
<tbody>
#foreach (var facility in Model.facilitiesInRoom)
{
<tr>
<td>
#(facility.RoomFacilityName)
</td>
<td style="text-align:center;">
<input type="checkbox" #(facility.RoomFacilityAvailable ? " checked=checked" : null) name="FacilityIDs" value="#facility.RoomFacilityID" />
</td>
</tr>
}
</tbody>
</table>
<br />
<p>
<input type="submit" value="Save" />
<input style="width:100px;" type="button" title="EditHotelDetail" value="Back to Detail" onclick="location.href='#Url.Action("Room", "Hotel") '" />
</p>
</fieldset>
}
My method
public List<ShowEditRoomViewModel> showNewRooms()
{
List<RoomType> roomTypes = (from d in _RoomTypeRepository.All()
select d).ToList();
List<ShowEditRoomViewModel> showEditRoomViewModel = new List<ShowEditRoomViewModel>();
foreach (RoomType roomType in roomTypes)
{
showEditRoomViewModel.Add(new ShowEditRoomViewModel
{
RoomTypeID = roomType.RoomTypeID,
facilitiesInRoom = LoadFacilityInRoom()
});
}
return showEditRoomViewModel;
}
can someone tell me, where is my mistake??
thanks
When you are inserting RoomtypeId in Database, you are using ExecuteNonQuery() method, It will always return 1 whenever you insert a new record in it,
If you are using stored procedure for inserting,you can use
select Scope_identity()
after insertion.

Resources