Knockout.js use foreach to display object properties - javascript

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.

Related

AngularJS ng-init not working with ng-repeat

I've been trying to call passID() function when page initializes but the function still doesn't work.
The website works this way:
Page-A passes data to Controller-A
then to Service then to Controller-B then the function from Controller-B gets called by Page-B.
My guess is that my error is in Page-B since I've successfully passed the data of Page-A up to Controller-B but I'm not 100% sure.
In my Page-A, I've made a button calling the function of Controller-A using ng-Click
<button class="viewDetails" ng-click="passID([1,03,07])">test button</button>
Controller-A only gets data from Page-A
angular.module('handsetExplorerModule')
.controller("compareHandsetController", ['$scope','compareHandsetService','sharedData', function($scope, compareHandsetService, sharedData) {
$scope.passID = function(id){
var arrayID = [];
arrayID = id;
sharedData.set(arrayID);
};
}]);
Service gets my JSON and is my getter and setter
angular.module('handsetExplorerModule')
.factory('compareDetailsService', ['$http','$q', function($http, $q) {
return {
getHandsetList: function(){
return $http.get("./data/compareJSON.json");
}
};
}]);
angular.module('handsetExplorerModule')
.factory('sharedData', function() {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
Controller-B filters the JSON file to only get what I need (i used ID from Page-A to filter)
angular.module('handsetExplorerModule')
.controller("compareDetailsController", ['$scope','compareDetailsService','sharedData', function($scope, compareDetailsService, sharedData) {
$scope.data = {phones: []};
compareDetailsService.getHandsetList()
.then(
function(data){
$scope.data.phones = data.data;
},
function(error){
//todo: handle error
}
);
var getData = sharedData.get();
$scope.passID = function passID(){
var newData = [];
for(var i=0; i<$scope.data.phones.length; i++){
if($scope.data.phones[i].id == getData[0] || $scope.data.phones[i].id == getData[01] || $scope.data.phones[i].id == getData[02]){
newData.push($scope.data.phones[i]);
}
}
$scope.phoneContent = newData;
}
$scope.viewDetails = function(id){
alert(id);
}
}]);
Lastly, In my Page-B, I've called Controller-B function: <div ng-init="passID()">
to be used by ng-repeat of my Page-B:
<table id="Content" ng-repeat="x in phoneContent track by x.id">
<tr>
<td class="one">
<table>
<tr>
<td><img class="contImage" ng-src="{{x.image}}" ng-alt="{{x.name}}" /></td>
<td class="textAlign">{{x.name}} <button class="viewDetails" ng-click="viewDetails(x.id)">VIEW DETAILS</button></td>
</table>
</td>
<td class="two">{{x.size}}</td>
<td class="one">{{x.storage}}</td>
<td class="two">{{x.camera}}</td>
<td class="one">{{x.battery}}</td>
<td class="two">{{x.network}}</td>
<td class="one">{{x.sim}}</td>
</tr>
</table>
I don't know why you define arrayID.
To solve your problem try with sharedData.set = arrayID; instead of sharedData.set(arrayID);.
Hope it helps!

Cannot use computable knockout js

I have a view model which is being initialized else where.
function PaymentPlanViewModel(root /* root not needed */, item) {
var self = this;
self.instalmentnbr = item.instalmentnbr;
self.Abbreviation = item.Abbreviation;
self.duedate = item.duedate;
self.capital_payment = ko.observable(item.capital_payment);
self.interest_payment = ko.observable(item.interest_payment);
self.overdue_payment = ko.observable(item.overdue_payment);
self.total_payment = ko.observable(item.total_payment);
self.capital_paid = ko.observable(item.capital_paid);
self.interest_paid = ko.observable(item.interest_paid);
self.overdue_paid = ko.observable(item.overdue_paid);
self.total_paid = ko.observable(item.total_paid);
self.INSERT_DT = item.INSERT_DT ;
};
self.total_remaining = ko.computed(function() {
var sum = 0;
sum += parseFloat(self.total_payment) - parseFloat(self.total_paid);
return sum.toFixed(2);
});
self.getPaymentPlan = function (request_orig_id) {
$.ajax({
type: 'POST',
url: BASEURL + 'index.php/moneyexchange/getPaymentPlanForRequest/' + auth,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: ko.toJSON({
request_orig_id : request_orig_id
})
})
.done(function(data) {
self.paymentPlan.removeAll();
$.each(data, function (index, item) {
// self.paymentPlan.push(item);
self.paymentPlan.push(new PaymentPlanViewModel(self, item));
});
self.nextDueDate(self.paymentPlan()[0].duedate);
})
.fail(function(xhr, status, error) {
alert(status);
})
.always(function(data){
});
};
This view model above is being initialized in this place,
// Initialize the MoneyBorrowedViewModel view-model.
$.getJSON(self.borrowmoneyUri, function (borrowedmoney) {
$.each(borrowedmoney, function (index, money) {
self.moneyborrowed.push(new MoneyBorrowedViewModel(self, money));
});
// holds the total moneyinvested count
self.TotalNumberOfMoneyborrowed(self.moneyborrowed().length);
// initialize the Money Requests and Offers available table
self.searchMoneyborrowed();
/* Read the payment plans for the frst request */
self.getPaymentPlan(self.moneyborrowed()[0].ORIG_ID);
self.lastDueDate(self.moneyborrowed()[0].Due);
});
So I was trying to use in the paymentPlanView model, a computed function to get two values and use them on a table like this
<tbody data-bind="foreach : paymentPlan" >
<tr>
<td class="text-center"><span data-bind="text: $data.duedate" ></span></td>
<td class="text-center"><span data-bind="text: $data.total_payment" ></span></td>
<td class="text-center"><span data-bind="text: $data.interest_payment" ></span></td>
<td class="text-center"><span data-bind="text: $data.capital_payment" ></span></td>
<td class="text-center"<span data-bind="text: $data.total_remaining" ></span></td>
</tr>
</tbody>
All the other values are shown in the table , only the total_remaining value I cannot see. So I am not sure why my computed value is not working. I have created the observables at the top like this.
self.paymentPlan = ko.observableArray();
So I need to know how can I put that computed value total_remaining, since I cannot see it now.
You need to remember that Knockout observables are functions. So to get the value of an observable, you need to "call" the observable. Your computed needs to be changed to:
self.total_remaining = ko.computed(function() {
var sum = 0;
sum += parseFloat(self.total_payment()) - parseFloat(self.total_paid());
return sum.toFixed(2);
});
Notice I'm using function call syntax for total_payment and total_paid.

Knockout options binding: how to remove items dynamically from popup if they are selected

Here is a sample for the problem, in this case someone selects toppings for a pizza (my real world problem is analogue to that):
http://jsfiddle.net/csabatoth/aUH2C/4/
HTML:
<h2>Pizza toppings (<span data-bind="text: toppings().length"></span>)</h2>
<table class="table table-bordered">
<thead><tr>
<th>Topping</th><th>Number of units</th>
</tr></thead>
<tbody data-bind="foreach: toppings">
<tr>
<td><select data-bind="options: $root.availableToppings(), value: name, optionsText: 'name'"></select></td>
<td><input data-bind="value: num" /></td>
<td>Remove</td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-primary" data-bind="click: addTopping, enable: toppings().length < 5">Add another topping</button>
JS:
// Class to represent a possible topping
function Topping(name) {
var self = this;
self.name = name;
// This will have other properties
}
// Class to represent a row in the grid
function ToppingRow(topping, num) {
var self = this;
self.topping = ko.observable(topping);
self.num = ko.observable(num);
self.toppingName = ko.computed(function() {
return self.topping().name;
});
}
function ToppingsViewModel() {
var self = this;
// Non-editable catalog data - would come from the server
self.availableToppings = ko.observableArray([
new Topping("Mushroom"),
new Topping("Pepperoni"),
new Topping("Cheese"),
new Topping("Olives"),
new Topping("Chicken")
]);
// Editable data
self.toppings = ko.observableArray([
new ToppingRow(self.availableToppings()[0], 1)
]);
// Operations
self.addTopping = function() {
self.toppings.push(new ToppingRow(self.availableToppings()[0], 1));
}
self.removeTopping = function(topp) { self.toppings.remove(topp) }
}
ko.applyBindings(new ToppingsViewModel());
What I would like is: when the user selects a topping, that option should disappear from the popup list. Once the user removes the topping, it should reappear in the popup. In other words: I don't want the user to add the same topping more than once. How to do that?
(Or now I think if I should approach this in a totally different way and would have a list with the toppings on the left, and the user could drag&drop to the right destination list from there...). In the real world example the number of "toppings" would be maybe some dozen I think.
You could simplify this slightly by having a topping selector with 1 drop down. Then when you click add it inserts the currently selected item to a selected toppings section and then removes the option from the available list.
If you are feeling clever you could also bind the drop-down to a computed collection of items that does not include already selected items. (underscore.js will also help with this immensely).
(Fiddle)
JS
// Class to represent a possible topping
function Topping(name) {
var self = this;
self.name = name;
// This will have other properties
}
// Class to represent a row in the grid
function ToppingRow(topping, num) {
var self = this;
self.topping = topping;
self.num = ko.observable(num);
self.toppingName = ko.computed(function() {
return self.topping.name;
});
}
function ToppingsViewModel() {
var self = this;
// Non-editable catalog data - would come from the server
self.allToppings = ko.observableArray([
new Topping("Mushroom"),
new Topping("Pepperoni"),
new Topping("Cheese"),
new Topping("Olives"),
new Topping("Chicken")
]);
self.selectedToppings = ko.observableArray([]);
self.availableToppings = ko.computed(function(){
return _.reject(self.allToppings(), function(topping) {
return _.contains(_.pluck(self.selectedToppings(), 'topping'), topping);
})
});
self.currentlySelectedTopping = ko.observable();
self.currentlySelectedToppingNumber = ko.observable(1);
// Operations
self.addTopping = function() {
self.selectedToppings.push(new ToppingRow(self.currentlySelectedTopping(), self.currentlySelectedToppingNumber()));
}
//self.removeTopping = function(topp) { self.toppings.remove(topp) }
}
ko.applyBindings(new ToppingsViewModel());
HTML
<h2>Pizza toppings</h2>
<table class="table table-bordered">
<thead><tr>
<th>Topping</th><th>Number of units</th>
</tr></thead>
<tbody>
<tr>
<td><select data-bind="options: availableToppings, value: currentlySelectedTopping, optionsText: 'name'"></select></td>
<td><input data-bind="value: currentlySelectedToppingNumber" /></td>
<td>Remove</td>
</tr>
<!-- ko foreach: selectedToppings -->
<tr><td data-bind="text: toppingName"></td><td data-bind="text: num"></td></tr>
<!-- /ko -->
</tbody>
</table>
<button type="button" class="btn btn-primary" data-bind="click: addTopping, enable: availableToppings().length">Add another topping</button>
I dont why you are making simple things difficult. Here is some modified version.
function Topping(name) {
var self = this;
self.name = name;
self.active = ko.observable(false)
self.toggle = function () {
self.active(!self.active())
}
// This will have other properties
}
// Class to represent a row in the grid
function ToppingRow(name, num) {
var self = this;
self.name = name;
self.num = num;
}
function ToppingsViewModel() {
var self = this;
// Non-editable catalog data - would come from the server
self.availableToppings = ko.observableArray([
new Topping("Mushroom"),
new Topping("Pepperoni"),
new Topping("Cheese"),
new Topping("Olives"),
new Topping("Chicken")
]);
self.list = ko.observableArray()
self.num = ko.observable(1)
self.selected = ko.observable()
// Operations
self.addTopping = function() {
self.list.push(new ToppingRow(self.selected(),self.num()));
self.setAvailableToppings(self.selected())
}
self.removeTopping = function(item) {
self.list.remove(item)
self.setAvailableToppings(item.name)
}
self.setAvailableToppings = function (name) {
var items = []
ko.utils.arrayForEach(self.availableToppings(),function (item) {
if(item.name == name){
item.toggle()
}
items.push(item)
})
self.availableToppings([])
self.availableToppings(items)
var selected = ko.utils.arrayFirst(self.availableToppings(),function (item) {
return item.active() == false
})
if(selected){
self.selected(selected.name)
}
}
self.setOptionDisable = function(option, item) {
ko.applyBindingsToNode(option, {disable: item.active()}, item);
}
}
$(document).ready(function(){
ko.applyBindings(new ToppingsViewModel());
})
And view
<h2>Pizza toppings (<span data-bind="text: list().length"></span>)</h2>
<table class="table table-bordered">
<thead><tr>
<th>Topping</th><th>Number of units</th>
</tr></thead>
<tbody data-bind="foreach: list">
<tr>
<td data-bind="text:name"></td>
<td data-bind="text: num"></td>
<td>Remove</td>
</tr>
</tbody>
</table>
<br clear="all"/>
<select data-bind="
options: $root.availableToppings(),
value: $root.selected,
optionsText: 'name',
optionsValue : 'name',
optionsAfterRender: $root.setOptionDisable,
enable: list().length < 5
">
</select>
<input data-bind="value: $root.num,enable: list().length < 5" />
<br clear="all"/>
<button type="button" class="btn btn-primary"
data-bind="
click: addTopping,
enable: list().length < 5
">Add another topping</button>
Fiddle Demo

Knockout click data binding

I have an issue in below example
HTML CODE:
<table id="items">
<thead>
<tr>
<td>ToDo List</td>
</tr>
</thead>
<tbody data-bind="foreach: toDoItems">
<tr>
<td><label data-bind="text: toDoItem"></label></td>
<td>Remove</td>
</tr>
</tbody>
</table>
Add item: <input type="text" id="newitem" />
<button data-bind="click: addnewItem">Add</button>
This is my JS code:
$(function () {
var MetalViewModel = function() {
var self = this;
self.toDoItems = ko.observableArray();
self.update = function() {
self.toDoItems.removeAll();
self.toDoItems.push(
new metals({"Task":"This is urgent task."}),
new metals({"Task":"You need to do it also."})
);
}
self.addnewItem = function () {
alert( $("#newitem").val() );
self.toDoItems.push(
new metals({"Task":$("#newitem").val()})
);
};
self.removeToDoItem = function(item) {
self.toDoItems.remove(item);
};
};
var MetalViewModel = new MetalViewModel();
var y = window.setInterval(MetalViewModel.update,1000);
ko.applyBindings(MetalViewModel, document.getElementById("items"));
});
var metals = function (data) {
return {
toDoItem: ko.observable(data.Task)
};
};
Everything is working fine. Listing, removing ....
The only issue I am facing is that when I add new item, function is totally not working... means even if I alert click binding does not reach there.
You bound only the table with your vewmodel (id=items):
ko.applyBindings(MetalViewModel, document.getElementById("items"));
And your button is outside the table so it is not related to your viewmodel.
As a solution, you can bind to the common parent or to the whole page:
ko.applyBindings(MetalViewModel);
Here're two issues:
invalid binding, instead of ko.applyBindings(MetalViewModel, document.getElementById("items")); should be ko.applyBindings(MetalViewModel);
invalid data-bind function name instead of additemToAdd should be addToDoItem
here's working sample

Knockoutjs does not update View

I use knockoutjs to update my Html view when an Javascript (Signalr) function is fired. But the view does not update when producthub.client.showOnlineUser is called. When i apply the binding everytime the table has same content multiple times. How can i just update the view in knockoutjs?
Html:
<table id="usersTable">
<thead>
<tr>
<th>Name</th>
<th>Mail</th>
</tr>
</thead>
<tbody data-bind="foreach: seats">
<tr>
<td data-bind="text: NameFirst"></td>
<td data-bind="text: Mail"></td>
</tr>
</tbody>
</table>
Javascript:
$(document).ready(function () {
var applied = false;
var model;
producthub.client.showOnlineUser = function (userOnlineOnUrl, msg1) {
function ReservationsViewModel() {
var self = this;
self.seats = ko.observableArray(userOnlineOnUrl);
}
model = new ReservationsViewModel();
if (!applied) {
ko.applyBindings(model);
applied = true;
}
};
});
userOnlineOnUrl is an JSON-Array which changes it's data on each function call. The view (table) is not updated with it's data.
try not to call model every time just update it with function
$(document).ready(function () {
var applied = false;
var model;
function ReservationsViewModel() {
var self = this;
self.seats = ko.observableArray();
}
model = new ReservationsViewModel();
ko.applyBindings(model);
applied = true;
producthub.client.showOnlineUser = function (userOnlineOnUrl, msg1) {
model = new ReservationsViewModel();
//remove the previous data in array
model.seats.removeAll();
//Add new data to array
model.seats(userOnlineOnUrl);
$('#onlineUsers').append(msg1);
};
});

Categories

Resources