I have below code to pull data from HTTP service and bind JSON data to HTML,
This function was placed with a 2 seconds timer to pull automatically when ever there is a change in the backend.
function sampleDevices() {
var devicesArray = [];
for(id in devices) {
var device = devices[id];
var pollingApiUrl = "DBService.asmx/HelloWorld";
$http({ method: 'GET', url: pollingApiUrl, params: { id: id } })
.then(function (response) { // Success
device.status= response.data.JsonStatus;
device.comp = response.data.CompanyName;
device.loc = response.data.ReceiverLocation});
}
$scope.devices = devicesArray; }
<tr ng-repeat="device in devices |orderBy: '-event.rssi' limitTo: 3">
<td> {{device.comp}} </td>
<td> {{device.loc}} </td>
<td> {{device.test }} </td>
</tr>
I encounter a problem with response data, only last JSON success data always gets updated.
for eg, output like
A IN OK --1st time get the value and does not change
B OUT OK -- update the changes when occurring in back end
not able to get the correct response and bind the corresponding value to the list.
How to solve this issue, to get the value from JSON and update correct value to HTML tag with 2 or 3 seconds timer
function sampleDevices() {
var devicesArray = [];
var i=0;
for(i=0; i < devices.length; i++) {
var device = {};
var pollingApiUrl = "DBService.asmx/HelloWorld";
$http({ method: 'GET', url: pollingApiUrl, params: { id: devices[i].id } })
.then(function (response) { // Success
device.status= response.data.JsonStatus;
device.comp = response.data.CompanyName;
device.loc = response.data.ReceiverLocation;
devicesArray.push(device);
});
}
$scope.devices = devicesArray; }
<tr ng-repeat="device in devices |orderBy: '-event.rssi' limitTo: 3">
<td> {{device.comp}} </td>
<td> {{device.loc}} </td>
<td> {{device.test }} </td>
</tr>
You are doing a http request multiple times in a for loop.
The for loop doesn't wait, it will trigger all requests in quick succession. To make the thing wait look into the async library and the each function https://caolan.github.io/async/docs.html#each
In the success cashback of async you would then set the scope.
Or... Change the backend so you only do one API call, this would be even better.
Related
I have some complication with service removing. I have function that removes service on the server but I have to reload page to update table. I found way how to remove row by click-binding but there is the issue beacuse I can only remove row or get ID for delete service from server NOT both. :/
This is example of code that removes service on the server but doesn't remove table row.
HTML:
<table id="serviceView" class="fixed_header" border: 1>
<thead>
<tr>
<th>Name</th>
<th>Adress</th>
<th>Notification</th>
</tr>
</thead>
<tbody data-bind="foreach: services">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: address"></td>
<td data-bind="text: serviceId"></td>
<td ><button data-bind="click: $parent.DeleteService.bind(this, serviceId)">Remove</button></td>
</tr>
</tbody>
</table>
JS:
self.services = ko.observableArray([]);
self.lastCheck = ko.observable();
$.getJSON("http://localhost:55972/api/status", function (data) {
self.services(data.services);
self.lastCheck = data.lastCheck;
}); //////This is loading data to the table from server
self.DeleteService = function (serviceId) {
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
self.services.remove(serviceId)
})
};
This is example of code that removes table row
When I use click-binding like this:
<button data-bind="click: $parent.DeleteService">Remove</button>
And change delete function to this:
self.DeleteService = function (serviceId) {
self.services.remove(serviceId)
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
// here I want to remove row but i doesnt goes here without service ID.
})
};
It removes row but instead serviceId I got [object, object] in the URL.
Can you help me with it ? I got idea to use jquery to just update the table but it's seems unnecessarily complicated for me when I can use knockout.
I know the solution is not that hard but I'am just unable to solve it..... -_-
I'am sorry for taking time with this bullshit but this is my first real project and I'am so desperate at this point beacuse I have lot of things to do and I'am stucked on this.
In your Js code, you can try this:
self.services = ko.observableArray([]);
self.lastCheck = ko.observable();
$.getJSON("http://localhost:55972/api/status", function (data) {
self.services(data.services);
self.lastCheck = data.lastCheck;
}); //////This is loading data to the table from server
var serviceIdRemoved;
self.DeleteService = function (serviceId) {
serviceIdRemoved = serviceId; // now you can do whatever you need more with this value
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
self.services.remove(serviceId)
})
};
With this way of work you can user the content of the variable and donĀ“t loose it. Also if you get [Object, Object], you can:
console.log(serviceId) // to see the content in the console.
JSON.stringify(data) //to see the content in html
This source could help you to understand it better.
The [object, object] you are seeing is actually the data and event objects which are secretly added to the JS function parameters by Knockout. If you want to add your own parameter to the click binding then you should do it like this:
<button data-bind="click: function(data, event) { $parent.DeleteService(serviceId, data, event) }">Remove</button>
You can then define your JS function as follows:
self.DeleteService = function (serviceId, data, event) {
[code here...]
}
You can read up on the exact details of it in the excellent Knockout documentation here:
http://knockoutjs.com/documentation/click-binding.html
It's about half-way down under the heading that reads Note 2: Accessing the event object, or passing more parameters
I'm having issues refreshing data that has been POSTed using Ajax. The POST is successfully being executed, but the data on the VIEW does not get refreshed with the new data. When I debug, the values from the Ajax POST are successfully being passed to my Search controller. When the controller returns the view model return View(model);, my VIEW is not refreshing the new data. How do I get the new data to show in my VIEW?
Ajax/jQuery
$(document).ready(function () {
$("#typeId, #scorecardId, #dateId").on('change', function () {
$.ajax({
url: "/StaffChange/Search",
type: "POST",
dataType: "json",
data: {
typeSelected: $('#typeId').val(),
scorecardSelected: $('#scorecardId').val(),
effectiveDateSelected: $('#dateId').val()
}
})
});
});
View
<table class="table table-condensed table-hover table-responsive table-striped">
<tr>
<th>
<a href="#Html.GetUrlAndRouteObject(Model.Sort, "change_date")">
Change Date
#Html.AddSortArrow(Model.Sort, "change_date")
</a>
</th>
<th>
#Html.EditorFor(model => model.effectiveDate, new { htmlAttributes = new { #class = "datepicker", #placeholder = "Effective Date", #id = "dateId" } })
</th>
<th>
#Html.DropDownListFor(model => model.type, new SelectList(Model.type), "-Type-", new { #id = "typeId" })
</th>
<th>
#Html.DropDownListFor(model => model.type, new SelectList(Model.scorecard), "-Scorecard-", new { #id = "scorecardId" })
</th>
</tr>
#foreach (var item in Model.get_staff_changelog_results)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Change_Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Effective_Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Type)
</td>
<td>
#Html.DisplayFor(modelItem => item.Scorecard)
</td>
</tr>
}
Controller
public ActionResult Search(string sort, string typeSelected, string scorecardSelected, DateTime? effectiveDateSelected)
{
//TO DO: Implement MVC way of permissions...
if (System.Web.HttpContext.Current.Session["ConnectelligenceAdmin"].ToString() != "true")
{
return View("AccessDenied");
}
//Get sort order for Change Date using BLL
var staffChangeSort = (String.IsNullOrEmpty(sort)) ? SortOptions.change_date_desc : (SortOptions)Enum.Parse(typeof(SortOptions), sort);
//Execute sort order for Change Date using BLL
var sortResults = _changeLogSort.SortStaffChangeLog(staffChangeSort,typeSelected, scorecardSelected, effectiveDateSelected);
//Get list of dropdown results which is used for filtering
var dropdownResults = _staffChangeFilter.StaffChangeFilter();
var model = new Hierarchy_AdjustmentViewModel { get_staff_changelog_results = sortResults.get_staff_changelog_results, Sort = staffChangeSort, type = dropdownResults.type, scorecard = dropdownResults.scorecard};
return View(model);
}
You need to use the response you receive from your ajax call. Currently your Search method is returning a view result. What you can do is, if the method is invoked from an xhr call ,you may return a partial view result which has only the markup for table rows (with the new set of data) and in your ajax call's done event, update the DOM (replace the existing table rows with this new markup).
So first create a partial view called _List.cshtml and have the code to render the table rows there
#model Hierarchy_AdjustmentViewModel
#if(Model.get_staff_changelog_results!=null)
{
foreach (var item in Model.get_staff_changelog_results)
{
<tr>
<td> #item.Change_Date </td>
<td> #item.Effective_Date </td>
<td> #item.Type </td>
<td> #item.Scorecard </td>
</tr>
}
}
You can use the same partial view in your main view as well to reduce duplicate code
Now update your action method to return this partial view when the request was made from ajax code
public ActionResult Search(string sort, string typeSelected)
{
// Your existing code to get data goes here
var model = new Hierarchy_AdjustmentViewModel();
mode.get_staff_changelog_results = sortResults.get_staff_changelog_result;
if (Request.IsAjaxRequest())
{
return PartialView("_List", model);
}
return View(model);
}
Now all you have to do is, use this response to update the table. Since you are returning view result (HTML markup), you do not need to specify dataType as json.
$("#typeId, #scorecardId, #dateId").on('change', function () {
var $tbl = $(this).closest("table");
$tbl.find("tbody").find("tr:gt(0)").remove();
$.ajax({
url: "#Url.Action("Search","StaffChange")",
type: "POST",
data: {
typeSelected: $('#typeId').val(),
scorecardSelected: $('#scorecardId').val(),
effectiveDateSelected: $('#dateId').val()
}
}).done(function(res) {
$tbl.find("tbody").append(res);
}).fail(function(x, a, e) {
alert(e);
});
});
Another option is returning the data as a JSON array, and the done handler has to parse it (loop through it) and create markup for each row and append to the table (after clearing existing rows).
Because you're not responding to the AJAX call in any way. Add a .done() callback handler to the AJAX call:
$.ajax({
/*...*/
}).done(function (response) {
// update the page in here
});
At that point the question becomes... What sort of updates do you plan to do? It looks like you're returning a view from the controller:
return View(model);
So the data you're getting back in the AJAX response is a bunch of raw HTML. If it's a subset of a full page (that is, no additional <head>, <body>, etc. tags) then you could replace an existing container element with the contents of the response:
$('#someContainer').html(response);
However, this tends to be a bit sloppy and can cause other problems. (For example, replacing DOM elements which have handlers attached to them or plugins initialized on them, requiring you to re-think how you approach some things.) Instead, for AJAX calls it's common to return JSON data:
return Json(model);
This returns just the data instead of all the HTML surrounding this. This is useful for a couple of reasons:
It's easier to manipulate if you only want to do small and specific things.
It uses less network bandwidth.
It's just plain silly to return a bunch of HTML that the page already has.
With that data you can then update the specific elements on the page. For example, maybe you want to update the value of an input:
$('#someInput').val(response.SomeProperty);
Or the text of a display element:
$('#someElement').text(response.AnotherProperty);
How you handle the response and what you need to do to your page to "update it" is up to you. The point is that the system doesn't automatically do this for you. You need to handle the AJAX response and write your logic accordingly.
I am rendering data using ng-repeat through GET request, which retrieves an array.
HTML
<div ng-controller="candidateCtrl" >
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>NO</th>
<th>NAMA</th>
<th>NIP</th>
<th>INSTANSI</th>
<th><span ng-show="animateCandidateAdmin" class="ion-load-a"></span></th>
</tr>
</thead>
<tbody ng-repeat="candidate in candidatesAdmin">
<tr class="well whel">
<td>{{$index + 1}}</td>
<td>{{candidate.candidate_name}}</td>
<td>{{candidate.candidate_nip}}</td>
<td>{{candidate.candidate_institusi}}</td>
<td>
<button class="btn btn-xs btn-success" ng-show="candidate.m_assesment_assesment_id == NULL" ng-click="addCandidate3(candidate.candidate_id)">
</td>
</tr>
</tbody>
</table>
</div><!-- OFF-MAINBAR -->
<div ng-repeat="item in percentage_penilaian" >
<div id="candidate_{{item.m_assesment_assesment_id}}" >
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>NO</th>
<th>NAMA</th>
<th>NIP</th>
<th>INSTANSI</th>
<th>BOBOT</th>
<th>SKOR</th>
<th>NILAI</th>
<th><span ng-show="animateCandidateManagerial" class="ion-load-a"></span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="candidate in candidates[item.m_assesment_assesment_id]" class="well whel">
<td>{{$index + 1}}</td>
<td>{{candidate.candidate_name}}</td>
<td>{{candidate.candidate_nip}}</td>
<td>{{candidate.candidate_institusi}}</td>
<td>{{candidate.percentage}}%</td>
<td ng-show="candidate.skor != NULL">
<button ng-click="$eval(arrAddCandidate[percentage_penilaian[$parent.$index+1].m_assesment_assesment_id])(candidate.candidate_id)"><i class="ion-arrow-right-a"></i> Mengikuti {{percentage_penilaian[$parent.$index+1].assesment_name}}</button>
</td>
</tr>
</tbody>
</table>
</div><!-- OFF-MAINBAR -->
</div>
</div>
</div>
</div>
JS
<script>
var SITEURL = "<?php echo site_url() ?>";
var selectionApp = angular.module("selectionApp", ["isteven-multi-select"]);
selectionApp.controller('candidateCtrl', function ($scope, $http) {
$scope.candidates = [];
$scope.arrAddCandidate = [];
$scope.getPercentagePenilaian = function () {
var url = SITEURL + 'xxx/xxx/' + 14;
$http.get(url).then(function (response) {
$scope.percentage_penilaian = response.data;
for(var i in response.data){
$scope.arrAddCandidate[response.data[i].m_assesment_assesment_id] = "addCandidate"+response.data[i].m_assesment_assesment_id;
}
})
};
$scope.getCandidateAdmin = function () {
var url = SITEURL + 'api/get_candidate_admin/' + 14;
$http.get(url).then(function (response) {
$scope.candidatesAdmin = response.data;
})
};
$scope.get_3 = function () {
var url = SITEURL + 'xxx/xxx/3__14';
$http.get(url).then(function (response) {
$scope.$apply(function () {
$scope.candidates[3] = response.data;
// $scope.candidates.push(response.data);
});
})
};
$scope.addCandidate3 = function (id) {
$scope.animateCandidateAdmin = true;
var postData = $.param({
candidate_id: id,
assesment_id: 3 });
$.ajax({
method: "POST",
url: SITEURL + "xx/xxx/xxxxx",
data: postData,
success: function (response) {
if(response=='sukses'){
$scope.animateCandidateAdmin = false;
$scope.getCandidateAdmin();
$scope.get_3();
}
}
});
};
$scope.get_5 = function () {
var url = SITEURL + 'xx/xxx/5__14';
$http.get(url).then(function (response) {
$scope.$applyAsync(function () {
$scope.candidates[5] = response.data;
// $scope.candidates.push(response.data);
});
})
};
$scope.addCandidate5 = function (id) {
$scope.animateCandidateAdmin = true;
var postData = $.param({
candidate_id: id,
assesment_id: 5 });
$.ajax({
method: "POST",
url: SITEURL + "xx/xxx/xxxxx",
data: postData,
success: function (response) {
if(response=='sukses'){
$scope.animateCandidateAdmin = false;
$scope.getCandidateAdmin();
$scope.get_5();
}
}
});
};
angular.element(document).ready(function () {
$scope.getPercentagePenilaian();
$scope.get_3;
$scope.get_5;
});
});
</script>
Response from $scope.getCandidateAdmin
[{"candidate_id":"24","candidate_name":"contoh","candidate_nip":"12345","candidate_institusi":"Institusi A","selection_selection_id":"14"}]
Response $scope.getPercentagePenilaian
[{"id":"14","m_assesment_assesment_id":"3","percentage":"50"},
{"id":"15","m_assesment_assesment_id":"5","percentage":"10"}]
Response from $scope.get_3
[{"id":"43","selection_selection_id":"14","m_assesment_assesment_id":"3"
,"candidate_id":"24","m_candidate_id" :"1","candidate_name":"contoh","candidate_nip":"12345","candidate_institusi":"Institusi A","competency_skor":null}]
After I adding candidate, I believe that the $scope.candidates array is updated correctly, however the table in my view does not change. I don't know what i'm doing wrong.
Potential Issue
I think 'm_assesment_assesment_id' is stored as a String and not a Integer, which is the root of your problem.
[
{ "id":"14",
"m_assesment_assesment_id":"3", // Note: the numbers are wrapped in quotes
"percentage":"50"
},
{ "id":"15",
"m_assesment_assesment_id":"5", // JSON parsers will interpret these as Strings, NOT numbers
"percentage":"10"}
]
Background to Understanding Problem
Since the ng-repeat is using the item.m_assesment_assesment_id property, Angular resolves these as Strings. When this String is passed into the Array, the Array is interpreted as a JavaScript Object and not an array. For example:
// Both the following evaluate to the same thing
// Using dot accessor
item.m_assesment_assesment_id
// Using square brackets to access data through Object Notation
item["m_assesment_assesment_id"]
This is why JavaScript interprets your Array as an Object, using item.m_assesment_assesment_id as a key and not and index.
<!-- item.m_assesment_assesment_id will evaluate as a String as the JSON data specifies the String format, triggering Object evaluation, not Array evaluation -->
<tr ng-repeat="candidate in candidates[item.m_assesment_assesment_id]" class="well whel">
Here is some good overview of Array Evaluation:
https://www.w3schools.com/js/js_arrays.asp
Associative Arrays Many programming languages support arrays with
named indexes.
Arrays with named indexes are called associative arrays (or hashes).
JavaScript does not support arrays with named indexes.
In JavaScript, arrays always use numbered indexes.
Solutions
There are a couple of solutions. If you have access to manipulating the JSON formatting, I would recommend correcting the problem there:
{
"id":"14",
"m_assesment_assesment_id": 3, // No quote == Integer
"percentage":"50"
}
But this would require you to have access to the server code constructing the response and there may be architectural reasons for using a string.
Alternatively, you could change your ng-repeat:
<tr ng-repeat="candidate in candidates[parseInt(item.m_assesment_assesment_id)]" class="well whel">
However there is a performance loss as this requires more processing during Angular's $digest() event. This will not be significant for small datasets, but it is worth noting.
If you don't have access to changing the JSON formatting on the server and you don't want to put extra load on the $digest event, your control could process the response data from the AJAX calls. This is often the approach recommended by Angular documentation.
angular.module("selectionApp", ["isteven-multi-select"])
.controller('candidateCtrl', function ($scope, $http) {
$scope.candidates = [];
$scope.arrAddCandidate = [];
$scope.getPercentagePenilaian = function () {
var url = SITEURL + 'xxx/xxx/' + 14;
$http.get(url).then(function (response) {
// USE OUR CONVENIENCE METHOD HERE TO CONVERT STRINGS TO INTEGERS
$scope.percentage_penilaian = processPercentagePenilaian(response.data);
for(var i in response.data){
$scope.arrAddCandidate[response.data[i].m_assesment_assesment_id] = "addCandidate"+response.data[i].m_assesment_assesment_id;
}
})
};
// *** MORE of your code exists between these functions
// ALSO YOU HAVE TO CALL THIS IN THE CALLBACK OF THE APPROPRIATE AJAX CALLBACK
var processPercentagePenilaian = function(items) {
// Loop through the Items
angular.forEach(items, function(item, key) {
// For Every Item, convert the m_assesment_assesment_id to an Integer
item.m_assesment_assesment_id = parseInt(item.m_assesment_assesment_id);
});
// End Controller
});
I have a api call who give me the list of data, and I am iterating data via ng-repeat (its a list of more than 100 items)
For getting list of data I have call an Api in App Controller in angularjs like this:
var path = serverUrl + 'api/getAllMails';
$http.get(path).then(function (result) {
$scope.mails=result
})
For Iterating the mails in Html file i have use table like the below
<table>
<tr class="header">
<th class="center">Id</th>
<th class="center">Mode of Payment</th>
<th class="center">Payment Collected</th>
<th class="center">Status</th>
</tr>
<tr ng-repeat="mail in mails">
<td>{{mail.id}}</td>
<td>{{mail.paymentType}}</td>
<td>Rs. {{mail.cost}}
<input type="text" ng-model="mail.cost">
<button ng-click="updateCost=(mail.id, mail.cost)">Update Cost</button>
</td>
<td>{{mail.status}}
<input type="text" ng-model="mail.status">
<button ng-click="updateStatus(mail.id, mail.status)">Update Status</button>
</td>
</tr>
</table>
Suppose in the first iterations the cost will be "100" and the status will be "pending". And I have to update this row only, change cost to "1000" and status will be "Delivered".
In my App controller of Angularjs I have create methods. These two methods are calling apis and updating data in database and return the list of updated mails.
$scope.updateStatus = function(mailId, mailStatus) {
var path = serverUrl + 'api/updateStatus';
$http.get(path, {
params: {
mailId: mailId,
mailStatus: mailStatus
}
}).then(function(result) {
$scope.mails = result
})
}
$scope.updateCost = function(mailId, mailCost) {
var path = serverUrl + 'api/updateStatus';
$http.get(path, {
params: {
mailId: mailId,
mailCost: mailCost
}
}).then(function(result) {
$scope.mails = result
})
}
These code are working fine but while it took lot of time to load a page. So what can I do to reduce the loading time or is there any better way to do the same thing.
Any help will be appreciable. Thank you
You are replacing the entire dataset when there is no reason for that, you should only update the row you change. Ensure your updateStatus return the object you update and update that item in $scope.mails
In example
$scope.updateCost = function(mailId, mailCost) {
var path = serverUrl + 'api/updateStatus';
$http.get(path, {
params: {
mailId: mailId,
mailStatus: mailCost
}
}).then(function(result) {
// result is the item you changed
for (var i = $scope.mails.length - 1; i >= 0; i--) {
if($scope.mails[i].id === mailId) {
$scope.mails[i] = result;
return;
}
};
})
}
I have a knockout observableArray bound to a table below.
<table id="Users">
<thead>
<tr>
<td>User Name</td><td>Primary Email</td><td>Product Role</td><td>Active</td><td>EditUser?</td>
</tr>
</thead>
<tbody data-bind="foreach: CustomerUsers">
<tr>
<td data-bind="text: UserName"></td>
<td data-bind="text: PrimaryEmail().EmailAddress"></td>
<td><select></select></td>
<td data-bind="text: StaticActiveText"></td>
<td>Edit User</td>
</tr>
</tbody>
</table>
And the following view model:
function CustomerAdminVm() {
var vm = this
this.CustomerUsers = ko.observableArray(GetCustomerUsers());
}
My problem is that when the view model initially loads GetCustomerUsers it correctly gets the values it needs, and inserts them into the observable array. However, the elements are not displayed in the table.
The strange thing is that if I call the following function:
this.AddUserToCustomer = function () {
if (vm.NewUser) {
vm.CustomerUsers.push(vm.ActiveUser());
}
vm.CloseUserModalDialog();
}
The user is added to the array, and correctly displayed in the table. Even more confusing is that the latest value of the array in the push shows the initial values that are not displayers are in the array.
Does anyone have any idea what could be causing this behavior?
The code for GetCustomerUsers is:
function GetCustomerUsers() {
var users = [];
$.ajax({
type: 'Get',
url: ControllerBase + 'Actions/GetAllUsersForCustomer',
async: false,
success: function (data) { users = $.map(data, function(item) { return new ObservableUser(item); }); }
});
return users;
}
And ActiveUser is also an ObservableUser.
What you should do is to create the ko variable in the constructor and then update it with the ajax values when the data arrives.
function CustomerAdminVm() {
var vm = this
this.CustomerUsers = ko.observableArray();
GetCustomerUsers(this.CustomerUsers);
}
function GetCustomerUsers(getData) {
$.ajax({
type: 'Get',
url: ControllerBase + 'Actions/GetAllUsersForCustomer',
async: false,
success: function (data) { getData($.map(data, function(item) { return new ObservableUser(item); })); }
});
}
Ok I actually found a work around for this. Instead of populating the data as part of the constructor I add it after the document is ready
function CustomerAdminVm() {
var vm = this
this.CustomerUsers = ko.observableArray();
$(document).read(function(){vm.CustomerUsers(GetCustomerUsers());});
}
I'm going to throw a theory out as to what is actually happening, feel free to tell me I am wrong, but the solution does work.
What I believe to be happening is the data is being populated in the viewmodel constructor. But because the call is a synchronous call the constructor is not completing before the data is being populated. then the apply bindings function is being called, but because the data is prepoulated before the apply bindings function comes back the data already in the object is ignored because it has no observable change. During a normal async operation the call the constructor completes, then the apply bindings function is applied, and then the data comes back and the observable is changed causing an event flag to update the view.