Access jQuery Object from inside a custom binding handler - javascript

I am trying to access a jQuery Object, which happens to be an input text box to do some validations and adding date picker but unfortunately I am unable to do so.
I am working with KnockoutJS.
I wanted to access the 'input' and call datepicker() on it if 'Datatype' happens to be a 'Date' or a 'Datetime'. But whenever I try to search using .closest('td').next('td').html(), I get a null (from inside the custom bind). Trying this so that I can call the datetimepicker constructor on the input based on the datatype 'td'.
Below is the fiddle that I am trying to work with
JsFiddle: http://jsfiddle.net/sourabhtewari/nkkt88v2/2/
var format = function (str, col) {
col = typeof col === 'object' ? col : Array.prototype.slice.call(arguments, 1);
return str.replace(/\{\{|\}\}|\{(\w+)\}/g, function (m, n) {
if (m == "{{") {
return "{";
}
if (m == "}}") {
return "}";
}
return col[n];
});
};
var data = ko.observableArray([{
paramKey: ko.observable('keyName'),
paramValue: ko.observable('Test1'),
dataType: ko.observable('Date')
}]);
var ParamConstr = function (key, value, dataType) {
return {
ParamKey: ko.observable(key),
ParamValue: ko.observable(value),
DataType: ko.observable(dataType)
};
};
var my = my || {};
function Generator() { };
Generator.prototype.rand = Math.floor(Math.random() * 26) + Date.now();
Generator.prototype.getId = function () {
return this.rand++;
};
var idGen = new Generator();
//var ParamData = ko.observableArray([]);
my.viewModel = {
ParamData : ko.observableArray([]),
addParam: function () {
this.ParamData.push(new ParamConstr("$$" + "key1", "value1","Date"));
}};
ko.bindingHandlers.hidden = {
update: function (element, valueAccessor) {
ko.bindingHandlers.visible.update(element, function () { return !ko.utils.unwrapObservable(valueAccessor()); });
}
};
ko.bindingHandlers.clickToEdit = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
var observable = valueAccessor(),
link = document.createElement("a"),
input = document.createElement("input");
var id = idGen.getId();
input.setAttribute("id", id);
element.appendChild(link);
element.appendChild(input);
console.log(document.getElementById(id).id);
var dataType = $(format("#{0}", id)).parent().next('td').html();
//.parent().next().html();
//.closest('td').next('td').html().toString();
console.log(dataType);
switch ($(format("#{0}", id)).parent().next('td').html()) {
case "Datetime":
$(format("#{0}", id)).datetimepicker();
break;
case "Date":
$(format("#{0}", id)).datetimepicker({
timepicker: false,
format: 'Y/m/d'
});
break;
}
observable.editing = ko.observable(false);
ko.applyBindingsToNode(link, {
text: observable,
hidden: observable.editing,
click: observable.editing.bind(null, true)
});
ko.applyBindingsToNode(input, {
value: observable,
visible: observable.editing,
hasfocus: observable.editing,
event: {
keyup: function (data, event) {
//if user hits enter, set editing to false, which makes field lose focus
if (event.keyCode === 13) {
input.blur();
observable.editing(false);
return false;
}
//if user hits escape, push the current observable value back to the field, then set editing to false
else if (event.keyCode === 27) {
observable.valueHasMutated();
observable.editing(false);
return false;
}
}
}
});
}
};
ko.applyBindings(my.viewModel);
html:
<div>
<button id="InputSubmitBtn" type="submit" class="btn btn-custom" data-bind="click: addParam"><span class="glyphicon glyphicon-plus-sign"></span> Add</button>
</div>
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Value</th>
<th>Data type</th>
</tr>
</thead>
<tbody data-bind="foreach: ParamData">
<tr>
<td data-bind="text: ParamKey" style="width: 20%"></td>
<td data-bind="clickToEdit: ParamValue" data-text="" style="width: 20%"></td>
<td data-bind="text: DataType" style="width: 20%"></td>
</tr>
</tbody>
</table>
Any help as to how I can fix this would be great!

So it seems you got an answer from G_S.
If anyone is wondering what was the right selector, var dataType = $(element).next().text(); does the job perfectly. You couldn't access any data because at the time of the call, it wasn't there yet. You could try var dataType = $(element).prev().text(); and see that you can retrieve $$key1.
Fiddle
Anyway, as you got answered, I didn't dig further on how to resolve this, have a good day :)

Related

Adding an Item from a string to insert into the database table

