Cannot use computable knockout js - javascript

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.

Related

Vue.js index and length resetting on each page

I'm having a strange problem that I can't solve. I wanted to add index number in for-each loop and count of total data in my vue.js component. I managed to do that, but my counter resets on every page within pagination, and items.lenght only counts data from current pagination page. I did the following:
<tr v-for="(item, index) in items" v-bind:key="item.id">
<td>{{index + 1}}</td>
<td>{{item.name}}</td>
</tr>
And to count all the data:
<div class="form-group">
Total data: {{items.length}}
</div>
Everything is working fine of first page of pagination, but when I select second page, it only counts total data from that specific page, and the counter is starting from 1 again. To illustrate, on first page it shows me that I have total of 15 data (and I have around 300), and indexing is fine:
And If I go to second page, it still shows me the same total, and index is starting from 1 again, where I want it to continue (ex 16, 17..)
Part of the Vue.js component:
mounted() {
this.getStatuses();
this.getSchoolYears();
if (this.items.length == 0) {
this.loadPage(this.pagination.current_page);
this.getObjects();
}
},
methods: {
getSaveStateConfig() {
return {
'cacheKey': 'ApplicationComponent',
};
},
addFilter(key, value) {
this.filters = this.filters.filter(function (obj) {
return obj.key !== key;
});
this.pagination.current_page = 1;
this.filters.push({key: key, value: value});
},
loadPageDebounce: _.debounce(function (page, parameters) {
this.loadPage(page);
}, 500),
loadPage(page, parameters) {
var parameters = '';
this.filters.forEach(function (obj, index) {
parameters = parameters + '&' + obj.key + '=' + obj.value;
});
var $this = this;
axios({
method: 'GET',
url: '/api/applications?page=' + page + parameters,
headers: {'X-CSRF-TOKEN': window.csrfToken, 'X-Requested-With': 'XMLHttpRequest'},
})
.then(function (response) {
// console.log('resposne', response);
$this.items = response.data.data
$this.pagination.total = response.data.total;
$this.pagination.last_page = response.data.last_page;
$this.pagination.current_page = response.data.current_page;
$this.pagination.from = response.data.from;
$this.pagination.to = response.data.to;
$this.pagination.next_page_url = response.data.next_page_url;
$this.pagination.prev_page_url = response.data.prev_page_url;
})
.catch(function (error) {
console.log(error);
});
},
loadData() {
this.loadPage(this.pagination.current_page, this.filters);
},
How to prevent this from happening when using vue.js pagination. All help appreciated.
Not the best solution I guess, but it should work as a quick fix.
<tr v-for="(item, index) in items" v-bind:key="item.id">
<td>{{(pagination.current_page*15)-15 + index+1}}</td>
<td>{{item.name}}</td>
</tr>
Edited better Solution as computed function to seperate view and logic:
<tr v-for="(item, index) in items" v-bind:key="item.id">
<td>{{getOverallIndex(index)}}</td>
<td>{{item.name}}</td>
</tr>
computed: {
getOverallIndex: function(index) {
return this.pagination.current_page*15)-15 + index + 1
}
}
In your method you have that:
.then(function (response) {
$this.items = response.data.data
$this.pagination.total = response.data.total; // THERE
If you have good backend code you should get your total count here.
So try to console.log($this.pagination.total) after that function or console.log(response.data.total) in that function. If you have there 15 not 300 it's error on your backend. Maybe is problem for binding this on axios.
Let me know if you founded that.
Good luck!
try like
<tr v-for="(item, index) in items" v-bind:key="item.id">
<td>{{items.indexOf(item)+1}}</td>
<td>{{item.name}}</td>
</tr>

Promises dont get resolved. Alasql doesnt import CSV

I cant figure out what the error is. I try to read out a CSV from my server and then to access the DateValues with an FUnction Called getZinsAnDatum.
The Chain begins in function:
$scope.calcZins = function database(ctrl, $scope) {....
I chained the results, reading out my CSV-Values via
getZinsAnDatum(dateee, fullDate)
works when I click twice or wait and click again. Guess it works, after clicking two times, because its load into the cache. The function itself returns a promise too, after i get Warning from this, pointing on the code lines from getZinsAnDatum(dateee, fullDate):
Warning: a promise was created in a handler at /localhost:8383/validations/js/index.js:269:17 but was not returned from it
Sorry if I miss fundamental concepts.
I guess the Promises arent chained right or its to much traffic, because for every Value i detect (about 200) it downloads the hole CSV? therefore I wanted to inject the CSV once in my Alasql database and work from there.
But putting it in doesnt seem to work. Therefore could you help me out maybe? Down my code HTML and javascript:
I spared out some functions and variables, all variables are given and defined.
Javascript:
/* global $scope, user,user.anpDat, user.zinsDiff, user.zinsJetz,user.zinsNext, user.nextAnpDat, user.AnzahlAnMonatenZwischenErsterAnpassungUndAktuellemDatum , user.Berechnung, ctrl.Berechnung, Promise, alasql*/
angular.module('MyApp', ['ngMaterial', 'ngMessages', 'material.svgAssetsCache']).controller('AppCtrl', function ($scope) {
this.fullDate = new Date("1999-12-20");
this.fullDate2 = new Date("2000-01-20");
this.fullDate3 = new Date("2010-03-19");
this.date = fullDate.toISOString().substring(0, 10);
this.date2 = fullDate2.toISOString().substring(0, 10);;
this.date3 = fullDate3.toISOString().substring(0, 10);;
var data = {};
var url = 'tests/fib.csv';
function getZinsAnDatum(dateee, fullDate) {
return new Promise(function (resolve, reject) {
alasql.promise('SELECT [1] FROM CSV("tests/fib.csv", {headers:false, }) WHERE [0] >=? AND [1]!="."', [dateee])
.then(function (data) {
var temp = data [1 ][1];
resolve(temp);
}).catch(function (err) {
console.log('Error:', err);
});
});
}
$scope.calcZins = function database(ctrl, $scope) {
this.mybase = new alasql.Database('mybase');
this.mybase.exec('CREATE TABLE one ');
this.mybase.exec('SELECT [0][1] INTO one FROM CSV("tests/fib.csv", {headers:false, })');
//this.mybase.exec('PRINT one'); //GIVES ERRORS, CANT READ UNDEFINED JSON Object
calcZins2(ctrl, $scope);
;
}
function calcZins2(ctrl, $scope) {
var user = new Object;
var anpassen = false;
user = angular.copy(ctrl);
user.Berechnung;
var Row;
getZinsAnDatum(user.date2, user.fullDate2).then(function (resu) {
// Code depending on result
var value = parseFloat(resu.replace(",", "."));
user.zinsJetz = value;
Row.zinsJetz=user.zinsJetz;
user.Berechnung.push(Row);
nextAbschnitt(user, ctrl, $scope);
}).catch(function () {
// An error occurred
});
function nextAbschnitt(user, ctrl, $scope) {
getZinsAnDatum(date3,fullDate3).then(function (resulte) {
// Code depending on result
var value = parseFloat(resulte.replace(",", "."));
user.zinsNext = value;
getZinsAnDatum(date, FullDate).then(function (result) {
// Code depending on result
var value3 = parseFloat(result.replace(",", "."));
user.zinsJetz = value3;
gO(user, ctrl, $scope);
}).catch(function () {
// An error occurred
});
}).catch(function () {
// An error occurred
});
}
}
function getMonthsDifference(date1, date2) {
var k = date1.getYear() * 12 + date1.getMonth();
console.log(k + "k")
var m2 = date2.getYear() * 12 + date2.getMonth();
console.log(m2 + "m2")
return (m2 - k + 1);
}
function gO(user, ctrl, $scope) {
var g = getMonthsDifference(user.date2 user.date3);
user.AnzahlAnMonatenZwischenErsterAnpassungUndAktuellemDatum = g;
var count = 0;
var promiseFor = Promise.method(function (condition, action, value) {
if (!condition(value))
return value;
return action(value).then(promiseFor.bind(null, condition, action));
});
promiseFor(function (count) {
return count < user.AnzahlAnMonatenZwischenErsterAnpassungUndAktuellemDatum;
}, function (count) {
// ctrl.Berechnung = user.Berechnung;
return getZinsAnDatum(date3, fullDate3 ).then(function (resulte) {
// DO STUFF
ctrl.done = true; // Shows THE HTML div via ng-Show
ctrl.Berechnung = user.Berechnung;
return ++count;
});
}, 0).then(function (user) {
});
}
});
HTML:
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr><td>Zinssatz nach richtiger Anpassung:</td><td> {{ctrl.zinssatzKorrekt}}</td></tr>
<tr><td>Aktueller Zinssatz der Bank</td><td>{{ctrl.zinssatzAktuell}}</td></tr>
<tr><td>Differenz: </td><td>{{ctrl.zinssatzAktuell-ctrl.zinssatzKorrekt}}</td></tr>
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr> </tr>
<tr> </tr> <tr> </tr>
<tr>
<td>FIBOR/EURIBOR-Stichtag</td><td>maßgeblicher FIBOR/EURIBOR in %</td><td>FIBOR/EURIBOR-
Veränderung</td> <td>Veränderung
kumuliert</td> <td>Anpassung</td> <td>gebotene
Anpassung</td> <td>Zinssatz korrekt</td></tr>
<tr ng-class="Texttype" ng-repeat="row in ctrl.Berechnung track by $index">
<td> {{row.anpDat}}</td><td>{{row.zinsJetz}} </td><td>{{row.zinsDiff}} </td> <td>{{row.veranderungKummuliert}} </td> <td>{{row.anpassen}} </td> <td>{{row.geboteneAnpassung}} </td> <td>{{row.zinssatzKorrekt}} </td></tr>
</table>

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.

Why does not the data-binding work? (using nested foreach loops)

The problem is that the data-binding doesn't work.
I have an observableArray() containing claims. Each claim has an observableArray() of expenses, which is one item from the self.pc_listOfExpenses().
Here's the structure of both arrays:
self.pc_listOfClaims = ([
ID: 34
claimAgainst: "CompanyName"
date: "2010-10-10"
desc: "Description"
**expenses: Object[0]** // <= This one should be an array.
]);
self.pc_listOfExpenses = ko.observableArray([
ID: "34"
**Array** // <= This is the array of "Expense" Objects
]);
Note: *Do not bother about the correct syntax above. I fill in the arrays from the services, using JSON. The Object[0] is what I see in the console. *
The idea is to map each claim, with the corresponding array of expenses:
If we have claimID = 34, then in the self.pc_listOfExpenses() array we have 34=>Array().
Here's the Knockout code:
//#region Preview Claims Block
/**************************************************/
/* Preview Claims Block */
/**************************************************/
self.pc_listOfClaims = ko.observableArray([]);
self.pc_showDetailsArr = ko.observableArray([]);
self.pc_listOfExpenses = ko.observableArray([]);
// Get userID-specified Claims using WS
pc_GetSpecifiedClaims(self);
for (var i = 0; i < self.pc_listOfClaims().length; i++) {
self.pc_showDetailsArr()[self.pc_listOfClaims()[i].ID] = ko.observable(false);
}
self.pc_showMoreDetails = function (claim) {
if (typeof claim !== "undefined") {
self.pc_showDetailsArr()[claim.ID](!self.pc_showDetailsArr()[claim.ID]());
pc_GetClaimExpenses(claim.ID);
for (var i = 0; i < self.pc_listOfExpenses()[claim.ID].length; i++) {
self.pc_listOfClaims()[claim_id]["expenses"]().push(self.pc_listOfExpenses()[claim.ID][i]);
}
}
}
//#endregion
Here is the Web Service:
function pc_GetClaimExpenses(claimID) {
$.ajax({
cache: false,
async: false,
type: 'GET',
url: '/DesktopModules/UltimateExpenses/API/Claims/GetSpecifiedExpensesAsJSONString',
success: function (data) {
self.pc_listOfExpenses()[claimID] = JSON.parse(data);
//console.log(JSON.parse(data));
}
});
}
Here's the view:
<table id="claimsDataTable">
<tbody data-bind="foreach: pc_listOfClaims">
<tr>
<td data-bind="text: claimAgainst"></td>
<td data-bind="text: projectName"></td>
<td data-bind="text: taskName"></td>
<td data-bind="text: price"></td>
<td data-bind="text: status"></td>
<td data-bind="text: date"></td>
<td class="actionOptions">
M
</td>
</tr>
<tr>
<td colspan="7" data-bind="visible: pc_showDetailsArr()[$data.ID]">
<!-- This is the part which does not work-->
<div data-bind="foreach: expenses">
<span data-bind="text: ID"></span>
<span data-bind="text: Type"></span>
<span data-bind="text: Price"></span>
</div>
</td>
</tr>
</tbody>
</table>
To solve the problem, I created a Claim class, with an observableArray() for the expenses. Then using a loop I created each Claim and pushed every expense into the expenses observableArray(). Here's the code I hade to add/change. I hope it helps someone else too.
The Claim class:
function Claim(ID, claimAgainst, userID, projectName, taskName, desc, price, status, date) {
this.ID = ID;
this.claimAgainst = claimAgainst;
this.userID = userID;
this.projectName = projectName;
this.taskName = taskName;
this.desc = desc;
this.price = ko.observable(price);
this.status = ko.observable(status);
this.date = date;
this.expenses = ko.observableArray([]);
}//Claim
The Web service to get the claims and create the objects:
function pc_GetSpecifiedClaims(self) {
$.ajax({
cache: false,
async: false,
type: 'GET',
url: '/DesktopModules/UltimateExpenses/API/Claims/GetSpecifiedClaimsAsJSONString',
success: function (data) {
tempArr = JSON.parse(data);
for (var i = 0; i < tempArr.length; i++) {
self.pc_listOfClaims.push(new Claim(tempArr[i].ID, tempArr[i].claimAgainst, tempArr[i].userID,
tempArr[i].projectName, tempArr[i].taskName, tempArr[i].desc, tempArr[i].price,
tempArr[i].status, tempArr[i].date));
}
}
});
}
And finally I pushed the array from the self.pc_listOfExpenses(), using the corresponding claimID, into the self.pc_listOfClaims().expenses():
for (var i = 0; i < self.pc_listOfExpenses()[claim.ID].length; i++) {
self.pc_listOfClaims()[claim_id].expenses.push(self.pc_listOfExpenses()[claim.ID][i]);
}
This can't work :
self.pc_listOfExpenses()[claimID] = JSON.parse(data);
Because you are modifying the internal array of the observableArray().
I am not sure, but you could try this :
var items = self.pc_listOfExpenses();
items[claimID] = JSON.parse(data);
self.pc_listOfExpenses(items);
I hope it helps.

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