ng-table not working for dynamic data - javascript

I have created a table and I am using http to load the data in the tables. So, in every click, my table data is changing, but I don't see the updated data in the table.
I had created a sample Plunker for the reference. In my project, WHen I click on Reload New Data, the data in table get's changed, but after 2-3 click it doesn't change. DId anyone know, how to fix it..

It is a problem with the ngTable directive. It updates only when data.length changes. Take a look at this plunk. I set $scope['tableParams1'] to null and inside the $timeout I set the new data. This forces angularJs to do a new cycle. So in the first cycle the ngTable sees the data.length changed to 0 and in the new cycle the ngTable sees the data.length changed again. If you don't use the $timeout, the ngTable will see that the data.length remains the same as before and won't do nothing.

With some trial and error I found a seemingly better solution to the issue than indicated in the plunkrs. For clarity, I am using $resource in a service to fetch my data. When I add a record via a modal, at first it wouldn't upload the ng-table after closing the modal. I figured out a way that works for me:
// Get data from factory
var data = dataFactory.query();
//Use promise to load data to table, note the replacing of the second tableParams
//object parameter with a function
data.$promise.then(function (data){
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10,
sorting: {
name: 'asc'
},
filter: {
name: undefined
}
}, resetTableParams()
);
});
//The function that replaces the tableParams param
var resetTableParams = function(){
return {
total: data.length,
getData: function($defer, params) {
var filteredData = params.filter() ? $filter('filter')(data, params.filter()) : data;
var orderedData = params.sorting() ? $filter('orderBy')(data, params.orderBy()) : filteredData;
params.total(orderedData.length);
$defer.resolve($scope.data = orderedData.slice((params.page() -1) * params.count(), params.page() * params.count()));
}
}
}
//A method to update the table that can be called when closing a modal
var updateTable = function(){
data = dataFactory.query();
data.$promise.then(function (data){
$scope.tableParams.reload();
});
}
// Add table row to table in modal and call updateTable() on closing modal
$scope.addRow = function(){
var modalInstance = $modal.open({
templateUrl: 'resources/partials/newrecord.html',
controller: NewRecordModalCtrl
})
modalInstance.result.then(function(){
updateTable();
});
}
Unfortunately, I can't give a clear explanation as to why this works and other methods don't. For instance, if you would not use the resetTableparams() function but leave it hardcoded, the table does not update. Somehow, Angular's digest cycle likes this better :) If somebody has a good explanation, please share!

You can directly use the provided method $scope.tableParams.reload();