I am trying to add an item from a string to insert into a table along with the other information. I have followed a tutorial for a shopping cart and I am in need of adding the total cost to the record. I have the item in the array and have added what I believe to be the items I need. What I am having an issue with is implementing it in the controller side of the project. Below is the code I have, I am not 100% sure if what I added for the total price is correct. This should be in decimal form. thank for your help.
Shopping Cart CSHTML page. This is not the whole page just relevant parts.
using (Html.BeginForm("AddOrder", "Parts", new { id = "f" }))
{
<table id="tableOrder" class="table table-hover">
<tr>
<th>Part Number</th>
<th>Unit Price</th>
<th>Qty Selected</th>
<th>Description</th>
<th>Total Price</th>
</tr>
#foreach (var parts in Model)
{
<tr>
<td>#parts.Material</td>
<td>#string.Format("{0:C2}", parts.SellingPrice)</td>
<td>#parts.QtySelected</td>
<td>#parts.Description</td>
<td>#string.Format("{0:C2}", (parts.SellingPrice * parts.QtySelected))</td>
</tr>
totalOrder += (parts.QtySelected * parts.SellingPrice);
#Html.HiddenFor(p => parts.Material)
#Html.HiddenFor(p => parts.QtySelected)
}
</table>
<!-- TOTAL PRICE-->
<h4 style="margin-left: 66%;">Total : <span class="label label-info">#string.Format("{0:C2}", totalOrder)</span></h4>
#Html.HiddenFor(p => totalOrder)
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="close" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="SaveOrder" data-toggle="modal" data-target="#additionalInfo">Save Order</button>
</div> <!-- MODAL FOOTER-->
}
Java Script portion.
$("#SaveOrder").click(function () {
var $form = $(this).closest('form');
var dataPart = $form.serializeArray();
console.log(dataPart);
var arrIdPart = [];
var arrQtyPart = [];
var totalPrice = [];
for (i = 0; i < dataPart.length; i++)
{
if (dataPart[i].name == 'parts.Material')
{
arrIdPart.push(dataPart[i].value);
}
else if (dataPart[i].name == 'parts.QtySelected')
{
arrQtyPart.push(dataPart[i].value);
}
else if (dataPart[i].name == 'totalOrder')
{
totalPrice.push(dataPart[i].value);
}
}
$.ajax({
type: 'POST',
url: '#Url.Action("AddOrder", "Parts")',
data: { arrIdPart, arrQtyPart },
success: function (response) {
if(response.data == true)
{
alert("Order has saved successfully ");
}
else
{
alert("Order did not save successfully ! ");
}
},
error: function (error) {
alert("Order did not collect data successfully ! ");
}
});
});
Here is the controller action. I have added the parts to all of these that have to do with totalPrice. The issue is how to implement it in the record add to customer.
[HttpPost]
public ActionResult AddOrder(string[] arrIdPart, int[] arrQtyPart, decimal[] totalPrice)
{
int countPart = arrIdPart.Length;
int CompanyId = (int)Session["CompanyId"];
bool statusTran = false;
EDIT - added
decimal totPrice = totalPrice.Length;
CustomerEntities _context = new CustomerEntities();
using (DbContextTransaction dbTran = _context.Database.BeginTransaction())
{
try
{
CompanyNames customer = _context.CompanyNames.Find(CompanyId);
if (customer != null)
{
EDIT - Changed this
customer.Ordered.Add(new Orders { OrderDate = DateTime.Now, TotalPrice = totPrice });
}
Orders orderSelected = customer.Ordered.LastOrDefault();
if (orderSelected != null)
{
for (int i = 0; i < countPart; i++)
{
Parts selectedPart = _context.Parts.Find(arrIdPart[i]);
orderSelected.OrderDetail.Add(new OrderDetails { Parts = selectedPart, Quantity = arrQtyPart[i] });
}
}
//Save Changes
int countResult = _context.SaveChanges();
//Commit Transaction
dbTran.Commit();
if (countPart > 0)
{
statusTran = true;
partsList.Clear();
}
}
catch (Exception)
{
dbTran.Rollback();
}
}
return Json(new { data = statusTran }, JsonRequestBehavior.AllowGet);
}
}
However I am not getting anything for totalOrder in the script. Says its null. But in my 'console.log(dataPart);' In the console it is there with the name totalOrder.
Not saying it's the final answer but I can't put multiline code in a comment..
I wish to point out that you don't post the total price you calculated, to the server:
type: 'POST',
url: '#Url.Action("AddOrder", "Parts")',
data: { arrIdPart, arrQtyPart }, //look, no totalprice data? You did all those efforts to calc it and then did nothing with it?
success: function (response) {
if(response.data == true)
totalPrice should probably be called arrSubtotalPart by the way, for consistency..
Warning: The server receives the items and the quantities, it should work the price out from that rather than let the end user dictate how much he wants to pay for an order by manipulating the JavaScript

Knockout JS binding on computed observable does not work

I am trying to add a new field to an asp.net MVC 5 website popup screen that uses Entity Framework 6 code first, Typescript and Knockout JS for databinding. I did not write this website. I have been making changes to it for a few months. The original programmer is no longer with the company. I have never worked with these technologies previously.
The new field is the result of a web service call. The web method does return results. However, the value is not displayed on the screen. I script runs and displays all the other data. The deferred call to the web service returns after the page displays. I will provide the markup and view model code. Any advice is greatly appreciated.
Below is the computed property that the HTML is bound to:
this.PredictedValue = ko.pureComputed(() => {
var age = "";
var race = "";
var height = "";
var studyId = this.Session().Study.PftCentralStudyId();
var predictedSetName;
var predictedSetId;
var gender;
if (this.StudyTestParameter().HasPredictedValues() == true) {
ko.utils.arrayForEach(this.Session().Study.StudyTestTypePredictedSets(),(item: Bll.TestTypePredictedSetVm) => {
if (String(item.TestType().Name()) == this.StudyTestParameter().TestType().Name())
predictedSetId = item.PredictedSetId();
});
if (predictedSetId == 0) {
return "";
}
else {
var match = ko.utils.arrayFirst(this.Session().PftCentralStudyPredictedSets(),(item: Bll.PftCentralPredictedSetsVm) => {
return String(item.Id) == String(predictedSetId)
});
predictedSetName = match.Name;
ko.utils.arrayForEach(this.Session().SessionValues(),(item: SessionValueVm) => {
if (String(item.StudySessionParameter().Name()) == "Age")
age = String(item.RecordedValue());
});
ko.utils.arrayForEach(this.Session().SessionValues(),(item: SessionValueVm) => {
if (String(item.StudySessionParameter().Name()) == "Race")
race = String(item.RecordedValue());
});
ko.utils.arrayForEach(this.Session().SessionValues(),(item: SessionValueVm) => {
if (String(item.StudySessionParameter().Name()) == "Height")
height = String(item.RecordedValue());
});
ko.utils.arrayForEach(this.Session().SessionValues(),(item: SessionValueVm) => {
if (String(item.StudySessionParameter().Name()) == "Sex")
gender = String(item.RecordedValue());
});
var promise = this.Session().CalculatePredicted(age, race, gender, height, String(this.StudyTestParameter().PftCentralStudyParameterId()), predictedSetName, studyId);
promise.done((data: string) => {
return data
});
}
}
else
return "";
});
CalculatePredicted = (age: string, race: string, gender: string, height: string, studySessionParameterId: string, predictedSetName: string, studyId: number) => {
var deferred = $.Deferred();
$.ajax({
url: "/Workflows/CalculatePredicted",
cache: false,
data: { age: age, ethnicity: race, gender: gender, height: height, studySessionParameterId: studySessionParameterId, testTypePredictedSetName: predictedSetName, studyId: studyId },
dataType: "json",
contentType: "application/json charset=utf-8"
}).done(data => {
deferred.resolve(data);
}).fail((jqXHR) => {
alert(jqXHR.responseText);
deferred.reject();
});
return deferred;
}
Below is the HTML.
<div>
Test Values:
<table class="width100pct gridtable">
<tbody data-bind="foreach: TestValues">
<tr>
<td data-bind="text: StudyTestParameter().Name"></td>
<td data-bind="text: RecordedValue"></td>
<td data-bind="text: ATSBestValue"></td>
<td data-bind="text: PredictedValue"></td>
</tr>
</tbody>
</table>
</div>
your promise object can't return for your computed. By the time the promise is done, the computed has long returned 'undefined'. That is the nature of async calls. Consider setting a different observable within the promise.done() function and bind to that new field in the UI instead; the computed function will still trigger if the underlying fields change.

go back to before directive was used in angularJS

I'm working on a project at the moment using angular JS.
I've used a directive to switch from advanced search so that the results can be viewed on its own page. basically when submit is clicked, the advanced search is hidden and only results table can be seen. so now, once on results table, I want to be able to go back to that advanced search to the point before I hit that submit button, the data is still there, I have just reversed the directive in a way. any suggestions would be greatly appreciated, this is all fairly new to me.
thanks! (please note, my search controller uses TypeScript)
this is being called just under the submit button on the search_partial.html
<div class="submit">
<button class="btn btn-primary" type="button" name="Submit" ng-click="vm.validateForm()" ng-disabled="!(!!vm.hospitalNumberInput || !!vm.nhsNumberInput || !!vm.fullNameInput || !!vm.sexInput || vm.yearOfBirthInput || !!vm.streetInput || !!vm.cityInput
|| !!vm.postCodeInput || !!vm.chosenCountry)">Search</button>
Clear all
</div>
</form>
<section>
<div id="searchDirective" search-results patients="vm.results" class="ng-hide"></div>
</section>
and I have a directives file called search.results.directive.js
(function () {
angular
.module('app.search.results')
.directive('searchResults', function() {
return {
restrict: 'AE',
templateUrl: 'app/search/Partials/result_partial.html',
scope: {
patients: '='
}
};
});
})();
so what I'm trying to do now that I can see the results-partial.html on the screen in front of me, I want to be able to click a back button on there to take me back to the search-partial.html at the point before the user clicked that submit button so that the data in the boxes can be altered if needs be or more search data added. (at the moment I have a back href going to the home url, it works for now, but that's what im hoping to replace).
results-partial.html:
<main class="container">
<!-- RESULT -->
<section class="result-display">
<form>
<div class="searchresults content table-responsive">
<h2>Search Criteria: </h2>
<h2> Search Results: {{patients.length}} patients found</h2>
<table class="table resultstable table-striped">
<thead>
<tr class="theading">
<th>Hospital number</th>
<th>NHS Number</th>
<th>Full Name</th>
<th>DOB</th>
<th>Sex</th>
</tr>
</thead>
<!--repeated simply for style insight-->
<tbody>
<tr ng-repeat="patient in patients">
<td>{{patient.resource.hospitalNumber}}</td>
<td>{{patient.resource.nhsNumber}}</td>
<td>{{patient.resource.nameString}}</td>
<td>{{patient.resource.birthDate}}</td>
<td>{{patient.resource.gender.toUpperCase()}}</td>
</tr>
</tbody>
</table>
</div>
<a href style="float: right; font-size:120%;" onclick="location.href='http://localhost:3000/';"><i class="close"></i><u>Back to Search</u></a>
</form>
</section>
</main>
If I understand the question right, you want to preserve the values in the input.
You can use a factory or value to store the data.
myApp.factory('DataHolder', function (){
return data;
});
// Or, with value
myApp.value('DataHolder', data);
And in your controller you can access that data anywhere.
myApp.controller('Ctrl', function ($scope, DataHolder){
$scope.data = DataHolder;
});
So when you come back to the back state, if you have data stored you can get it back to show it.
fixed it. managed to get it working my changing the flag to false with "returnToSearch()" function at the bottom.
createDisplayParams(): void {
// add in search params to display params
var paramTypes: string[] = [];
for (var i: number = 0; i < this.searchParams.length; ++i) {
var objectIndex: number = paramTypes.indexOf(this.searchParams[i].ParamName);
if (objectIndex === -1) {
// if param name dosen't exist, add it to the paramTypes
paramTypes.push(this.searchParams[i].ParamName);
}
}
for (var j: number = 0; j < paramTypes.length; ++j) {
var valueParams: core.ISearchParam[] = [];
valueParams =_.filter(this.searchParams, searchParam => { //lodash
return searchParam.ParamName == paramTypes[j];
});
var valueStrings: string[] = [];
valueParams.forEach(v => valueStrings.push(v.ParamValue));
this.displayParams.push({ paramType: paramTypes[j], paramValue: valueStrings.join(", ") });
}
}
obtainPatientInformation(): void {
this.createSearchParams();
if (this.searchParams.length > 0) {
var rawResults: angular.IPromise<core.IPatient> = this.searchDataService.performAdvancedSearch(this.searchParams);
var searchControllerInstance: SearchController = this;
this.createDisplayParams();
rawResults.then(
function success(response: any): void {
if (response.data.entry && response.data.entry.length > 0) {
searchControllerInstance.noResults = false;
searchControllerInstance.results = response.data.entry;
for (var index: number = 0; index < searchControllerInstance.results.length; index++) {
var patient: core.IEntry = searchControllerInstance.results[index];
patient.resource.nameString = '';
if (patient.resource.name) {
var familyNameArray: string[] = patient.resource.name[0].family;
for (var familyIndex: number = 0; familyIndex < familyNameArray.length; familyIndex++) {
var familyName: string = familyNameArray[familyIndex];
patient.resource.nameString = patient.resource.nameString + ' ' + familyName.toUpperCase() + ',';
}
var givenNameArray: string[] = patient.resource.name[0].given;
for (var givenIndex: number = 0; givenIndex < givenNameArray.length; givenIndex++) {
var givenName: string = givenNameArray[givenIndex];
patient.resource.nameString = patient.resource.nameString + ' ' + givenName;
}
}
var identifiers: core.IIdentifier[] = patient.resource.identifier;
for (var indentifierIndex: number = 0; indentifierIndex < identifiers.length; indentifierIndex++) {
var identifier: core.IIdentifier = identifiers[indentifierIndex];
if (identifier.system) {
if (identifier.system === 'nhsNumber') {
patient.resource.nhsNumber = identifier.value;
}
if (identifier.system === 'hospitalNumber') {
patient.resource.hospitalNumber = identifier.value;
}
}
}
}
} else {
searchControllerInstance.noResults = true;
searchControllerInstance.results = null;
}
});
}
this.searchClicked = true;
this.checkSearch();
}
checkSearch(): void {
var resultSectionElements: angular.IAugmentedJQuery = angular.element('[id*="resultSection"]');
var advanceSearchSectionElements: angular.IAugmentedJQuery = angular.element('[id*="advanceSearchSection"]');
if (this.searchClicked) {
resultSectionElements.removeClass('ng-hide');
advanceSearchSectionElements.removeClass('ng-show');
advanceSearchSectionElements.addClass('ng-hide');
} else {
resultSectionElements.addClass('ng-hide');
advanceSearchSectionElements.removeClass('ng-hide');
advanceSearchSectionElements.addClass('ng-show');
}
}
returnToSearch(): void {
this.searchClicked = false;
this.searchParams.splice(0,this.searchParams.length);
this.displayParams.splice(0,this.displayParams.length);
this.checkSearch();
}

