Updating totalItems in angular-ui-grid with external - javascript

I am using external pagination in AngularJS's ui-grid as described here:
http://ui-grid.info/docs/#/tutorial/314_external_pagination
That updates a special field of totalItems when new page is arrived:
var getPage = function() {
// skipped some part
$http.get(url)
.success(function (data) {
$scope.gridOptions.totalItems = 100;
// updating grid object with data
});
};
My only difference to the standard example is that I have gridOptions inside an object that is passed to my directive, that has a grid inside it:
// Line is called right after I receive a page of data from server
$scope.gridObj.gridOptions.totalItems= response.TotalItems;
If I initialize this field before grid is shown on screen (together with configuration) then it remains at the value that I set back then (and obviously, it is not adequate to set of data I receive later, during runtime). But when I alter it later after I call my loading method (that returns current "page" as well as value for totalItems), the change is not picked up by ui-grid. If it is not initialized at startup, again, I could not set this to any value.
How am I to successfully modify the totalItems parameter for the ui-grid during runtime? Can I possibly re-trigger some grid update manually?

I've found that assigning totalItems within a $timeout works nicely for now. This was necessary because ui-grid was updating the value, just not to what I was expecting.
Update:
In my case I need to show all filtered items across all pages, which there is no API for. So for now I've implemented an angular filter, included below. GridRow objects have a visible flag set to true when filters have not filtered them out from the result set.
Example:
angular.filter('renderableRows', function() {
return function(allRows) {
var renderables = [];
angular.forEach(allRows, function(row) {
if(row.visible) {
renderables.push(row);
}
});
return renderables;
};
});
$timeout(function() {
$scope.gridOptions.totalItems = $filter('renderableRows')($scope.gridApi.grid.rows).length;
});

Related

Refresh Kendo Grid with a new filter (ServerSide)

I have some external elements on my angular application page that I want to use to filter the grid. Unfortunately I am not sure how to do this. My filters need to be applied dynamically to the grid and sent to the backend as serverFiltering is set to true.
What I have so far: This refreshes the grid, but doesn't send the new filter to the backend. It seems to be sending the default initial parameters. I have checked getFilter and it returns the necessary filter object.
$scope.fromDateChanged = function ()
{
$scope.grid.dataSource.filter = getFilter();
$scope.grid.dataSource.read();
};
I found the issue. I had to simply pass the filter object into the filter function of the grid. read() is not required to be called. Hope this helps somebody.
$scope.fromDateChanged = function ()
{
$scope.grid.dataSource.filter(getFilter());
};

Highcharts series data not persisting through angular route change

