Knockout Nested List Post Null - asp.net-mvc

I'm trying integrate ko on one of my razor views. I've researched quite a bit yesterday but haven't been able to find a similar solution for my problem.
I have a record model:
public class Record
{
public Int Request Id {get;set;}
public string RecordName {get;set;}
public Person Person {get;set;}
public IList<Person> People { get; set; }
}
And a Person Model:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<Alias> Aliases { get; set; }
}
My View:
#model Record
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("PostRequest", "Request", FormMethod.Post))
{
#Html.LabelFor(m => m.Person.FirstName)
#Html.TextBoxFor(m => m.Person.FirstName)
<h5>People</h5>
<table>
<tbody data-bind="foreach: People">
<tr>
<td>#Html.LabelFor(m => m.Person.FirstName)</td>
<td><input type="text" data-bind="value: FirstName, attr: {name: 'People[' + $index() + '].FirstName'}" /></td>
<td>#Html.LabelFor(m => m.Person.LastName)</td>
<td><input type="text" data-bind="value: LastName, attr: {name: 'People[' + $index() + '].LastName'}" /></td>
</tr>
<tr>
<td><button id="removePerson" data-bind="click: $root.removePerson">Remove Person</button></td>
</tr>
<tr>
<td>
<button id="addAlias" data-bind="click: addAlias">Add Alias</button>
</td>
</tr>
<!-- ko foreach: Aliases -->
<tr>
<td>#Html.LabelFor(m => m.Alias.FirstNameAlias)</td>
<td><input type="text" data-bind="value: FirstNameAlias, attr: {name: 'Aliases[' + $index() + '].FirstNameAlias'}" /></td>
<td>#Html.LabelFor(m => m.Alias.LastNameAlias)</td>
<td><input type="text" data-bind="value: LastNameAlias, attr: {name: 'Aliases[' + $index() + '].LastNameAlias'}" /></td>
<td><button id="removeAlias" data-bind="click: $root.removeAlias">Remove Alias</button></td>
</tr>
<!-- /ko -->
</tbody>
</table>
<button id="addPerson" data-bind="click: addPerson">Add Person</button>
<button>Submit</button>
<input id="clickMe" type="button" value="clickme" onclick="submit();" />
}
And Script:
#section scripts
{
<script type="text/javascript">
$(function () {
var personItem = function () {
var self = this;
self.LastName = ko.observable();
self.FirstName = ko.observable();
self.Aliases = ko.observableArray();
};
var model = ko.mapping.fromJS(#Html.Raw(Model.ToJson()));
alert('The length of the array is ' + model.People().length);
alert('The first element is ' + model.People()[0].Aliases().length);
alert(ko.toJSON(model));
model.addPerson = function () {
model.People.push(new personItem());
};
model.removePerson = function (person) {
model.People.remove(person);
};
ko.applyBindings(model);
})
function submit() {
$.ajax({
url: '/Request/PostRequest',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(model),
success: function (status) {
alert(status);
}
});
};
</script>
}
The Controller action that GETs this view pre initializes some data for the record model, so the binding appears to be working properly, however when I post data, the data for Alias List comes back Null.
Controller:
public ActionResult CreateRequest(Record viewModel)
{
var list = new Record
{
People = new List<Person> {
new Person {FirstName = "My First Person", Aliases = new List<Alias> { new Alias{FirstNameAlias = "firstnamealias",LastNameAlias = "lastnamealias1"}},},
new Person {FirstName = "My Second Person", Aliases = new List<Alias> { new Alias{FirstNameAlias = "firstnamealias2", LastNameAlias ="lastnamealias2"}},}
},
Person = new Person()
{
FirstName = "me"
}
};
return View(list);
}
[HttpPost]
public JsonResult PostRequest(Record viewModel)
{
return Json(String.Format("'Success':'false','Error':'"));
}
I feel that I am missing something with ko.mapping. But so close, as the data populates from the GET controller action, but fails to POST.
SOLUTION
I updated my KO script, and the way I was rendering the ko data in the view. This has solved my problem.
Updated Script
<script>
var initialData = #Html.Raw(Serialize(Model.People));
var BackgroundModel = function(people) {
var self = this;
self.people = ko.mapping.fromJS(people);
self.addPerson = function() {
self.people.push({
FirstName: "",
LastName: "",
Aliases: ko.observableArray()
});
};
self.removePerson = function(person) {
self.people.remove(person);
};
self.addAlias = function(person) {
person.Aliases.push({
//todo
FirstNameAlias: "",
LastNameAlias: ""
});
};
self.removeAlias = function(Alias) {
$.each(self.people(), function() { this.Aliases.remove(Alias) })
};
self.save = function() {
self.lastSavedJson(JSON.stringify(ko.toJS(self.people), null, 2));
var subModel = JSON.stringify(ko.toJS(self));
$.ajax({
url: '/Request/PostRequest',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: subModel,
success: function (status) {
alert(status);
}
});
};
self.lastSavedJson = ko.observable("")
};
ko.applyBindings(new BackgroundModel(initialData));
</script>
Updated View
<div data-bind="foreach: people">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"> <a data-bind="attr:{href:'#collapseOne'+$index()}, text:FirstName() +' '+ LastName()" #*href="#collapse_One"*# class="accordion-toggle" data-toggle="collapse" data-parent="#accordion"></a> </h4>
</div>
<div data-bind="attr:{id:'collapseOne'+$index()}" class="panel-collapse collapse in" #*data-bind="attr:{id:_collapseId, class:_collapsedIn}"*#>
<div class="panel-body">
<table>
<tbody>
<tr>
<td>#Html.DisplayNameFor(x => x.Person.FirstName)</td>
<td>#Html.DisplayNameFor(x => x.Person.LastName)</td>
</tr>
<tr>
<td><input data-bind="value: FirstName, name: FirstName" /></td>
<td><input data-bind="value: LastName, name: LastName" /></td>
</tr>
<tr>
<td><a href='#' data-bind='click: $root.removePerson'>Remove Person</a></td>
</tr>
</tbody>
</table>
<!-- ko foreach: Aliases -->
<table>
<tbody>
<tr>
<td>#Html.DisplayNameFor(x => x.Alias.FirstNameAlias)</td>
<td>#Html.DisplayNameFor(x => x.Alias.LastNameAlias)</td>
</tr>
<tr>
<td><input data-bind="value: FirstNameAlias, name: FirstNameAlias" /></td>
<td><input data-bind="value: LastNameAlias, name: LastNameAlias" /></td>
</tr>
<tr>
<td><a href='#' data-bind='click: $root.removeAlias'>Remove Alias</a></td>
</tr>
</tbody>
</table>
<!-- /ko -->
<a href='#' data-bind='click: $root.addAlias'>Add Alias</a>
</div>
</div>
</div>
</div>

The variable model in your submit funtion is going to be undefined it's not the model you're expecting it to be.
Move the submit method to in to your model...
$(function () {
var personItem = function () {
var self = this;
self.LastName = ko.observable();
self.FirstName = ko.observable();
self.Aliases = ko.observableArray();
};
var model = ko.mapping.fromJS(#Html.Raw(Model.ToJson()));
alert('The length of the array is ' + model.People().length);
alert('The first element is ' + model.People()[0].Aliases().length);
alert(ko.toJSON(model));
model.addPerson = function () {
model.People().push(new personItem());
};
model.removePerson = function (person) {
model.People().remove(person);
};
model.submit = function() {
$.ajax({
url: '/Request/PostRequest',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(model),
success: function (status) {
alert(status);
}
});
};
ko.applyBindings(model);
});
and update the binding on your button to
<input id="clickMe" type="button" value="clickme" data-bind="click: submit" />
Also
Controller.Json expects an object to serialize rather than a string
[HttpPost]
public JsonResult PostRequest(Record viewModel)
{
return Json(String.Format("'Success':'false','Error':'"));
}
Should be:
[HttpPost]
public JsonResult PostRequest(Record viewModel)
{
return Json(new { success = false, error: String.Empty });
}
Update
Also noticed another issue with updating the model.People array
model.addPerson = function () {
model.People.push(new personItem());
};
model.removePerson = function (person) {
model.People.remove(person);
};
In these functions model.People should be model.People(), i.e.
model.addPerson = function () {
model.People().push(new personItem());
};
model.removePerson = function (person) {
model.People().remove(person);
};
Notice the () after model.People - included in the code block above.

Related

I am unable to POST a List<T> using knockout.js to my MVC ActionMethod?

In MVC, I am passing a ViewModel to my View page.
The ViewModel consists of a few properties and a List of connected boards, where is another ViewModel.
In the View, I create a view model and ensure they are observable and the list of boards is an observable array.
I use Ajax to load the boards depending on the selected value of a drop down, and bind it to a table.
It works pretty great except that the list of boards is not POST'ed back to my HttpPost action method. The other plain string or int variables POST back fine.
The two ViewModels:
public class ManufacturingJobcardViewModel
{
public int Id { get; set; }
public string SelectedDevice { get; set; }
public int Quantity { get; set; }
public List<ManufacturingBoardsDeviceModel> Boards { get; set; }
public string Customer { get; set; }
}
public class ManufacturingBoardsDeviceModel
{
public int Id { get; set; }
[Display(Name = "Serial Number")]
public string SerialNumber { get; set; }
public int Quantity { get; set; }
[Display(Name = "From")]
public int FromNumber { get; set; }
[Display(Name = "To")]
public int ToNumber { get; set; }
}
Relevant Part of the View:
#using (Html.BeginForm("CreateManufacturingGui", "Jobcard", FormMethod.Post, new { #id = "manufactureForm" }))
{
#Html.AntiForgeryToken()
<div>
<table class="table">
<tr>
<th>SerialNumber</th>
<th>Quantity</th>
<th>From</th>
<th>To</th>
</tr>
<tbody data-bind="foreach: Boards">
<tr>
<td data-bind="text: SerialNumber"></td>
<td>#Html.TextBox("quantity", "", new { data_bind = "value:Quantity", #class = "form-control", #type = "number", #min = 1 })</td>
<td data-bind="text: FromNumber"></td>
<td data-bind="text: ToNumber"></td>
</tr>
</tbody>
</table>
</div>
<div>
<input type="submit" value="Create" class="btn btn-primary btn-wider" />
</div>
}
<script type="text/javascript">
$(function () {
getBoards();
$('#Devices').change(function () {
getBoards();
});
window.viewModel = new CreateManufacturingViewModel();
ko.applyBindings(window.viewModel);
});
function Board(serialNumber, qty, from) {
var self = this;
self.Id = ko.observable();
self.SerialNumber = serialNumber;
self.Quantity = ko.observable(qty);
self.FromNumber = ko.observable(from);
self.ToNumber = ko.computed(function () {
return parseInt(self.FromNumber()) + parseInt(self.Quantity()) - 1;
}, this);
}
function CreateManufacturingViewModel() {
var self = this;
self.Customer = ko.observable();
self.Quantity = ko.observable();
self.Boards = ko.observableArray();
self.addBoard = function (serialNumber, qty, fromNumber) {
self.Boards.push(new Board(serialNumber, qty, fromNumber));
}
self.setQuantity = function () {
ko.utils.arrayForEach(this.boards(), function (board) {
board.quantity($('#quantity').val());
});
}
}
function getBoards() {
$.ajax({
type: "GET",
url: '#Url.Action("GetBoards", "Jobcard", null)',
data: { SelectedModel: $('#Devices').val() },
}).done(function (data) {
$(data).each(function (index, element) {
 window.viewModel.addBoard(element.SerialNumber, element.Quantity, element.FromNumber);
});
}).error(function (ex) {
alert("Error");
});
}
The code seems to work correctly as all the boards and the correct data is show, but when I click the POST button the Boards variable is null.
I have tried adding the boards in the ajax success function by using
window.viewModel.Boards.fromJS(data);
but that does not display any lines at all.
I am extremely new to knockout.js so any ideas would be greatly appreciated.
EDIT: This is what I am trying:
<tbody data-bind="foreach: Boards">
<tr>
<td data-bind="text: FromNumber">
<input data-bind="attr: { name: 'Boards[' + $index + '].FromNumber', value: FromNumber }" class="form-control" type="hidden" />
</td>
</tr>
</tbody>
I get the following error:
Uncaught Error: Syntax error, unrecognized expression: input[data-bind='attr: { name: 'Boards[' + $index + '].FromNumber', value: FromNumber }']
EDIT 2:
Got this working by changing my button from type submit to type button and calling my ActionMethod by Ajax:
$('#btnCreate').off().on("click", function () {
$.ajax({
type: "POST",
contentType: 'application/json; charset=utf-8',
url: '#Url.Action("CreateManufacturingGui", "Jobcard", null)',
data: ko.toJSON(viewModel),
}).success(function (data) {
window.location.replace('#Url.Action("Home", "Jobcard", null)');
});
});
I would like to get it working by direct submit but for now this will suffice.
EDIT 3:
Got it working by doing this:
<tbody data-bind="foreach: Boards">
<tr>
<td>
<input data-bind="text: Id, attr: { name: 'Boards[' + $index() + '].Id' }"
class="form-control" type="hidden" />
</td>
<td>
<input data-bind="value: SerialNumber, attr: { name: 'Boards[' + $index() + '].SerialNumber' }"
class="form-control" type="text" readonly="readonly" />
</td>
<td>
<input data-bind="value: Quantity, attr: { name: 'Boards[' + $index() +'].Quantity' }"
class="form-control" type="number" min=1 />
</td>
<td>
<input data-bind="value: FromNumber, attr: { name: 'Boards[' + $index() +'].FromNumber' }"
class="form-control" type="text" />
</td>
<td>
<input data-bind="value: ToNumber, attr: { name: 'Boards[' + $index() +'].ToNumber' }"
class="form-control" type="text"/>
</td>
</tr>
</tbody>
I think the previous answer has the correct approach but the bindings aren't quite right. You need the use the attr binding like this
<input data-bind="attr: { name: 'Boards[' + $index + '].Id', value: Id }" class="form-control" type="hidden" />
<input data-bind="attr: { name: 'Boards[' + $index + '].Quantity', value: Quantity }" class="form-control" type="hidden" />
It's due to name attribute in input filed, intend of #Html.TextBox use input HTML tag with name attribute data binding with proper index.
For List during HttpPost back should be like name="Boards[RowIndex].PropertyName".
Remove data-bind="text: FromNumber" from "td" tag.
Note: "name" attribute is mandatory for Form Submit. Index value should start form Zero.
<input data-bind="value: Id, attr: { name: 'Boards[' + $index() + '].Id' }"
class="form-control" type="hidden" />
<input data-bind="value: Quantity, attr: { name: 'Boards[' + $index() +
'].Quantity' }" class = "form-control" type="number" min = 1 />
If Borads.length is 2 and required Id and Quantity field alone during HttpPost then following output after KnockoutJs binding should be like,
<input name="Boards[0].Id" type="hidden" ... />
<input name="Boards[0].Quantity" type="number" ... />
<input name="Boards[1].Id" type="hidden" ... />
<input name="Boards[1].Quantity" type="number" ... />
Demo
Please let us know in case of any issues / concerns.

Foreach Checked and click event knockout

I would like to add the values of 'Cost' based on checkbox checked event on knock out.
<tbody data-bind="foreach: $root.searchedResults">
<!-- ko if: Id-->
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Cost"></td>
<td><input type="checkbox" data-bind="checked: $root.searchedResults().length > 0, click: $root.Hospital_click"></td>
</tr>
<!-- /ko -->
</tbody>
And the sum of cost should be displayed in
<div data-bind="text: ko.toJSON(SumofItems)"></div>
The click event is not updating my sum based selection. Can someone help?
self.Hospital_click = function () {
//Suggest
}
My View model is:
var SearchModel = function () {
var self = this;
self.searchFromDate = ko.observable('01/01/2014');
self.searchToDate = ko.observable('01/01/2016');
self.searchedResults = ko.observableArray([]);
self.search = function () {
var model = {
BeginDate: self.searchFromDate(),
EndDate: self.searchToDate()
};
$.ajax({
url: '#Url.Action("SearchSpend", "Analysis", new { Area = "Portal" })',
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: "json",
data: ko.toJSON(model),
success: function (response) {
self.searchedResults(response);
}
});
};
You could add a new observableArray with the id's of the selected results (selectedResults). The viewmodel could be:
function Result(id,cost) {
this.Id = id;
this.Cost = cost;
}
function viewModel() {
var self = this;
self.searchedResults = ko.observableArray([]);
self.selectedResults = ko.observableArray([])
self.totalCost = ko.computed(function() {
let total = 0;
for(let p = 0; p < self.searchedResults().length; ++p)
{
if (self.selectedResults.indexOf(self.searchedResults()[p].Id)>=0){
total += self.searchedResults()[p].Cost;
}
}
return total;
});
};
totalCost will return the sum of the Cost field for all selected results.
And the view could be something like this:
<table data-bind="foreach: searchedResults">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Cost"></td>
<td><input type="checkbox" value="" data-bind="checkedValue: Id, checked: $parent.selectedResults"></td>
</tr>
</table>
<div data-bind="text: totalCost"></div>
totalCost is a computed value that returns de sum of the Cost for only the selected results.
Here is a fiddle.
In the fiddle, the data comes from the array listOfResults. In your case, this comes from the success function (Ajax request); in this function you also needs to clean the selectedResults.

Can not save data 2nd time knockout mvc

I am new in knockout and asp.net mvc both.
I am trying to Insert update delete data in database with knockout. My knockout model is
function City(data) {
this.CityId = ko.observable(data.CityId);
this.CityName = ko.observable(data.CityName);
}
function CityViewModel() {
var self = this;
self.Citys = ko.observableArray([]);
self.CityId = ko.observable();
self.CityName = ko.observable();
self.selectedCity = ko.observable();
// self.City = ko.observable();
selectCity = function (item) {
self.selectedCity(item);
}
//load
loadCitys = function () {
$.getJSON("/Admin/GetCitys", {}, function (result) {
var mappedCitys = ko.utils.arrayMap(result.Data, function (item) {
return new City(item);
});
self.Citys([]);
self.Citys.push.apply(self.Citys, mappedCitys);
});
}
//edit
EditCity = function (item) {
//what need to do here
// is it possible to fill the hidden fild and the text box ??
}
//save
SaveCity = function (item) {
City = new City(item);
$.ajax({
type: "POST",
url: "/Admin/SaveCity",
data: ko.toJSON({ City: City }),
contentType: "application/json",
success: function (result) {
if (result.Edit) {
City.CityId = result.Success;
City.CityName = item.CityName;
self.Citys.push(City);
toastr.success('City Information Save Successfully', 'Success');
}
else if (result.Edit == false) {
toastr.success('City Information Update Successfully', 'Success');
}
else {
toastr.error('There is an error please try again later', 'Errror');
}
}
});
}
//delete
DeleteCity = function (City) {
$.ajax("/Admin/DeleteCity", {
data: ko.toJSON({ CityId: City.CityId }),
type: "POST", contentType: "application/json",
success: function (result) {
if (result.Success) {
self.Citys.remove(City);
toastr.success('City Remove Successfully', 'Success');
}
else {
alert("Error..");
}
}
});
}
}
(function () {
ko.applyBindings(new CityViewModel, document.getElementById("Citys"));
loadCitys();
});
And my Html codes are
<table class="table table-striped">
<thead>
<tr>
<th>City Id</th>
<th>City Name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: $root.Citys">
<tr data-bind="click: selectCity">
<td><span data-bind="text:CityId"></span></td>
<td><span data-bind="text:CityName"></span></td>
<td><button data-bind="click: EditCity" class="btn btn-primary">Edit</button></td>
<td><button data-bind="click: DeleteCity" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<fieldset>
<legend>Add new / Edit City</legend>
<label>City name</label>
<input type="hidden" data-bind="value: CityId" />
<input type="text" data-bind="value: CityName" placeholder="Type city name…">
<button type="submit" data-bind="click: SaveCity" class="btn">Submit</button>
</fieldset>
With this codes I can get data form database display them successfully in my view file,
I delete the data from database, and I also can Insert data to database but here is a problem I can save data only 1st time when I change the textbox value (without page refresh) and try to save city information then it say (in Firebug on my javascript code):
TypeError: City is not a constructor
City = new City(item);
My question is what have I done wrong in this codes, and I am trying to fill the textbox and the hidden field when edit button click, how can I do this?
Thanks in advance.
There are a number of faults with your javascript, including:
The methods on your viewmodel, such as SaveCity, DeleteCity, EditCity are all being defined without the 'this/self' prefixes, therefore they are being added to the global namespace.
In your SaveCity method, your are assigning a new instance of the City class to a variable called 'City', therefore destroying the City class. It will work the first time, but any other attempts to create an instance of a City will yield an exception.
Anyway, this should be a working version of your script and HTML without the ajax stuff. You will need to adapt that yourself. I have also created a working JsFiddle here..
function City(data) {
this.CityId = ko.observable(data.CityId);
this.CityName = ko.observable(data.CityName);
}
function CityViewModel() {
var self = this;
self.Citys = ko.observableArray([]);
self.SelectedCity = ko.observable();
self.EditingCity = ko.observable(new City({CityId: null, CityName: ''}));
self.EditCity = function(city){
self.EditingCity(new City(ko.toJSON(city)));
};
//load
self.loadCitys = function () {
self.Citys().push(new City({CityId: '1245', CityName: 'Los Angeles'}));
self.Citys().push(new City({CityId: '45678', CityName: 'San Diego'}));
};
//save
self.SaveCity = function () {
var city = self.EditingCity();
if(city.CityId()){
var cityIndex;
for(var i = 0; i < self.Citys().length; i++) {
if(self.Citys()[i].CityId() === city.CityId()) {
cityIndex = i;
break;
}
}
self.Citys[cityIndex] = city;
}
else{
city.CityId(Math.floor((Math.random()*1000000)+1));
self.Citys.push(city);
}
self.EditingCity(new City({CityId: null, CityName: ''}));
}
//delete
self.DeleteCity = function (city) {
self.Citys.remove(city);
};
}
var viewModel = new CityViewModel();
viewModel.loadCitys();
ko.applyBindings(viewModel, document.getElementById("Citys"));
HTML
<table class="table table-striped">
<thead>
<tr>
<th>City Id</th>
<th>City Name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Citys">
<tr data-bind="click: $root.SelectedCity">
<td><span data-bind="text:CityId"></span></td>
<td><span data-bind="text:CityName"></span></td>
<td><button data-bind="click: $root.EditCity" class="btn btn-primary">Edit</button></td>
<td><button data-bind="click: $root.DeleteCity" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<fieldset data-bind='with:EditingCity'>
<legend>Add new / Edit City</legend>
<label>City name</label>
<input type="hidden" data-bind="value: CityId" />
<input type="text" data-bind="value: CityName" placeholder="Type city name" />
<button type="submit" data-bind="click: $root.SaveCity" class="btn">Submit</button>
</fieldset>

MVC 4(Razor) KnockoutJS & DataTable

I am trying to show a DataTable in HTML table through knockout binding...I dont know where i am missing or wrong:
My Controller:
public JsonResult GetEmployees()
{
BAL.Employee dbProvider = new BAL.Employee();
DataTable dataTable = dbProvider.ShowEmployeeDetails();
List<Model.Employee> objExerciseList = new List<Model.Employee>();
foreach (DataRow dataRow in dataTable.Rows)
{
Model.Employee objExercise = new Model.Employee();
objExercise.EmployeeCode = dataRow["EmpCode"].ToString();
objExercise.EmployeeName = dataRow["EmpName"].ToString();
objExerciseList.Add(objExercise);
}
return Json(objExerciseList, JsonRequestBehavior.AllowGet);
}
My Model:
public class Employee
{
private string employeeCode;
private string employeeName;
public int ID { get; set; }
[Required(ErrorMessage="Employee Code is Required")]
public string EmployeeCode
{
get
{
return employeeCode;
}
set
{
employeeCode = value;
}
}
[Required(ErrorMessage = "Employee Name is Required")]
public string EmployeeName
{
get
{
return employeeName;
}
set
{
employeeName = value;
}
}
}
This is my ViewModel Code:
#{
ViewBag.Title = "Exercise9";
Layout = "../Shared/Master.cshtml";
}
<html>
<head>
<title>KO</title>
<script src="../../Scripts/jquery-1.6.2.js" type="text/javascript"></script>
<script src="../../Scripts/knockout-2.2.1.js" type="text/javascript"></script>
<script src="../../Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
<script src="../../Scripts/json2.js" type="text/javascript"></script>
</head>
<body>
<form action="" method="get">
<div style="width: 990px; background-color: White; height: 710px;">
<table id="tbllist" align="center" style="border:5px #fff solid;">
<tr>
<td colspan="6">
<h2>
Employee List</h2>
</td>
</tr>
<tr>
<td colspan="6" style="padding: 0px;">
<div id="title_p">
Listing</div>
</td>
</tr>
<tr>
<th align="left">
Employee Code
</th>
<th align="left">
Employee Name
</th>
</tr>
<tbody data-bind="foreach: Employees">
<tr style="border-bottom: 1px solid #000000;">
<td>
<span data-bind="text: EmployeeCode"></span>
</td>
<td>
<span data-bind="text: EmployeeName"></span>
</td>
</tr>
</tbody>
</table>
</div>
</form>
<script type="text/javascript">
var EmpViewModel = function () {
//Make the self as 'this' reference
var self = this;
//Declare observable which will be bind with UI
self.EmployeeCode= ko.observable("0");
self.EmployeeName= ko.observable("");
//The Object which stored data entered in the observables
var EmpData = {
EmpCode:self.EmployeeCode,
EmpName: self.EmployeeName
};
//Declare an ObservableArray for Storing the JSON Response
self.Employees = ko.observableArray([]);
GetEmployees(); //Call the Function which gets all records using ajax call
//Function to Read All Employees
function GetEmployees() {
//Ajax Call Get All Employee Records
$.ajax({
type: "GET",
url: "/Exercise/GetEmployees/",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
self.Employees(data); //Put the response in ObservableArray
},
error: function (error) {
alert(error.status + "<--and--> " + error.statusText);
}
});
//Ends Here
}
};
ko.applyBindings(new EmpViewModel());
</script>
Please help me...THANKS IN ADVANCE....!!
Give it a try to convert from Json to observable JavaScript model as follow :
$.ajax({
type: "GET",
url: "/Exercise/GetEmployees/",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
ko.mapping.fromJS(data, {}, self.Employees);
},
error: function (error) {
alert(error.status + "<--and--> " + error.statusText);
}
});