A pattern to be designed using template binding of knockoutjs

We have a situation as mentioned below:
There is a set of data for a search panel, it's called in several pages with different types of components and placement of it. There can be combo boxes, radio buttons, input boxes and buttons.
Knockout has a feature of template binding in which we can have the flexibility to show numerous panels on condition using a template in the html mapped to MOdel.
Below is the code and pattern:
HTML:
<div id="content-wrapper">
<div class="spacer"></div>
<div>
<table class="data-table">
<thead>
<tr>
<th colspan="4"> Search </th>
</tr>
</thead>
<tbody data-bind="foreach: preSearchData" >
<tr>
<!-- ko template: { name: 'label_' + templateName()} -->
<!-- /ko -->
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/html" id="label_Combo">
<td>It is a Combo </td>
</script>
<script type="text/html" id="label_Number">
<td>
It is a Number
</td>
</script>
MODEL:
Models.Components = function(data) {
var self = this;
self.number = data.number;
self.labelCd = data.labelCd;
self.xmlTag = data.xmlTag;
self.Type = new Cobalt.Models.Type(data.Type);
};
Models.Type = function(data) {
var self = this;
self.component = data.component;
self.records = data.records;
self.minLength = data.minLength;
self.maxLength = data.maxLength;
self.defaultValue = data.defaultValue;
self.targetAction = data.targetAction;
};
Models.ComponentType = function (paymentTypeCode, data, actionId) {
var ret;
self.templateName(data.component);
if (!data || (actionId === Cobalt.Constant.Dashboard.copyProfile))
data = {};
if (paymentTypeCode == Cobalt.Constant.Dashboard.creditCard)
ret = new Cobalt.Models.CreditCardPaymentType(data.cardHolderName, data.cardNumber, data.cardExpireDate);
else if (paymentTypeCode == Cobalt.Constant.Dashboard.dd)
ret = new Cobalt.Models.DDPaymentType(data.pinNumber);
else if (Cobalt.Utilities.startsWith(paymentTypeCode, Cobalt.Constant.Dashboard.yahooWallet)) {
if (!data && paymentTypeCode.indexOf('~') > -1) {
data.payCode = paymentTypeCode.substr(paymentTypeCode.indexOf('~') + 1, paymentTypeCode.lastIndexOf('~'));
data.billingAgentId = paymentTypeCode.substr(paymentTypeCode.lastIndexOf('~') + 1);
}
ret = new Cobalt.Models.WalletPaymentType(data.payCode, data.billingAgentId);
}
else if (paymentTypeCode == Cobalt.Constant.Dashboard.ajl) {
ret = new Cobalt.Models.DDPaymentType(data.pinNumber);
}
else
ret = data || {};
return ret;
};
Models.POCModel = function () {
var self = this;
self.templateName = ko.observable();
self.preSearchData = ko.observableArray([]);
self.getResultData = function () {
var data = Cobalt.Data.getResultData();
var componentList = data.componentList;
self.preSearchData(componentList);
};
};
Above code gives me a error saying:
Ajax error: parsererror ( Error: Unable to parse bindings. Message: ReferenceError: templateName is not defined; Bindings value: template:
{ name: 'label_' + templateName()} ) cobalt.init.js:66
This is not a direct answer to your question, but it shows an alternate way of doing this using the ViewModel type to find the view (Template)
http://jsfiddle.net/nmLsL/2
Each type of editor is a ViewModel
MyApp.Editors.BoolViewModel = function(data) {
this.checked = data;
};
MyApp.Editors.BoolViewModel.can = function(data) {
return typeof data === "boolean";
};
And it has a can function that determins if it can edit the value
I then usea library called Knockout.BindingConventions to find the template connected to the ViewModel
https://github.com/AndersMalmgren/Knockout.BindingConventions/wiki/Template-convention
Your foreach binding creates a child binding context, which doesn't include templateName since that's part of the parent. Replace it with
<!-- ko template: { name: 'label_' + $parent.templateName()} -->

