I am calling many services from a controller in angularJs, Some of my code depends with other service result. Sometimes it works well and but sometimes I am getting undefined error because service execution is not completed.
If I am waiting for completion of service execution(nested code,ie one service inside another service success function etc),which means the nested $http request only start once after parent completed,It will effect performance.
HOw can I solve this situation?
Sample code,
//init client list
clientService.list().success(function (data, status) {
$scope.clients = data.data;
console.log('clients retrieved');
//sales person
settingService.getSalesPerson().success(function (data, status) {
$scope.sales_persons = data;
console.log('sales persons retrieved');
//init item list
itemService.list().success(function (data, status) {
$scope.items = data.data;
console.log('items retrieved');
//quotation settings
settingService.getQuotationSettings().success(function (data, status) {
var qty = data[0];
$scope.quotation.note = data[0].note;
$scope.quotation.terms_and_condition = data[0].terms_and_condition;
console.log('settings retrieved');
//when change client,updating the address
var watchClientModel = function () {
$scope.$watch('selectedClient', function () {
$scope.quotation.client_id = $scope.selectedClient.id;
$scope.quotation.billing_street = $scope.selectedClient.address_info.billing_street;
$scope.quotation.billing_city = $scope.selectedClient.address_info.billing_city;
$scope.quotation.billing_pobox = $scope.selectedClient.address_info.billing_pobox;
$scope.quotation.billing_country = $scope.selectedClient.address_info.billing_country;
$scope.quotation.shipping_street = $scope.selectedClient.address_info.shipping_street;
$scope.quotation.shipping_city = $scope.selectedClient.address_info.shipping_city;
$scope.quotation.shipping_pobox = $scope.selectedClient.address_info.shipping_pobox;
$scope.quotation.shipping_country = $scope.selectedClient.address_info.shipping_country;
});
};
var watchSalesPersonModel = function () {
$scope.$watch('selectedPerson', function () {
$scope.quotation.sales_person_id = $scope.selectedPerson.id;
console.log('Sales person updated');
});
};
//when udpate
if ($scope.isUpdate) {
//Create Quotation
$scope.pageTitle = "Edit Quotation";
$scope.pageTitleIcon = "fa fa-pencil";
//init quotaion details
quotationService.get($routeParams.quotationId).success(function (data, status) {
$scope.quotation = data;
//get clients
$scope.selectedClient = _.findWhere($scope.clients, {id: $scope.quotation.client_id});
console.log('Client preselected');
//get sales person
$scope.selectedPerson = _.findWhere($scope.sales_persons, {id: $scope.quotation.sales_person_id});
console.log('Sales person preselected');
});
} else {
//when insert
$scope.selectedClient = $scope.clients[0];
$scope.selectedPerson = $scope.sales_persons[0];
$scope.quotation.items.push({
'item': null,
'quantity': 0.00,
'rate': 0.00
});
if (qty.auto_generate) {
$scope.quotation.quotation_number = qty.prefix_string + 1000 + Math.floor((Math.random() * 100000) + 1);
}
}
watchClientModel()
watchSalesPersonModel();
});
});
});
});
Here I put all service call as nested ladder.it is working fine but effecting performance.
If I bring back all service call outside,Getting error is $scope.selectedClient is undefined
If you have queries that are not interdependent, you can perform them separately from each other, and then wait on the results:
//init client list
var clients = clientService.list().success(function (data, status) {
$scope.clients = data.data;
console.log('clients retrieved');
});
//sales person
var sales = settingService.getSalesPerson().success(function (data, status) {
$scope.sales_persons = data;
console.log('sales persons retrieved');
});
//init item list
var items = itemService.list().success(function (data, status) {
$scope.items = data.data;
console.log('items retrieved');
});
//quotation settings
var quotes = settingService.getQuotationSettings().success(function (data, status) {
$scope.qty = data[0];
$scope.quotation.note = data[0].note;
$scope.quotation.terms_and_condition = data[0].terms_and_condition;
console.log('settings retrieved');
});
//when change client,updating the address
$scope.$watch('selectedClient', function () {
var selectedClient = $scope.selectedClient,
address_info = selectedClient.address_info,
quotation = $scope.quotation;
quotation.client_id = selectedClient.id;
quotation.billing_street = address_info.billing_street;
quotation.billing_city = address_info.billing_city;
quotation.billing_pobox = address_info.billing_pobox;
quotation.billing_country = address_info.billing_country;
quotation.shipping_street = address_info.shipping_street;
quotation.shipping_city = address_info.shipping_city;
quotation.shipping_pobox = address_info.shipping_pobox;
quotation.shipping_country = address_info.shipping_country;
});
$scope.$watch('selectedPerson', function () {
$scope.quotation.sales_person_id = $scope.selectedPerson.id;
console.log('Sales person updated');
});
// when all finished loading
$q.all([clients, sales, items, quotes]).then(function () {
// when udpate
if ($scope.isUpdate) {
//Create Quotation
$scope.pageTitle = "Edit Quotation";
$scope.pageTitleIcon = "fa fa-pencil";
//init quotaion details
quotationService.get($routeParams.quotationId).success(function (data, status) {
$scope.quotation = data;
//get clients
$scope.selectedClient = _.findWhere($scope.clients, {id: $scope.quotation.client_id});
console.log('Client preselected');
//get sales person
$scope.selectedPerson = _.findWhere($scope.sales_persons, {id: $scope.quotation.sales_person_id});
console.log('Sales person preselected');
});
} else {
//when insert
$scope.selectedClient = $scope.clients[0];
$scope.selectedPerson = $scope.sales_persons[0];
$scope.quotation.items.push({
'item': null,
'quantity': 0.00,
'rate': 0.00
});
if (qty.auto_generate) {
$scope.quotation.quotation_number = $scope.qty.prefix_string + 1000 + Math.floor((Math.random() * 100000) + 1);
}
}
});
Related
I am trying to create an observableArray of "Board" objects to populate a view.
I can currently add new Board objects to the array after each timed page refresh. But instead of clearing the array and then adding new boards from the foreach loop, it just adds to the existing ones causing duplicates.
$(document).ready(function() {
refreshPage();
});
function refreshPage() {
getGames();
setTimeout(refreshPage, 10000);
console.log("Page refreshed");
};
function Board(data) {
this.gameChannel = ko.observable(data.GameChannel);
this.HomeTeamImage = ko.observable(data.HomeTeamImage);
this.HomeTeamName = ko.observable(data.HomeTeamName);
this.HomeBeerPrice = ko.observable(data.HomeBeerPrice);
this.HomeTeamArrow = ko.observable(data.HomeTeamArrow);
this.HomeBeer = ko.observable(data.HomeBeer);
this.HomeBeerAdjustedPrice = ko.observable(data.HomeBeerAdjustedPrice);
this.AwayTeamArrow = ko.observable(data.AwayTeamArrow);
this.AwayBeerPrice = ko.observable(data.AwayBeerPrice);
this.AwayTeamName = ko.observable(data.AwayTeamName);
this.AwayBeerAdjustedPrice = ko.observable(data.AwayBeerAdjustedPrice);
this.AwayBeer = ko.observable(data.AwayBeer);
this.awayTeamImage = ko.observable(data.AwayTeamImage);
this.FullScore = ko.computed(function() {
return data.HomeTeamScore + " | " + data.AwayTeamScore;
}, this);
}
function vm() {
var self = this;
self.gameCollection = ko.observableArray([]);
}
getGames = function() {
var _vm = new vm();
$.ajax({
type: "GET",
dataType: "json",
url: "/Dashboard/PopulateMonitor/",
error: errorFunc,
success: function(data) {
_vm.gameCollection = [];
$.each(data, function() {
_vm.gameCollection.push(new Board(this));
});
}
});
function errorFunc() {
alert("Error, could not load gameboards");
}
ko.applyBindings(_vm);
}
The issue appears within the getGames() function on or around the line
_vm.gameCollection = [];
I appreciate any help available. Not very well versed with Knockout.js
Every time you're calling getGames you're creating a new '_vm':
getGames = function () {
var _vm = new vm();
Move var _vm = new vm(); to
$(document).ready(function () {
var _vm = new vm(); // <-- HERE
refreshPage();
});
Some lines have to be moved too, see the snippet :
$(document).ready(function() {
_vm = new vm();
refreshPage();
});
function refreshPage() {
getGames();
setTimeout(refreshPage, 10000);
console.log("Page refreshed");
};
function Board(data) {
this.gameChannel = ko.observable(data.GameChannel);
this.HomeTeamImage = ko.observable(data.HomeTeamImage);
this.HomeTeamName = ko.observable(data.HomeTeamName);
this.HomeBeerPrice = ko.observable(data.HomeBeerPrice);
this.HomeTeamArrow = ko.observable(data.HomeTeamArrow);
this.HomeBeer = ko.observable(data.HomeBeer);
this.HomeBeerAdjustedPrice = ko.observable(data.HomeBeerAdjustedPrice);
this.AwayTeamArrow = ko.observable(data.AwayTeamArrow);
this.AwayBeerPrice = ko.observable(data.AwayBeerPrice);
this.AwayTeamName = ko.observable(data.AwayTeamName);
this.AwayBeerAdjustedPrice = ko.observable(data.AwayBeerAdjustedPrice);
this.AwayBeer = ko.observable(data.AwayBeer);
this.awayTeamImage = ko.observable(data.AwayTeamImage);
this.FullScore = ko.computed(function() {
return data.HomeTeamScore + " | " + data.AwayTeamScore;
}, this);
}
function vm() {
var self = this;
self.gameCollection = ko.observableArray([]);
ko.applyBindings(this);
}
getGames = function() {
$.ajax({
type: "GET",
dataType: "json",
// placeholder:
url: 'data:application/json;utf8,[]',
//url: "/Dashboard/PopulateMonitor/",
error: errorFunc,
success: function(data) {
_vm.gameCollection.removeAll();
$.each(data, function() {
_vm.gameCollection.push(new Board(this));
});
}
});
function errorFunc() {
alert("Error, could not load gameboards");
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Couple of things:
You shouldn't call applyBindings more than once. So, move it outside of your setTimeout.
_vm.gameCollection = [] won't work. To clear your observableArray, use removeAll. You can also set it to an empty array like this: _vm.gameCollection([])
Also, if you want to call the same function after an interval of time, you can make use of setInterval.
Here's a minimal version of your code. Click on Run code snippet to test it out. I have created a counter variable which updates gameCollection with new data every second.
let counter = 0;
function refreshPage() {
getGames();
console.log("Page refreshed");
};
function Board(data) {
this.gameChannel = ko.observable(data.GameChannel);
}
function vm() {
var self = this;
self.gameCollection = ko.observableArray([]);
}
getGames = function() {
let data = [
{
GameChannel: `GameChannel ${++counter}`
},
{
GameChannel: `GameChannel ${++counter}`
}];
_vm.gameCollection.removeAll(); // <- Change here
data.forEach(function(item) {
_vm.gameCollection.push(new Board(item));
});
}
var _vm = new vm();
ko.applyBindings(_vm); // this needs to be only called once per page (or element)
setInterval(refreshPage, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<!-- ko foreach: gameCollection -->
<span data-bind="text: gameChannel"></span><br>
<!-- /ko -->
I have this grid that i am creating using knockoutjs, it works perfectly at first, now i am using a window.location.hash to run another query, it works too and query returns the correct amount of data however when i insert it within the observableArray (which gets inserted correctly as well), the grid doesn't update the data and shows the old data... I'm using removeAll() function on the observableArray as well before inserting new set of data but it wont update my grid, i suspect there is something wrong with the DOM?
I should mention when i reload the page (since the page's url keeps the hash for query) my grid shows the data and works perfectly. for some reason it needs to reload the page and doesn't work without,
Here is my JS:
if (!ilia) var ilia = {};
ilia.models = function () {
var self = this;
this.pageCount = ko.observable(0);
//this is the observableArray that i am talking about ++++++++
this.items = ko.observableArray();
var $pagination = null;
var paginationConfig = {
startPage: 1,
totalPages: 20,
onPageClick: function (evt, page) {
self.generateHash({ pageNum: page });
self.getData();
}
}
var hashDefault = {
pageNum: 1,
pageSize: 20,
catId: null,
search: ""
}
this.dataModel = function (_id, _name, _desc, _thumb, _ext) {
var that = this;
this.Id = ko.observable(_id);
this.Name = ko.observable(_name);
this.Desc = ko.observable(_desc);
this.Url = '/site/ModelDetail?id=' + _id;
var b64 = "data:image/" + _ext + ";base64, ";
this.thumb = ko.observable(b64 + _thumb);
}
this.generateHash = function (opt) {
//debugger;
var props = $.extend(hashDefault, opt);
var jso = JSON.stringify(props);
var hash = window.location.hash;
var newHash = window.location.href.replace(hash, "") + "#" + jso;
window.location.href = newHash;
return jso;
}
this.parseHash = function () {
var hash = window.location.hash.replace("#", "");
var data = JSON.parse(hash);
if (data)
data = $.extend(hashDefault, data);
else
data = hashDefault;
return data;
}
var _cntrl = function () {
var _hdnCatName = null;
this.hdnCatName = function () {
if (_hdnCatName == null)
_hdnCatName = $("hdnCatName");
return _hdnCatName();
};
var _grid = null;
this.grid = function () {
if (_grid == null || !_grid)
_grid = $("#grid");
return _grid;
}
this.rowTemplate = function () {
return $($("#rowTemplate").html());
}
}
this.createPagnation = function (pageCount, pageNum) {
$pagination = $('#pagination-models');
if ($pagination && $pagination.length > 0)
if (paginationConfig.totalPages == pageCount) return;
var currentPage = $pagination.twbsPagination('getCurrentPage');
var opts = $.extend(paginationConfig, {
startPage: pageNum > pageCount ? pageCount : pageNum,
totalPages: pageCount,
onPageClick: self.pageChange
});
$pagination.twbsPagination('destroy');
$pagination.twbsPagination(opts);
}
this.pageChange = function (evt, page) {
var hash = self.parseHash();
if (hash.pageNum != page) {
self.generateHash({ pageNum: page });
self.getData();
}
}
this.getData = function () {
var _hash = self.parseHash();
inputObj = {
pageNum: _hash.pageNum,
pageSize: _hash.pageSize,
categoryId: _hash.catId
}
//debugger;
//console.log(_hash);
if (inputObj.categoryId == null) {
ilia.business.models.getAll(inputObj, function (d) {
//debugger;
if (d && d.IsSuccessfull) {
self.pageCount(d.PageCount);
self.items.removeAll();
_.each(d.Result, function (item) {
self.items.push(new self.dataModel(item.ID, item.Name, item.Description, item.Thumb, item.Extention));
});
if (self.pageCount() > 0)
self.createPagnation(self.pageCount(), inputObj.pageNum);
}
});
}
else {
ilia.business.models.getAllByCatId(inputObj, function (d) {
if (d && d.IsSuccessfull) {
self.pageCount(d.PageCount);
self.items.removeAll();
console.log(self.items());
_.each(d.Result, function (item) {
self.items.push(new self.dataModel(item.ID, item.Name, item.Description, item.Thumb, item.Extention));
});
// initializing the paginator
if (self.pageCount() > 0)
self.createPagnation(self.pageCount(), inputObj.pageNum);
//console.log(d.Result);
}
});
}
}
this.cntrl = new _cntrl();
};
And Initialize:
ilia.models.inst = new ilia.models();
$(document).ready(function () {
if (!window.location.hash) {
ilia.models.inst.generateHash();
$(window).on('hashchange', function () {
ilia.models.inst.getData();
});
}
else {
var obj = ilia.models.inst.parseHash();
ilia.models.inst.generateHash(obj);
$(window).on('hashchange', function () {
ilia.models.inst.getData();
});
}
ko.applyBindings(ilia.models.inst, document.getElementById("grid_area"));
//ilia.models.inst.getData();
});
Would perhaps be useful to see the HTML binding here as well.
Are there any console errors? Are you sure the new data received isn't the old data, due to some server-side caching etc?
Anyhow, if not any of those:
Are you using deferred updates? If the array size doesn't change, I've seen KO not being able to track the properties of a nested viewmodel, meaning that if the array size haven't changed then it may very well be that it ignores notifying subscribers. You could solve that with
self.items.removeAll();
ko.tasks.runEarly();
//here's the loop
If the solution above doesn't work, could perhaps observable.valueHasMutated() be of use? https://forums.asp.net/t/2056128.aspx?What+is+the+use+of+valueHasMutated+in+Knockout+js
I create a factory where I put my http request for adding, retrieving and deleting tasks.
Now when I add a task you don't see the visual change. I have to refresh the browser. Now to fix this I figured I would write a function to check the length of the old array against the length of the new one and set an interval off 1 minutes on it to make this change and then pull in the tasks. I am able to log my length change of the array but nothing happens visually (still have to refresh the browser).
If anybody could help me out here.
Part of my app.js code:
global vars -> defaultStart, defaultEnd, clickDate
zazzleApp.factory('TaskService', function ($http) {
var TaskService = {};
TaskService.taskList = [];
TaskService.getTasks = function(cb){
$http.get('api/task/all')
.success(function(dataFromServer){
for (var i = 0; i < dataFromServer.length; i++) {
TaskService.taskList[i] = dataFromServer[i];
};
//console.log('LOGGING GET_TASK ', TaskService.taskList);
if(cb){
cb(dataFromServer);
}else{
return dataFromServer;
}
//return dataFromServer;
})
.error(function(errorFromServer){
//something went wrong, process the error here
console.log("Error in getting the users from the server ", errorFromServer);
})
};
TaskService.addTask = function(pTask){
var newClickDate = clickDate;
console.log('LOGGGING NEW CLICK DATE = ', newClickDate);
var newEditId = editId;
//console.log('LOGGGING NEW edit id = ', newEditId);
var url;
if (newEditId) {
url = 'api/task/update/' + newEditId;
} else {
url = 'api/task/create';
}
//console.log("URL URL USA", url, newEditId, newClickDate);
defaultStart = new Date(newClickDate);
defaultStart = defaultStart.getFullYear() + "-" + (defaultStart.getMonth() + 1) + "-" + defaultStart.getDate();
defaultStart += " 00:00:00";
defaultEnd = new Date(newClickDate).addDays(1);
defaultEnd = defaultEnd.getFullYear() + "-" + (defaultEnd.getMonth() + 1) + "-" + defaultEnd.getDate();
defaultEnd += " 00:00:00";
console.log('LOGGING DEFAULT START AND DEFAULT END ' , defaultStart, defaultEnd);
pTask.color = $('#containerColorPicker').attr('ng-data-id');
// returns makes sure the promis is returned from the server
return $http.post(url, {
'name': pTask.project_name,
'project_id': pTask.project_type,
'location_id': pTask.location,
'estimate_time': pTask.estimate_time || 2,
'project_client_name': pTask.project_client_name,
'url': pTask.url,
'resource_link': pTask.resource_link,
'notes': pTask.notes,
'start_time': pTask.start_time || defaultStart,
'end_time': pTask.end_time || defaultEnd,
/*'start_time': defaultStart,
'end_time': defaultEnd,*/
'color': pTask.color
}, {
headers: {
"Content-Type": "text/plain"
}
})
.success(function(data, status, headers, config) {
console.log(data);
TaskService.getTasks();
TaskService.taskList.push(data);//pushing the new task
//console.log("YYYYYYYYYYYYY -------->>>>>", defaultStart);
})
.error(function(data, status, headers, config) {
console.log("Failed to add the task to DB");
});
};
TaskService.deleteTask = function (){
var newEditId= editId;
$http.delete('api/task/delete/' + newEditId)
.success(function(dataFromServer){
console.log('logging edit id in delete taks func server ', newEditId);
var index;
for (var i = 0; i < TaskService.taskList.length; i++) {
if(TaskService.taskList[i]._id == newEditId){
//index = i;
console.log ("removing the element from the array, index: ", newEditId, i);
TaskService.taskList.splice(i,1);
}
};
/* if(editId !== -1){
console.log ("removing the element from the array, index: ", editId, index);
UserService.userList.splice(index,1);
}*/
console.log('TaskArray ', TaskService.taskList)
$('div[ng-data-id="'+ newEditId +'"]').remove();
})
.error(function(errorFromServer){
//something went wrong, process the error here
console.log("Error in deleting a user from the server");
})
};
return TaskService;
})
//START CONTROLLER
angular.module('zazzleToolPlannerApp')
.controller('CalendarCtrl', function ($scope, $mdDialog, $http, $rootScope, $timeout, User, Auth, UserService, TaskService) {
$scope.newTask = {};
$scope.newTask.project_name = "";
$scope.newTask.project_type = "";
$scope.newTask.location = "";
$scope.newTask.estimate_time = "";
$scope.newTask.project_client_name = "";
$scope.newTask.url = "";
$scope.newTask.resource_link = "";
$scope.newTask.notes = "";
$scope.newTask.color = "";
$scope.tasks = TaskService.taskList;
$scope.getTasksFromService = function () {
TaskService.getTasks(); //after this gets called, the data will be shown in the page automatically
}
$scope.getTasksFromService();
$scope.addTaskWithService = function () {
//note that you can process the promise right here (because of the return $http in the service)
TaskService.addTask($scope.newTask)
.success(function(data){
//here you can process the data or format it or do whatever you want with it
console.log("Controller: the task has been added");
$scope.tasks = [];// EMPTY THE ARRAY
$scope.tasks = TaskService.getTasks();
//console.log('Taskservice Controller ', $scope.updateGridDataAwesome);
})
.error(function(data){
//something went wrong
console.log("Controller: error in adding task");
});
}
$scope.deleteTaskWithService = function(){
TaskService.deleteTask();
}
TaskService.getTasks(function(data){
$scope.tasks = data;
});
var interval = setInterval(function(){
var oldLength = $scope.tasks.length;
TaskService.getTasks(function(data){
console.log('lengths', oldLength, data.length)
if(oldLength != data.length){
//$scope.tasks = data;
//TaskService.taskList.push(data);
$scope.tasks = TaskService.getTasks();
}
});
}, 6000)
This might be what you're looking for, by using $interval you can set an interval every x seconds:
$interval(function() {
// Something to be executed after 1min (60000ms)
}, 60000);
And then inject $interval into your factory:
zazzleApp.factory('TaskService', function ($http, $interval).....
Try this:
$http
.post("/api/pin", {})
.success(function(data) {
$scope.$apply(function() {
$scope.pins.push(data);
});
});
reference: https://stackoverflow.com/a/24836089/3330947
Update:
I did it like this:
Service (fetch all accounts):
.factory('accountService', function ($http) {
var accountObj = {
async: function () {
var promise = $http.get('account/').then(function (response) {
return response;
});
return promise;
}
};
return accountObj;
})
Controller:
//Call get accounts service and put all accounts in $scope.accounts
var getAccounts = function () {
accountService.async().then(function (d) {
$scope.accounts = d.data;}
}
//Create new account and update array of accounts
$scope.createAccount = function () {
$scope.data = {
'id' : 0,
'customerId' : $scope.outputCustomer[0].id,
'camsId' : $scope.outputCams[0].id,
'camsPin' : parseInt($scope.camsPin),
'username' : $scope.username,
'password' : $scope.password,
'email' : $scope.email,
'acfUsername' : $scope.acfUsername,
'accountActive' : $scope.accountActive,
'agentId' : $scope.outputAgent[0].id,
'freeswitchIds' : freeswitchIds
};
$http.post('account/save', $scope.data).success(
function (data, status) {
$scope.accounts.push(data);
}).error(function () {
});
};
My add function is in controller, i thin it can be redone to be service but this work soo
The problem may be in success of addTask. TaskService.getTaskis async. So the execute order will be: 1. http request in TaskService.getTasks 2. TaskService.taskList.push(data); 3. $http success callback.
.success(function(data, status, headers, config) {
console.log(data);
TaskService.getTasks();
TaskService.taskList.push(data);//pushing the new task
//console.log("YYYYYYYYYYYYY -------->>>>>", defaultStart);
})
In this order, step 2 is pointless. Because step 3 code may override TaskService.taskList
for (var i = 0; i < dataFromServer.length; i++) {
TaskService.taskList[i] = dataFromServer[i];
};
And another wrong place:
$scope.tasks = TaskService.getTasks();
this line code appears twice. But TaskService.getTasks() returns undefined because you don't put keyword return.
In addition, your practice to use promise is wrong. $http leverages promise specification. Don't use callback since you used promise. You may need read promise docs.
On first load images and blocks overlap but after that it works fine
<div id="container" class="isotope" data-bind="isotope: bills, isotopeOptions: isotopeOptions ">
<div class="bill-subcategory-image"><img data-bind="style: { width: '100%'}, attr : { src: categoryimage() } " /></div>
Getting Data from Json and rendering at above divs
$(function (){
function Bill(id, billNo, votes, description, category, categoryimage, name, itemType) {
var self = this;
self.id = ko.observable(id);
self.billNo = ko.observable(billNo);
self.description = ko.observable(description);
self.votes = ko.observable(votes);
self.category = ko.observable(category);
self.categoryimage = ko.observable(categoryimage);
self.name = ko.observable(name);
self.itemType = ko.observable(itemType);
self.RandomHeight = ko.observable(Math.floor(Math.random() * 200) + 150);
self.voteFor = function () {
console.log("voting for: %s", self.billNo());
forUs.vote(self.id(), 'for').done(function (data, textStatus, jqXHR) {
console.log("you have voted for %s", self.billNo());
});
};
// values for isTracked and isShared is dynamic
self.isTracked = ko.observable(true);
self.isShared = ko.observable(true);
self.trackClass = ko.computed(function () {
if (self.isTracked()) {
return 'active';
}
else {
return 'inactive';
}
});
self.shareClass = ko.computed(function () {
if (self.isShared()) {
return 'active';
}
else {
return 'inactive';
}
});
self.voteAgainst = function () {
console.log("vote against: %s", self.billNo());
forUs.vote(self.id(), 'against').done(function (data, textStatus, jqXHR) {
console.log('you have voted against %s', self.billNo());
});
};
self.track = function () {
var identity = (self.billNo() || self.name());
forUs.track(self.id()).done(function (data, textStatus, jqXHR) {
self.isTracked(!self.isTracked());
console.log('you %s tracking %s', self.isTracked() ? 'are now' : 'stopped', identity);
});
};
self.share = function () {
var identity = (self.billNo() || self.name());
forUs.share(self.id()).done(function (data, textStatus, jqXHR) {
self.isShared(!self.isShared());
console.log('you %s sharing %s', self.isShared() ? 'are now' : 'stopped', identity);
});
};
self.billUrl = ko.computed(function () {
return "../Bill/" + self.billNo();
});
self.popupUrl = ko.computed(function () {
return "../" + self.itemType() + "/Popup/" + self.id();
});
//console.log(self.categoryimage());
};`
// getting records from JSON file
`function ViewModel() {
var self = this;
self.bills = ko.observableArray();
var $container = $('#container');
$container.bill-subcategory-image(function () {
$container.isotope({
itemSelector: '.item',
layoutMode: 'masonry'
});
});
self.isotopeOptions = {
itemSelector: '.item',
masonry: {
columnWidth: 280
}
};
self.count = 0;
$.getJSON(url + "1", function (json) {
for (var i = 0; i < json.length; i++) {
var bill = new Bill(
json[i].Id,
json[i].BillNo,
json[i].Votes,
json[i].Description,
json[i].Category,
json[i].CategoryImage,
json[i].Name,
json[i].ItemType
);
self.bills.push(bill);
}
});
self.Add = function (newData) {
var newItems = ko.utils.arrayMap(newData, function (item) {
return new Bill(item.Id, item.BillNo, item.Votes, item.Description, item.Category, item.CategoryImage, item.Name, item.ItemType);
});
self.bills.push.apply(self.bills, newItems);
self.count = self.count + newItems.length;
console.log("added " + newItems.length + " items! Total added since start = " + self.count);
};
};`
//if page scroll than more isotopes render
var vm = new ViewModel();
$(window).paged_scroll({
handleScroll: function (page, container, doneCallback) {
$.getJSON(url + page, function (json) {
//console.log("got data, adding...");
vm.Add(json);
//console.log("finished adding!");
doneCallback();
});
},
triggerFromBottom: '200px',
targetElement: $('#container'),
loader: '<div class="loader">Loading next page...</div>',
monitorTargetChange: false
});
//knockout binding
ko.applyBindings(vm, $('#container')[0]);
});
}
For images, try using imagesloaded.js
var $container = $('#container');
$container.bill-subcategory-image(function () {
$container.imagesLoaded( function(){
$container.isotope({
itemSelector: '.item',
layoutMode: 'masonry'
});
});
});
I would like to use knockout js to enable scroll pagination
Problem
I would like to pass in url and id into my `GetPage(controller, id#, page#))
Currently it is hard coded but i would like to change that.
Knockout js
$.views.Roster.GetPage = function (url, id, pageNumber) {
$.grain.Ajax.Get({
Url: url,
SectionID: {id:id},
DataToSubmit: { pageNumber: pageNumber, id: id },
DataType: "json",
OnSuccess: function (data, status, jqXHR) {
$.views.Roster.RosterViewModel.AddUsers(data);
}
});
};
Next = function () {
var _page = $.views.Roster.ViewModel.CurrentPage() + 1;
$.views.Roster.ViewModel.CurrentPage(_page);
$.views.Roster.GetPage("/api/Roster", 9, _page);
}
Scroll pagination
$(document).ready(function(){
$('#main').scroll(function () {
if ($('#main').scrollTop() >= $(document).height() - $('#main').height()) {
$('#status').text('Loading more items...' + $.views.Roster.ViewModel.TotalRoster());
if ($.views.Roster.ViewModel.RosterUsers() == null ) {
$('#status').hide();
$('#done').text('No more items...'),
$('#main').unbind('scroll');
}
setTimeout(updateStatus, 2500);
}
//updateStatus();
});
});
Change the data in getRoster function to what your server function is expecting for you to return the data. Also, remove the code $.views.Roster.GetRoster, it is not required anymore. Now when you do ko.applyBindings(new $.views.Roster.RosterViewModel()); you should get the first page of data, subsequently, when you scroll, the next() call on the view model will continue paging. That logic is all you.
$.views.Roster.RosterViewModel = function (data) {
var self = this;
self.RosterUsers = ko.observableArray([]);
_rosterUsers = self.RosterUsers;
self.currentPage = ko.observable(1);
self.toDisplay = ko.observable(10);
var filteredRoster = ko.computed(function(){
var init = (self.currentPage()-1)* self.toDisplay(),
filteredList = [],
rosterLength = self.RosterUsers().length,
displayLimit = self.toDisplay();
if(rosterLength == 0)
return[];
for(var i = init; i<(displayLimit + init) && i<rosterLength; i++)
{
filteredList.push(self.RosterUsers()[i]);
}
return filteredList;
}),
totalRoster = ko.computed(function () {
return self.RosterUsers().length;
}),
changePage = function (data) {
self.currentPage(data);
},
next = function () {
if ((self.currentPage() * self.toDisplay()) > self.RosterUsers().length)
return;
self.currentPage(self.currentPage() + 1);
},
prev = function () {
if (self.currentPage() === 1)
return;
self.currentPage(self.currentPage() - 1);
},
getRoster = ko.computed(function () {
var data = {
currentPage: self.currentPage(),
pageSize: self.toDisplay()
},
$promise = _makeRequest(data);
$promise.done(function (data) {
var localArray = [];
ko.utils.arrayForEach(data, function(d){
localArray.push(new $.views.Roster.UserViewModel(d));
});
self.RosterUsers.push.apply(self.RosterUsers,localArray);
});
}),
_makeRequest = function(data){
return $.getJSON('your url here', data);
};
};