Child JSON object undefined when its not

I have an array of JSON objects in the page source that all work except a child object (category).
Here is the code in cshtml:
<script type="text/javascript">
var initialData = #Html.Raw(Json.Encode(ViewBag.OfferItems));
</script>
Here is the resulting page source:
<script type="text/javascript">
var initialData = [{"Id":1,"Name":"Item1","ProductVariantLinks":[{"category":{"Id":2,"Name":"Basic Pizza","Products":null},"product":{"Id":1,"Name":"Margherita","Description":null,"ProductVariants":null},"productVariant":{"Id":1,"Name":"10 inch"}},{"category":{"Id":2,"Name":"Basic Pizza","Products":null},"product":{"Id":2,"Name":"Zeno","Description":null,"ProductVariants":null},"productVariant":{"Id":4,"Name":"8 inch"}}]},{"Id":2,"Name":"Item2","ProductVariantLinks":[]}];
</script>
As far as I can tell category is there and contains properties, but it appears as undefined in IE's debugger.
Is there something I'm missing?
P.S. the JSON is valid.
Update
I'm using knockoutjs and category is in inialdata until it does ko.applybindings. I'm not sure why it would do this, code below:
/// <reference path="jquery-1.5.1.js" />
/// <reference path="knockout-2.0.0.js" />
var ProductVariantLink = function() {
var self = this;
self.category = ko.observable();
self.product = ko.observable();
self.productVariant = ko.observable();
// Whenever the category changes, reset the product selection
self.category.subscribe(function() {
self.product(undefined);
self.productVariant(undefined);
});
};
var OfferItem = function() {
var self = this;
self.Name = new String();
self.ProductVariants = new Array();
};
var SpecialOfferItemModel = function (specialOfferItems) {
var self = this;
self.specialOfferItems = ko.observableArray(ko.utils.arrayMap(specialOfferItems, function (specialOfferItem) {
return { Id: specialOfferItem.Id, Name: specialOfferItem.Name, ProductVariants: ko.observableArray(specialOfferItem.ProductVariantLinks) };
}));
self.addSpecialOfferItem = function () {
self.specialOfferItems.push({
Id: "",
Name: "",
ProductVariants: ko.observableArray()
});
};
self.removeSpecialOfferItem = function (specialOfferItem) {
self.specialOfferItems.remove(specialOfferItem);
};
self.addProductVariant = function (specialOfferItem) {
specialOfferItem.ProductVariants.push(new ProductVariantLink());
};
self.removeProductVariant = function (ProductVariant) {
$.each(self.specialOfferItems(), function () { this.ProductVariants.remove(ProductVariant) })
};
self.save = function () {
var OfferItems = new Array();
$.each(self.specialOfferItems(),
function () {
var item = this;
var offer = new OfferItem();
offer.Name = item.Name;
$.each(item.ProductVariants(),
function () {
offer.ProductVariants.push(this.ProductVariant);
});
OfferItems.push(offer);
});
self.lastSavedJson(JSON.stringify(ko.toJS(self.specialOfferItems()), null, 2));
return false;
};
self.lastSavedJson = ko.observable("");
};
var model = new SpecialOfferItemModel(initialData);
ko.applyBindings(model);
$(function () {
$('#myForm').submit(function () {
model.save();
});
});
<table class="specialOfferItemsEditor">
<tr>
<th>
</th>
<th>
Name
</th>
<th>
ProductVariants
</th>
</tr>
<tbody data-bind="foreach: specialOfferItems">
<tr>
<td>
<div>
Delete</div>
</td>
<td>
<input data-bind="value: Name" />
</td>
<td>
<table>
<tr>
<th>
Category
</th>
<th>
Product
</th>
<th>
ProductVariant
</th>
</tr>
<tbody data-bind="foreach: ProductVariants">
<tr>
<td>
<select data-bind='options: ProductCategories, optionsText: "Name", optionsCaption: "Select...", value: category, uniqueName: true'>
</select>
</td>
<td data-bind="with: category">
<select data-bind='options: Products, optionsText: "Name", optionsCaption: "Select...", value: $parent.product, uniqueName: true' >
</select>
</td>
<td data-bind="with: product">
<select data-bind='options: ProductVariants, optionsText: "Name", optionsCaption: "Select...", value: $parent.ProductVariant, uniqueName: true'
>
</select>
</td>
<td>
<a href='#' data-bind='click: $root.removeProductVariant'>Delete</a>
</td>
</tr>
</tbody>
</table>
<a href='#' data-bind='click: $root.addProductVariant'>Add Product Variant</a>
</td>
</tr>
</tbody>
</table>
The JSON isn't coming in as you're expecting. I assigned the JSON string you provided above and my IE debugger was able to find 'category' without any issues.
Screenshot: https://i.stack.imgur.com/cI60U.jpg
Try to console.log (or alert) JSON.stringify(initialData);

Resources