AngularJS Populate Data in Directive from Rest Call with ng-repeat - javascript

Using AngularJS and Typescript (Not to worry if you do not know TypeScript, an AngularJS proposal would do me) I am trying to create a directive for highcharts that has dynamic data that is populated by a call to a service that gets the chart data (via REST)
My html looks like this:
<div ng-repeat="request in controller.chartRequests">
<bar-chart id = "snapshotChart"
get-chart-data = "controller.getChartData(request)" // Some other attributes />
</bar-chart>
</div>
My hope is to dynamically draw the charts based on the number of stored requests I have in my controller.
The request object is used by the 'chartDataService' as shown below.
It returns the data as an array - eg: [1,12] and I resolve as a promise
public getChartData = (chartRequest) => {
let deferred = this.$q.defer();
this.chartDataService.dispatchCommand(chartRequest).then(
this.returnChartData // just returns a series
).then((series) => {
// This populates fine e.g [1,12]
deferred.resolve(series);
});
return deferred.promise;
};
constructor() {
let directive:ng.IDirective = {};
directive.restrict = 'E';
directive.replace = true;
directive.scope = {
getChartData: '&',
/** Other attributes **/
};
directive.link = (scope, element, attrs) => {
let highchartsOptions:HighchartsOptions = {
/**
* Some Highcharts config this all works fine
**/
series: [
{
data: scope.getChartData()
}
]
};
let chart = new Highcharts.Chart(
highchartsOptions
);
}
I have tried a scope.watch on getChartData() but this cause an infinite loop of requests it seems.
Not sure if anyone can help me out here - I may be approaching this problem in the wrong way.
Any help much appreciated!
Cheers

Related

Do async calls in services block component load in angularjs?

The angularjs guide contains an example detailing an async call in a service.
The following code is given
angular.module('finance3', [])
.factory('currencyConverter', ['$http', function($http) {
var currencies = ['USD', 'EUR', 'CNY'];
var usdToForeignRates = {};
var convert = function(amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
};
var refresh = function() {
var url = 'https://api.exchangeratesapi.io/latest?base=USD&symbols=' + currencies.join(",");
return $http.get(url).then(function(response) {
usdToForeignRates = response.data.rates;
usdToForeignRates['USD'] = 1;
});
};
refresh();
return {
currencies: currencies,
convert: convert
};
}]);
Since the refresh function is async, how does angularjs ensure that the data is loaded before loading the controller which accesses the data returned by the refresh function, ie the data contained in the usdToForeignRates object.
I assume there is some blocking going on somewhere, otherwise when the controller accesses the object returned, it will get undefined values.
I want to understand the flow, does angular internally ensure that service loads before injecting it into the controller?

How to make filters work with array in angularjs

Iam trying to create a custom filter to filter matching array of values in angularjs. Array Structure below
["tag1","tag2"]
Now I need to filter all objs having tags matching id1,id2.. Below is the filter I have tried
var autoFilter = angular.module("autoFilters",[]);
autoFilter.filter('arrayData', function (){
return function(){
return ["id1","id2"];
}
//$scope.arrayValues = ["id1","id2"];
});
and UI code below
<li style="cursor:pointer" ng-cloak class="list-group-item" ng-repeat="values in suggestionResults | arrayData">{{values.id}} -- {{values.title}}</li>
But Data is not showing up. Can you help me out where Iam doing wrong. Plunker Code available below
plunker here
see the code below :) This is not the best approach in my opinion and will definitely have some performance issue with larger lists, but it does the work (now I used indexOf(2) but there you can pass any truthy/falsy argument)
var autoFilter = angular.module("autoFilters",[]);
autoFilter.controller("filterController",['$scope','$http', function ($scope,$http) {
$scope.searchSuggest = function(){
//$http({method: 'GET', url: 'json/searchSuggestions.json'}).success(function(data) {
$http.get("assets.json").then(function(response) {
//var str = JSON.stringify(response);
//var arr = JSON.parse(str);
$scope.suggestionResult = response.data;
console.log($scope.suggestionResult);
//$scope.arrayData = ["asset_types:document/data_sheet","asset_types:document/brochure"];
}).catch(function activateError(error) {
alert('An error happened');
});
}
$scope.showProduct = function(){
}
}]);
autoFilter.filter('arrayData', function (){
return function(data){
// if you are using jQuery you can simply return $.grep(data, function(d){return d.id.indexOf('2') >-1 });
return data.filter(function(entry){
return entry.id.indexOf('2') > -1
})
}
});
Having experienced working with large lists I would, however, suggest you to avoid using a separate filter for this and rather manipulate it in the .js code. You could easily filter the data when you query it with your $http.get like:
$scope.suggestionResult = response.data.filter(function(){
return /* condition comes here */
}
This way you are not overloading the DOM and help the browser handling AngularJS's sometimes slow digest cycle.
If you need it to be dynamic (e.g. the filtering conditions can be changed by the user) then add an ng-change or $watch or ng-click to the modifiable information and on that action re-filter $scope.suggestionResult from the original response.data

How to filter by an array in AngularJs ng-repeat?

I am using ng-repeat to display my data. That is working fine.
One of the data fields in my ng-repeat result set is an array of items.
Example: {x:1, y:[2,3,4]}
I would like to filter by data by data in the array. I can easily filter by the non array data, but I am having trouble when I try to filer by looking inisde in the array.
Here is my markup
ng-repeat = "con in cons | filter: usrSingle.username in con.conPlayerList"
(edited markup to match my example better ng-repeat = "con in cons | filter: '3' in con.y" )
usrSingle is a scope in this controller I can access. I don't get any errors and I can't seem to find examples of this.
More code was requested and here it is below. I forgot to mention this is a MEAN app. I have MongoDB serving the data. I use a REST API for my data calls.
(EDIT) code from the angular module:
// call to the api for data
app.factory('usrService', function ($resource) {
return $resource('/api/users', {});
});
// factory to hold user data between controllers
app.factory('usrTracker', function () {
var vUsrProfile = {
usrSingle: 'usrSingle'
};
return {
getProperty: function () {
return vUsrProfile;
},
setProperty: function (value) {
vUsrProfile = value;
}
};
});
// angular controller
app.controller('usrPageController', function ($scope, usrTracker, conService, postService) {
var usrSingle = usrTracker.getProperty();
$scope.usrSingle = usrSingle;
$scope.cons = conService.query();
$scope.posts = postService.query();
});
(FINAL EDIT) The answer marked for this is a good solution. But I went another direction. I used the mongoDB $unwind aggregation to explode my data, like below.
code from API file:
// get
.get(function (req, res) {
// unwind on the conPlayerList array field
Con.aggregate([{ $unwind: "$conPlayerList" }], function (err, cons) {
return res.json(cons);
});
})
Then I filtered on the user I was looking for. Changing the HTML Angular markup to this
ng-repeat="con in cons | filter:{conPlayerList:usrSingle.username}"
to match my example better, by removing my specific code, it would be:
ng-repeat="con in cons | filter: {y:'3'} "
You can use Angular's built in filter logic on the array, BUT bear in mind this is not an exact match and will match against an array entry of 300 if usrSingle is set to 3, see example in Fiddle.
<div ng-repeat = "con in cons | filter:{y:usrSingle} ">...</div>
To match exactly against array elements you can use filter: and a predicate function on the controller i.e.
Controller (Fiddle):
app.controller('usrPageController', function ($scope) {
$scope.filterFn = filterFn;
$scope.usrSingle = 3;
$scope.cons = [
{x:0, y:[1,2,3]},
{x:1, y:[2,3,4]},
{x:2, y:[3,4,5]},
{x:3, y:[4,5,6]},
{x:4, y:[5,6,7]}
];
function filterFn(con){
return con.y.indexOf($scope.usrSingle) > -1;
}
});
View:
<div ng-repeat = "con in cons | filter:filterFn">...</div>
Alternatively you can create a custom filter which can be reused elsewhere in your app and pass usrSingle in as a parameter:
Filter (Fiddle):
app.filter('customFilter', function() {
return function(input, value) {
var result = [];
input.forEach(function(item){
if(item.y.indexOf(value) > -1){
result.push(item);
}
});
return result;
};
});
View:
<div ng-repeat = "con in cons | customFilter:usrSingle">...</div>

AngularJS - Improving service call and data binding performance

I have an Angular service that goes away to retrieve a pretty big JSON file (nearly 10,000 lines).
The problem i am facing, is that it is taking some time to bind the data to the front-end (as expected).
Sample controller:
$scope.dataLoaded = false;
serviceReport.getData( function (data) {
$scope.data1 = data.data1;
$scope.data2 = data.data2;
$scope.data3 = data.data3;
$scope.data4 = data.data4;
$scope.data5 = data.data5;
$scope.data6 = data.data6;
$scope.data7 = data.data7;
$scope.data8 = data.data8;
$scope.data9 = data.data9;
$scope.data10 = data.data10;
$scope.data11 = data.data11;
$scope.data12 = data.data12;
$scope.data13 = data.data13;
$scope.data14 = data.data14;
$scope.data15 = data.data15;
$scope.data16 = data.data16;
$scope.data17 = data.data17;
$scope.dataLoaded = true;
});
Service:
app.factory('serviceReport', function($http) {
return {
getData: function(value,done) {
$http.get('data.json', {
})
.success(function(data) {
done(data);
})
.error(function(error) {
alert('An error occured');
});
}
}
});
I have ng-cloak on my HTML element, when dataLoaded = true, this is removed as it indicates the data is available to be displayed.
How can i improve the service call/data bind? Would splitting the call help?
Server-side solution would be to reduce the size of the response and make more requests with smaller responses. Do you actually need the whole response at start? You have to be aware that binding the whole response will generate many watchers, which will slow down all subsequent digests.
Client-side solution would be to bind the response part by part in a loop as a callback parameter for $scope.$apply() or even $timeout().

AngularJS and Restangular, trying to convert update method to API

I'm trying to convert my basic crud operations into an API that multiple components of my application can use.
I have successfully converted all methods, except the update one because it calls for each property on the object to be declared before the put request can be executed.
controller
$scope.update = function(testimonial, id) {
var data = {
name: testimonial.name,
message: testimonial.message
};
dataService.update(uri, data, $scope.id).then(function(response) {
console.log('Successfully updated!');
},
function(error) {
console.log('Error updating.');
});
}
dataService
dataService.update = function(uri, data, id) {
var rest = Restangular.one(uri, id);
angular.forEach(data, function(value, key) {
// needs to be in the format below
// rest.key = data.key
});
// needs to output something like this, depending on what the data is passed
// rest.name = data.name;
// rest.message = data.message;
return rest.put();
}
I tried to describe the problem in the codes comments, but to reiterate I cannot figure out how to generate something like rest.name = data.name; without specifying the name property because the update function shouldn't need to know the object properties.
Here is what the update method looked like before I started trying to make it usable by any of my components (this works)
Testimonial.update = function(testimonial, id) {
var rest = Restangular.one('testimonials', id);
rest.name = testimonial.name;
rest.message = testimonial.message;
return rest.put();
}
How can I recreate this without any specific properties parameters hard-coded in?
Also, my project has included lo-dash, if that helps, I don't know where to start with this problem. Thanks a ton for any advice!
Try like
angular.extend(rest,testimonial)
https://docs.angularjs.org/api/ng/function/angular.extend

Categories

Resources