I'm using Highcharts with AngularJS. I have a directive which generates the chart. In the link function I specify a default object chartOptions. After setting the default options, I append that object to another object passed to the directive which persists between routes and refreshes, passedObject. Thus any changes to chartOptions after being appended successfully persist. Then the chart generates with the call to Highcharts.chart();
return {
restrict: 'E',
scope: {
passedObject: "="
},
link: function (scope, element) {
var chartOptions = {
title : {
text: 'sample report'
}
...
}
scope.passedObject.chartOptions = scope.passedObject.chartOptions || chartOptions;
var chart = Highcharts.chart(element[0], scope.passedObject.chartOptions);
}
// successfully add new series
// series rendered immediately but does not persist though view changes and page reload.
scope.addSeries = function(seriesObject){
scope.elementObject.chart.addSeries(seriesObject);
};
// successfully pushes object to array
// does not immediately render because Highchart doesn't see change
// but does render after reloading because Highchart checks options again
// Does not render with animation like addSeries() API does.
scope.addSeriesNoAPI = function(seriesObject){
scope.elementObject.chart.series.push(seriesObject);
};
}
There is no issue generating the chart the first time, and if I return to this view, the chart generates again with the same default options.
However, if I use the Highcharts API to programatically add a new data series chart.addSeries(newSeriesObject) the series is added and renders on the chart, but will not persist between routes and refreshes.
If I manually add the series object to my chartOptions via straight JS scope.passedObject.chartOptions.series.push(newSeriesObject) the object is successfully pushed to the array and will persist, but the chart will not automatically update because it doesn't know the series array changed, which is clearly why the API exists.
Upon closer inspection, it appears that Highchart's addSeries() function is pushing the new object to some array other than the one I am persisting, but I can't figure out how to get this to work.
I assumed that this line of code: var chart = Highcharts.chart(element[0], scope.passedObject.chartOptions); would bind the chart to the scope.passedObject.chartOptions object, such that when something like the addSeries() function added a new series, it would be added into scope.passedObject.chartOptions. After logging and watching objects, however that does not appear to be the case.
One way is to hold that data in the root scope (but that is not the best way).
Another is to use angular service to store this data. And include this service where you need it.
An angular service unlike controllers will not get reset when changing views/pages.
I would suggest that you create an angular service that stores the chart options, which has a chart options object and a getter and a setter. You could also hold the functions that make changes to the chartobject in this service.
In your case you seem to do manipulations to the chart object within the controller (after you are getting it from a service perhaps?), if so you will have to set the new object back to the service.
Also when I say service I mean something like this:
app.service('highChartService', function() {
var chartOptions;
var highChartServiceObj = {};
highChartServiceObj.getChartOptions = function() {
return chartOptions;
};
highChartServiceObj.setChartOptions = function (options) {
chartOptions = options;
};
highChartServiceObj.computeChartObject = function () {
//do some computation
}
return highChartServiceObj;
});
Add the above service to your directive and use that to update your highchart object when any changes are made.
Also if I am not wrong what you would like to do is add newseriesobject like this chart.addSeries(newSeriesObject) which solves your highchart dilemma. Why not then update your chart object as the next step: scope.passedObject.chartOptions.series.push(newSeriesObject)? Though I would rather that be part of the service too if I was doing this, all of the highchart manipulations would just be a part of the service, where I would have a function like this in above service:
highChartServiceObj.updateChart(newSeriesObject, chart) {
chartOptions.series.push(newSeriesObject);
chart.addSeries(newSeriesObject);
}

Updating DOM according to Firebase Object

