AngularJS Leaflet heatmap directive does not update the data - javascript

Here is my github gist which describes my problem:
https://gist.github.com/saxena-gaurav/516fb24d2d11d1243adf
In the initialization phase, I am setting the dataPoints array to empty. Upon the success of ajax call, I hard-code the dataPoints array to some values. I added the watcher which then calls the helper function to update the 'layers overlays data' object. Inspite of this, I do not see the data getting rendered on the map.
Any suggestions please to see what I am doing wrong ?
I am using the below link as a reference example:
https://github.com/tombatossals/angular-leaflet-directive/blob/master/examples/heatmap-example.html

Try this after your data updates.
leafletData.getMap().then(function (map) {
map.eachLayer(function (layer) {
if (layer.options.layerName == 'heatmap') {
layer.setLatLngs(newHeatmapData);
}
})
})

Related

Angular Material Table won't populate with external data on initial load

I'm working on an Angular 6 project where I'm loading data in from an AWS DynamoDB table via Observable into a Material Table component. I used the angular-cli to generate the initial structure of the table, and then added my own service to fetch external data, since the example used hard coded data in an array.
Everything seems to be working (I can see the correct data returned via console.log) except for the fact that on my initial load, the data that I'm returning from the observable isn't getting populated into the table. In fact if I inspect the "this.data" variable it seems like it's immediately getting set back to "undefined." If I select and change the number of results per page on the pagination component, the data returned by the observable is inserted.
connect(): Observable<DynamoTableItem[]> {
// Combine everything that affects the rendered data into one update
// stream for the data-table to consume.
const dataMutations = [
observableOf(this.data),
this.paginator.page,
this.sort.sortChange
];
// Set the paginators length
this.paginator.length = this.data.length;
return merge(...dataMutations).pipe(map(() => {
return this.getPagedData(this.getSortedData([...this.data]));
}));
}
I've put the project on stackblitz if someone wouldn't mind taking a look.
To reproduce the issue:
Go to: https://stackblitz.com/edit/mat-table-dynamo
Notice there is no data in the table.
Select the "Items per page" pulldown and change to a different value.
The table populates with the data returned from the Observable.
Thanks for your help!
The rule of thumb of writing any service in angular is that if you have a .subscribe() inside your service, you are probably(99%) do it wrong. Always return an Observable, and let your component do the .subscribe().
The reason why your code doesn't work is because you subscribe your data first inside your service, and then re-wrap it using Observable.of(). That won't work, because your http call is asynchronous. By the time your subscription inside your constructor has received emission, your connect has long established and this.data is first undefined before it can be assigned any values.
To solve your problem, simply change the of(this.data) to its original Observable source, and everything is working:
connect(): Observable<DynamoTableItem[]> {
// Combine everything that affects the rendered data into one update
// stream for the data-table to consume.
const dataMutations = [
this.dynamoService.getData(this.params),
this.paginator.page,
this.sort.sortChange
];
// Set the paginators length
this.paginator.length = this.data.length;
return merge(...dataMutations).pipe(map((received) => {
return this.getPagedData(this.getSortedData([...received]));
}));
}
Here is the working working StackBlitz

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);
}

GeoJSON layer does not get recreated when rendered using EmberJS's 'link-to' helper

I am trying to render a LeafletJS map where the colours of the states in the map are dependent on a global parameter that is set in the appropriate Ember route. The setting of the parameter is not the issue but rather the (re)creation of the geoJson layer. When hitting the URL for the first time or when reloading the page the correct map is created, however when the page is rendered using Ember's 'link-to' helper, the map still holds the state colours of the previous page.
drawAll: function() {
var that = this;
Ember.$.ajax('/data/sa_provinces.json').then( function(data){
Frontend.globalPaths = data;
that.get('store').findAll('province').then(function(provinces) {
provinces.forEach(function(province) {
var provinceGeoJSON = window.L.geoJson( province.get('dataFromJSON'),
{ style: province.get('geoJSONStyle'),
province: province,
onEachFeature: province.get('onEachFeature') });
province.set('geo_json', provinceGeoJSON);
provinceGeoJSON.addTo(Frontend.map);
window.province = province;
});
});
});
}.property('drawAll')
This drawAll function is located within a Ember controller and is called from an Ember template. The functions dataFromJSON, geoJSONStyle and onEachFeature are all called the first time a page is called or when the page is refreshed but not when the page is rendered using the Ember's 'link-to' helper. Neither are they called when the URL is entered manually.
If anyone has any ideas or experience with LeafletJS and/or Ember I would really appreciate your help. Thanks in advance, Greg.
The first issue I notice is that drawAll is a computed property, not a function - you seem to be confusing computed properties and functions.
http://emberjs.com/guides/object-model/computed-properties/
Ember computed properties are more like normal attributes that observe other variables, and recompute when those variables change. The property() method after the function declaration changes it into a computed property and specifies which variables the property depends on. On the last line you're specifying that drawAll observes itself, which doesn't make much sense.
You can't call functions from handlebars templates - you can only access properties. So you can access a property, with the side effect of causing that property's function to be called.
If you want just a function that is called as soon as the template loads, you can implement the didInsertElement function on that templates corresponding view, and the contents of the didInsertElement function will run when the template loads.
If you want a property that recomputes based on some conditions changing, you should change the last line to specify which conditions it is observing.
I can't be sure without more info about the template and controller you're using, but for your current use case it looks like you just want a function that runs whenever the template is inserted, so changing the drawAll to an actual function (by removing the .property('drawAll)) and calling it from didInsertElement of the corresponding view will rerun it every time the controller is inserted. Like:
didInsertElement: function() {
this.drawAll()
}
(You need to have created a view that corresponds to the controller in this context)

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.

Categories

Resources