Knockout.js use foreach to display object properties

I have this Viewmodel to load the users and their list of Socialgraphs from WCF services. The users appear correct but no socialgraph entry appears. I have checked the service and json returned and all seems ok.
Should I change my Models to sth different or is it the way I'm loading stuff in the ViewModel? thanks
$(document).ready(function () {
var viewModel = {
users: ko.observableArray([]),
loadUsers: function () {
OData.read("Service_UserProfile/", function (data) {
viewModel.users.removeAll();
$.each(data.results, function (index, item) {
var socialgraphs = viewModel.loadSocialGraph();
var user = new UserProfileModel(item, socialgraphs);
viewModel.users.push(user);
});
});
},
loadSocialGraph: function () {
var result = new Array();
// user id will be loaded dynamically in later steps
OData.read("/Service_UserProfile(1)/Socialgraph/", function (data) {
$.each(data.results, function (index, item) {
result.push(new SocialGraph(item));
});
});
return result;
}
};
ko.applyBindings(viewModel);
viewModel.loadUsers();
});
The Model
function UserProfileModel(item,socialgraphs) {
this.Id = ko.observable(item.Id),
this.Nickname = ko.observable(item.Nickname),
this.socialgraphs = ko.observableArray(socialgraphs)
};
function SocialGraph(item) {
this.Id = ko.observable(item.Id),
this.StartTime = ko.observable(item.StartTime),
this.Latitude = ko.observable(item.Latitude),
this.Longitude = ko.observable(item.Longitude)
};
The View
<table>
<thead>
<tr>
<th>User ID</th>
<th>Nickname
</th>
<th>Social Graph
</th>
</tr>
</thead>
<tbody data-bind="foreach: users">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Nickname"></td>
<td>
<ul data-bind="foreach: socialgraphs">
<li data-bind="text: Id"></li>
<li data-bind="dateString: StartTime"></li>
<li data-bind="text: Latitude"></li>
<li data-bind="text: Longitude"></li>
</ul>
</td>
</tr>
</tbody>
</table>
You should change:
loadSocialGraph: function () {
var result = ko.observableArray();
// user id will be loaded dynamically in later steps
OData.read("/Service_UserProfile(1)/Socialgraph/", function (data) {
$.each(data.results, function (index, item) {
result.push(new SocialGraph(item));
});
});
return result;
}
and
function UserProfileModel(item,socialgraphs) {
this.Id = ko.observable(item.Id),
this.Nickname = ko.observable(item.Nickname),
this.socialgraphs = socialgraphs
};
Why:
At the line this.socialgraphs = ko.observableArray(socialgraphs)
socialgraphs in the right part is []. Only after some time interval it
will be filled with values. And because of it is not observable array,
knockout won't notice that some items were pushed in it. I.e. it will stay empty.

Categories

Resources