I'm not sure about the exact cause of the incorrect incrementing, but the problem here may be more due to the approach. You should attach the count to the scope via $scope.count, and then use the ng-click directive to increment it: <button type="button" ng-click="count++;".
It would also make it easier for you/others to read and debug if you externalized the $scope.tableParams and the data from $scope.table1 conditional thing:
$scope.count = 0;
var dataCollections = [
[//.. first collection],
[//.. second collection],
[//.. third collection],
[//.. fourth collection]
];
$scope.data = dataCollections[0];
$scope.$watch('count', function () {
$scope.data = $scope.count < 4 ? dataCollections[$scope.count] : dataCollections[3];
});
I'm also not sure what you've got going on there with the $compile inside of the controller. It might make your task easier if you investigated some stuff about writing Angular controllers before delving into using a third-party module.

I was working on ng-tables with dynamic data as well (adding/removing),
I was using an ajax call to make changes to the database, and the success: function() {} property make changes to the tableParams
but changes wouldn't show on the page unless i refreshed it, with a few console.log()'s, I found out that the success: function() {} actually never executes
but there's another function that always executes, complete: function() {}
I know it's logically wrong to put the code that's supposed to work only after a successful call into complete: function() {} but if my success: function isn't working, this fix isn't that bad, especially knowing that the change is always successfully made to the database
it's strange because the success call works on other pages of the website, but it doesn't on some others.
EDIT:
well, this fix still doesn't solve the problem when the length of the data doesn't change "editing the text in the data" as mentioned above,, frustrating...
$.ajax({
type: "POST",
url: /*some url*/,
data: JSON.stringify({ /*some variable*/ }
}),
contentType: "application/json; charset=utf-8",
dataType: "Json",
success: function () { // would never execute even if it's a successful call
console.log("success");
},
error: function() { // optional, personally didn't try it
console.log("error");
}
complete: function () { //always executes regardless of the result
console.log("complete");
}
});

To solve the issue, make sure you have set the ng-controller="yourController" only once in your page.
Code below will not update data:
<div ng-controller="yourController">
<table ng-table = "tableParams" ng-controller = "yourController">
</table>
</div>
Solve the issue by removing extra ng-controller in your html page:
<div ng-controller="yourController">
<table ng-table = "tableParams">
</table>
</div>

Related

JQuery & Ajax disable click

I have a table with data and a function to help me get values from rows:
function getRow () {
$('#mytable').find('tr').click( function(){
let fname = $(this).find('td:eq(4)').text();
let start = $(this).find('td:eq(5)').text();
let end = $(this).find('td:eq(6)').text();
.......ajax method () etc
................
}
So far, it has been working perfectly and fetching me the correct data. I had another function elsewhere in the page, where clicking on some links would fetch some data from the server and reload the page to display the new data. Everything was working like clockwork.
Now, I decided that when re-displaying fresh data, instead of reloading the page, it's better to refresh the #mytable div. Indeed, it worked, but alas it spoiled the first function. So basically the function below has introduced a bug elsewhere in the page, and I'm not sure why or how to fix it. It's as if the div refresh has completely disabled the event handler. Any ideas?
$(document).ready(function() {
$(".key").click(function(event) {
event.preventDefault();
var word = event.target.innerHTML;
$.ajax({
url: '.../',
data: {
action : "key",
keyword: word
},
type: 'get',
success: function(data){
$('#mytable').load("/.../../..." + ' #ytable');
},
error: function(e){
console.log(e);}
});
});
});

Angular show/hide working only on second function call, apply/digest issue?

I have the following in a controller, which is designed to show the content after it's loaded. Should in theory be pretty simple but is not playing ball, so I'm trying to understand what I'm doing wrong. This is for a view containing an ionic slide box which is what I'm trying to hide until the data is loaded, and with an ion-refresher for pull to refresh, hence the $scope.broadcast('scroll.refreshComplete');
//initial controller vars:
$scope.data = {};
$scope.data.loading = true;
$scope.data.user = 1;
$scope.data.week = {};
$scope.data.getContent = function() {
var now = new Date().getTime();
serverRequestFactory.getWeek(now)
.success(function(response){
$scope.data.week = response;
$scope.data.loading = false;
$ionicSlideBoxDelegate.update();
$scope.$broadcast('scroll.refreshComplete');
})
.error(function(response){
$scope.data.loading = false;
$scope.$broadcast('scroll.refreshComplete');
})
}
if($scope.data.user == 1){
//calls above on view load
$scope.data.getContent();
//$scope.data.getContent();
}
The weird thing is the above works if I uncomment the second call to $scope.data.getContent() but I don't know why. I've tried $scope.apply() both before and after setting the $scope.data.week object and the update of the slide box delegate. Where's my error?
EDIT: So I just added an ng-repeat directive to one of the slide box items:
<ion-slide-box on-slide-changed="slideHasChanged($index)" ng-hide="data.loading">
<ion-slide class="customSlidePadding" ng-repeat="item in data.week.items">
And now the entire slide box respects the initial ng-hide value and shows up without a second function call... There surely has to be an angular reason adding the directive to a nested item in the hidden slide box makes it work?
If you are using $scope.data.week on your view you should initialize it, otherwise angular does not create $watch over it after the first call.
Just do.
$scope.data.week = []; //or whatever data you want...
you should do this before calling the async request.
Unless your template is actually calling your function via an event handler
on-event="data.getContent()"
or via some binding mechanism (which I don't recommend)
<p>{{data.getContent()}}
Then you aren't actually calling this method. Nothing I've seen in the supplied code is actually calling this, you've only defined the method which calls itself in the if block.
Try explicitly calling it:
$scope.data.getContent = function() {
var now = new Date().getTime();
serverRequestFactory.getWeek(now)
.success(function(response){
$scope.data.week = response;
$scope.data.loading = false;
$ionicSlideBoxDelegate.update();
$scope.$broadcast('scroll.refreshComplete');
})
.error(function(response){
$scope.data.loading = false;
$scope.$broadcast('scroll.refreshComplete');
})
}
if($scope.data.user == 1){
//calls above on view load
$scope.data.getContent();
//$scope.data.getContent();
}
}
//explicitly calling
$scope.data.getContent();

KnockoutJS Binding Issue - Cannot read property

I have what is likely a simple Knockout question but I'm a complete beginner with it. I was tossed this page of code that someone else has worked on but never finished.
When this page first loads, the data is retrieved and the main grid loads properly. The problem comes in when I attempt to auto-select the first record in the results so that a detail list gets filled out below the grid.
When that happens, I receive the following message.
Uncaught TypeError: Unable to process binding "text: function (){return selected.RequestLog.Timestamp }" , Message: Cannot read property 'Timestamp' of undefined
Here is the code snippets with which I'm working. The data coming back is from Entity Framework.
var siteLogModel = function () {
var self = this;
self.errorList = ko.observableArray([]);
self.selected = ko.observable();
self.updateErrorList = function (page) {
jQuery.ajax({
type: "POST",
url: "/Admin/ErrorPage",
data: { pageNum: page },
success: function (result) {
self.errorList(result);
self.selected(result[0]);
// Since we have success, add the click handler so we can get the details about a row by id.
//addRowHandlers();
},
error: function (result) {
jQuery("#status").text = result;
}
});
};
};
This is the actual binding that tries to happen after the data is loaded. RequestLog does not seem to exist at binding time, even though it does seem to be ok if I set a breakpoint in the above function on the line self.selected(result[0]).
I think this is a scope problem but I can't for the life of me think of how best to fix it. Any help would be appreciated.
<div class="param">
<span>Time</span>
<label data-bind="text: selected.RequestLog.Timestamp"></label>
</div>
UPDATE: Here is the document ready portion.
jQuery(document).ready(function () {
var vm = new siteLogModel();
vm.updateErrorList(0);
ko.applyBindings(vm);
});
Your selected observable does not have a .RequestLog property at the time ko is evaluating the binding expression. That error is coming from javascript, not ko (though ko wraps the exception in the error message you see). When running, selected.RequestLog === undefined is true, and you can't invoke anything on undefined. It's like a null reference exception.
It makes sense if you are calling applyBindings before the ajax call finishes.
One way to fix this by doing a computed instead:
<div class="param">
<span>Time</span>
<label data-bind="text: selectedRequestLogTimestamp"></label>
</div>
self.selectedRequestLogTimestamp = ko.computed(function() {
var selected = self.selected();
return selected && selected.RequestLog
? selected.RequestLog.TimeStamp
: 'Still waiting on data...';
});
With the above, nothing is ever being invoked on an undefined variable. Your label will display "Still waiting on data" until the ajax call finishes, then it will populate with the timestamp as soon as you invoke self.selected(result[0]).
Another way to solve it is by keeping your binding the same, but by giving the selected observable an initial value. You can leave all of your html as-is, and just to this:
self.selected = ko.observable({
RequestLog: {
TimeStamp: 'Still waiting on data'
}
});
... and you will end up with the same result.
Why?
Any time you initialize an observable by doing something like self.prop = ko.observable(), the actual value of the observable is undefined. Try it out:
self.prop1 = ko.observable();
var prop1Value = self.prop1();
if (typeof prop1Value === 'undefined') alert('It is undefined');
else alert('this alert will not pop up unless you initialize the observable');
So to summarize what is happening:
You initialize your selected observable with a value equal to undefined in your viewmodel.
You call ko.applyBindings against the viewmodel.
ko parses the data-bind attributes, and tries to bind.
ko gets to the text: selected.RequestLog.Timestamp binding.
ko invokes selected(), which returns undefined.
ko tries to invoke .RequestLog on undefined.
ko throws an error, because undefined does not have a .RequestLog property.
All of this happens before your ajax call returns.
Reply to comment #1
Yes, you can call applyBindings after your ajax success event. However, that's typically not always what you should do. If you want to, here's one example of how it could be done:
self.updateErrorList = function (page) {
self.updateErrorPromise = jQuery.ajax({
type: "POST",
url: "/Admin/ErrorPage",
data: { pageNum: page },
success: function (result) {
self.errorList(result);
self.selected(result[0]);
},
error: function (result) {
jQuery("#status").text = result;
}
});
};
jQuery(document).ready(function () {
var vm = new siteLogModel();
vm.updateErrorList(0);
vm.updateErrorPromise.done(function() {
ko.applyBindings(vm);
});
});
Yet another way would be to go ahead and eager-bind (applyBindings before the ajax call finishes), but wrap your markup in an if binding like so:
<div class="param" data-bind="if: selected">
<span>Time</span>
<label data-bind="text: selected.RequestLog.Timestamp"></label>
</div>

Execute function once model loaded and content rendered in AngularJS

In my angular code, I load some data in ajax which fill my template, so far so good...
Then, once the data are loaded, I want to call a javascript function.
I couldn't find a way to have callback once the template is rendered. I have try several events but none are working.
My solution is to call the javascript method after a timeout:
$http.post('url').success(function (data) {
$timeout(function () {/* process data */ }, 200);
});
It seems to work, but that's a timeout, nothing guarantee me that at the end of the timeout everything is rendered. Maybe it is working only because my PC is fast...
Is there an event based solution? Or any solution better than this one...
The jsfiddle of my solution : http://jsfiddle.net/ZCT4K/5/
You need to $watch() your data. So you could try the following:
$http.post('url')
.success(function (data) {
$scope.postData = data;
});
//Watch the value of postData
$scope.$watch('postData', function () {
if ($scope.postData === undefined || $scope.postData === null || $scope.postData === "") {
return;
}
/*Else process your data*/
});

Basic Javascript Global Scope Issue

I'm having issues with scope in Javascript. Take a look at this code, for example:
$(function() {
var items = "GLOBAL";
$('.add').click(function() {
$.post("main/get", { 'get' : 'all' },
function(data){
items = String(data.result);
items = items.split(' *** ');
alert(items);
}, "json");
alert(items);
return false;
});
$(".add").autocomplete({
source: items
});
});
I'm trying to get autocomplete working, and it almost is. The only problem is that I can't seem to change items outside of the inner-most function. The first alert gives me what I'm looking for, but the second just gives me "GLOBAL." The bottom autocomplete part has to be able to access it.
Any help is appreciated!
Thanks!
It is not just a scope issue. Since your request is very likely to happen asynchronously (unless configured otherwise) it won't work that way anyway. You have to initialize the autocomplete in the callback function which gets called once your AJAX request is complete:
$(function() {
$('.add').click(function() {
$.post("main/get", { 'get' : 'all' },
function(data){
var items = String(data.result);
items = items.split(' *** ');
$(".add").autocomplete({
source: items
});
}, "json");
});
});

Categories

Resources