I want to update my DOM element everytime the value on the Firebase changes. I've seen that Angularfire handles three-way data binding, but from what I understood it only works if you take elements from $firebaseArray directly from the DOM.
What I have is an Element on the DOM (chart) that depends on some of the data on a $firebaseArray, but my element gets the data from a function instead of directly from the $firebaseArray. That means I have to do some pre-processing on the $firebaseArray before my element can use it.
This is what I have:
<pie-chart ng-repeat="chart in myCtrl.charts"
data="chart.data"
options="chart.options"></pie-chart>
This is my controller:
function MyCtrl($firebaseArray) {
let myRef = new Firebase(refUrl);
let chartsFirebase = $firebaseArray(myRef);
let getCharts = function() {
let charts = [];
distanceGoals.$loaded().then(function() {
// push some things from chartsFirebase on the charts array
charts.push({
options: { ... },
data: [ ... ]
});
}
return charts;
}
this.charts = getCharts();
}
Turns out that in this way this.charts is only updated one time, after modifications on the data in Firebase I have to refresh the browser.
Has anyone an idea of what I could do to achieve this behavior?
You can add a child-changed event listener to your ref like this:
// Get a reference to our posts
var ref = new Firebase("https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts");
// Get the data on a post that has changed
ref.on("child_changed", function(snapshot) {
var changedPost = snapshot.val();
console.log("The updated post title is " + changedPost.title);
});
This will get called everytime something changes in the location you put the listener on.
For more info take a look at https://www.firebase.com/docs/web/guide/retrieving-data.html and https://www.firebase.com/docs/web/api/query/on.html.
From the AngularFire documentation on $loaded()(emphasis mine):
Returns a promise which is resolved when the initial array data has been downloaded from the database.
That explains the behavior you're seeing.
To solve this, you should extend the $firebaseArray as documented here: https://www.firebase.com/docs/web/libraries/angular/guide/extending-services.html#section-firebasearray
Some related questions:
AngularFire extending the service issue
Joining data between paths based on id using AngularFire (includes a full example by the author of AngularFire)

Caching data and updating another object for an chart does not work

In my Controller, I'm quering data from a $resource object bundled in a caching service.
$scope.data = myService.query(); //myService caches the response.
In the same controller I have the configuration for a chart (for the GoogleChartingDirectve).
$scope.chart = {
'type': 'AreaChart',
'cssStyle': 'height:400px; width:600px;',
....
'rows' : convert($scope.data),
...
}
convert is just a function which takes the data from myService and returns an array for the chart.
This is only working, if the response of the query has already been cached (After changing the route, I can see the graph, but if I call the route directly it is empty).
Perhaps the problem is my caching?
angular.module('webApp')
.service('myService', function myService($cacheFactory,res) {
var cache = $cacheFactory('resCache');
return {
query: function() {
var data = cache.get('query');
if (!data) {
data = res.query();
cache.put('query', data);
}
return data;
}
};
});
I've tried the $scope.$watch in my Controller. It is fired only once with no data. If I change to a different route and back again it is fired again; this time with data.
$scope.$watch('data',function(newValue){
console.log("Watch called " + newValue);
}
I'm not sure what the problem actually is.
It looks like the $watch event is missing.
If I call the refresh with a button, $watch is fired and data is shown.
$scope.refresh = function(){
$scope.data = $scope.data.concat([]);
}
I'm just an angular newbie and playing around in a small demo project.
So I'm looking for the best way to solve some common problems.
Any idea how to solve my problem?
I guess the problem is that you are not aware that res.query() is an asynchronous call. The $resource service works as follow: the query call returns immediatly an empty array for your data. If the data are return from the server the array is populated with your data. Your $watch is called if the empty array is assigned to your $scope.data variable. You can solve your problem if you are using the $watchCollection function (http://docs.angularjs.org/api/ng.$rootScope.Scope):
$scope.$watchCollection('data', function(newNames, oldNames) {
var convertedData = convert($scope.data);
...
});
Your cache is working right. If you make your server call later again you got the array with all populated data. Btw. the $ressource service has a cache option (http://docs.angularjs.org/api/ngResource.$resource) - so you don't need to implement your own cache.
You may ask why the $ressource service works in that way - e.g. returning an empty array and populating the data later? It's the angular way. If you would use your data in a ng-repeat the data are presented to the view automatically because ng-repeat watches your collection. I guess you chart is a typical javascript (for example jQuery) that doesn't watch your data.

Knockout - added observable not updating on new objects

For UI purposes, when I load the array my viewModel is based on I add a new property to each object based on some other properties:
item.forEach(function (party) {
if (party.AcknowledgementDate() === null) {
party.Agreed = ko.observable(false);
}
else {
party.Agreed = ko.observable(true);
}
vm.Parties.push(party);
});
"Parties" is defined as ko.observableArray when the page starts.
The items in this array are edited in a separate UI window. When those changes are saved and the window closed, I call this function to update those values:
function updateAgreed() {
vm.Parties().forEach(function (i) {
if (i.AcknowledgementDate() === null) {
i.Agreed(false);
}
else {
i.Agreed(true);
}
});
}
This all works fine, and makes me very happy. The problem arrives when users create a new party item. We're using Breeze too, so we go off to the data service which requests entity framework create a new object of the appropriate type, then add an observable:
var lp = manager.createEntity('Party_dto'. { [an array of initial values] });
lp.Agreed = ko.observable('');
return lp;
Thanks to Breeze, this adds itself to the Parties observableArray because it's related to the same parent object. I can then call updateAgreed again to populate the Agreed observable with the appropriate value.
Logically, this work as expected - you can step through it and watch the Agreed observable of the new item be added and populated with the expected values. The problem comes in the UI - it doesn't update as having changed. Yet running the same code against an already-loaded object does cause the UI to update.
I'm stumped by this. I can't replicate it in Fiddle because we create objects in Breeze and not on the fly - and making a mock version without Breeze works perfectly. Why do my observables update on already loaded objects, but the same observable not update on a new object?
There are a few things that I see that need to be addressed. One, since you are using Breeze, take advantage of the model constructors and initializers. Wherever you are defining properties for your models, add the following code -
metadataStore.registerEntityTypeCtor(
'Party', null, partyinitializer);
function partyinitializer(party) {
party.Agreed = ko.observable(false);
}
Now all of your party entities have an agreed property that you can access. Next, make sure you aren't setting the Party's parent navigation property in the createEntity method, as that will break your binding.
var lp = manager.createEntity('Party'. { [an array of initial values] });
lp.parentParty(something); // Set the parent here
return lp;
This will make sure that before the party is bound back to the parent and shown in the view, all of the properties will be set. Then when you set the navigation property, it will show up in your view all happy-like.

Categories

Resources