AngularJs $filter in service using function - javascript

I got stuck with learning angular in particular the $filter('filter') function and how to pass a local function to it as a filter function.
The API (json files) contain all the trips in trips.json and the id array of all the trips the user has been on in the user.trips.json.
The factory getting fetching the data looks like this:
app.factory("tripsApi", function ($http) {
return {
allTrips: $http.get('/api/trips.json')
.success(function (response) {
return response;
})
.error(function (error) {
return error;
}),
userTrips: $http.get('api/user.trips.json')
.success(function (response) {
return response;
})
.error(function (error) {
return error;
})
}
});
The next piece of code is just a service to retrieve all the user trips information. The service uses a factory to access the API (in this case just the men json files). And it SHOULD filter through the trips information to retrieve only the ones the user has been on using the id array.
app.service("trips", function (tripsApi, $filter) {
var self = this;
this.user = {
trips: Array()
};
this.trips = Array();
this.getUserTrips = function () {
self.getAllTrips.then(function () {
tripsApi.userTrips.success(function (response) {
self.user.trips = $filter('filter')
(self.trips, self.containsChild(id, response));
});
});
};
this.getAllTrips = tripsApi.allTrips.success(function (response) {
self.trips = response;
});
this.containsChild = function (id, idsArray) {
if (id != 0 && idsArray != null) {
for (var i = 0; i < idsArray.length(); i++) {
if (idsArray[i] == i)
return true;
return false;
}
}
}
});
Yet I can't get it to work. The first error I get is the id not defined in the self.containsChild(id, response);
Where are the mistakes? Any help is welcome :)

The id issue, the call should be:
$filter('filter')(self.trips, self.containsChild); // this won’t really work, though, because your `containsChild` function is not properly defined.
Second, This is not a properly defined service. You want something that looks like the following
app.service('ServiceName', [ '$filter', function ($filter) {
var myInstance = {};
myInstance.containsChild = function (value, index, array) {
};
myInstance.user = . . .;
return myInstance;
}]);
Third, fix your containsChild function to take three parameters. The first will be the value passed to it, the second will be the index, and the third will be the array being filtered.

Yet I can't get it to work. The first error I get is the id not
defined in the self.containsChild(id, response);
Where are the mistakes?
You passed id to the function but there's no idvariable declared anywhere in the code the you provided which is what's causing the error.
Also, I've observed that there's no consistency in your code, you assigned this to self but you keep using this and self everywhere in your code.

Related

How do I call another function within ng-repeat in Angular

I am sure this question has been already asked before, but I could not see or find a best explanation of it, therefore I would like to re-ask the same question and open the thread for more clear answers with some examples.
My goal is to display teams and its ranking, note: for displaying ranking I have a separate function that gets the ranking of team depending on their score field in db.
I have two functions within a TeamController as below:
tm.showAllByClass = function (classId) {
TeamService.showAllByClass(classId).then(function (response) {
tm.teamsInClass = response.data;
}).catch(function (error) {
$scope.result = error;
});
};
tm.ranking = function (classId, teamId) {
TeamService.ranking(classId, teamId).then(function (response) {
return response.data;
}).catch(function (error) {
$scope.result = error;
});
};
<tr ng-repeat="tm in team.teamsInClass.data">
<td>{{tm.group_number}}</td>
<td>{{tm.role.name}}</td>
<td>{{tm.ranking(tm.class_id, tm.id)}}</td>
<td>{{tm.amount | currency}}</td>
</tr>
And this is the function in backend part that gets all teams:
public function findAllTeamsInClass($classId)
{
return Team::where('class_id', '=', $classId)->with('role', 'business')->get();
}
// return rank of team
public function teamRanking($classId, $teamId){
return 3; // for sake of simplicity I just return a static value
}
Is there any way I can attach teamRanking function directly to the team entitiy as relationship or something?
For some reason tm.ranking() is not returning anything, how can I call a function that returns a value within the ng-repeat.
Since the fetch of individual rankings is asynchronous, those operations need to be chained from the fetch of the list of teams.
team.showAllByClass = function (classId) {
TeamService.showAllByClass(classId).then(function (response) {
team.teamsInClass = response.data;
//return for chaining
return team.teamsInClass;
}).then(function(teamsInClass) {
promiseArray = [];
for (var iTeam=0; iTeam<teamsInClass.length; iTeam++) {
//IIFE
(function (iTeam) {
var iPromise = TeamService.ranking(classId, iTeam.id);
iPromise = iPromise.then(function(response) {
var ranking = response.data;
team.teamsInClass[iTeam].ranking = ranking;
return ranking;
});
promiseArray.push(iPromise);
})(iTeam);
//end IIFE
};
//return for chaining
return $q.all(promiseArray);
}).then(function(rankingArray) {
console.log("All rankings fetched");
}).catch(function (error) {
$scope.result = error;
});
};
The above example fetches the list of teams and then creates a list of promises that attach each ranking to each team. It uses $q.all to chain the list of promises.
The HTML
<tr ng-repeat="tm in team.teamsInClass">
<td>{{tm.group_number}}</td>
<td>{{tm.role.name}}</td>
<td>{{tm.ranking || 'PENDING' }}</td>
<td>{{tm.amount | currency}}</td>
</tr>
The ranking will display as PENDING until the data is retrieved from the API.
You might have the same answer here:
https://stackoverflow.com/a/26400951/6715875
you can simply call a function inside ng-repeat same as normal one.

How to handle information needed from a promise for future saves and updates

I have an object I'm saving to an API. The API returns a unique identifier: id.
When I save with I get a promise which includes my new, API assigned, id.
Now the user may want to carry out other operations, which will require the id. For example, renaming the widget and re-saving, or adding further objects that point back to its id.
What are the practical and ideally straightforward options*?
*Other advice on stackoverflow I've read suggests using 'resolve' which would be fine if I was reverting to the router at this point. But I'm not at the moment.
Here's a simple example:
widget.saveThis = function() {
if ('id' in this) {
this.put();
} else {
var _this = this;
rest.all('widgets').post(this).then(function(result) {
// Copy the new properties I have received to this object.
// ignore the methods from restangular.
for (var key in result) {
if (typeof(result[key]) != 'function')
_this[key] = result[key];
}
p.refresh();
});
}
};
Where if save is pushed twice in a row we might get two copies of the object.
Imagine you have a service where you do the API Communication (maybe via REST?
"use strict";
(function() {
var module = angular.module('myModule.service');
module.factory('myService', function($http, $q) {
return {
/**
* save and get
*/
saveAndGet: function(myObject) {
var deferred = $q.defer();
$http.post(getContextPath()+'/rs/saveObj', JSON.stringify{myObject})
.success( function(data) {
deferred.resolve(data);
})
.error(function(response) {
deferred.reject(response);
});
return deferred.promise;
}
}
});
})();
now imagine you have a controller where you wait for the saving to be done:
"use strict";
(function() {
var module = angular.module('myModule.controller');
module.controller('MyController' , function($scope, myService) {
var myObj = //set it somehow
$scope.id; //needed for save the "new" id
myService.saveAndGet(myObj)
.then( function(result) {
// is called if no error occured
$scope.id = result.id;
)};
})();
and then image you have that backend (in java for example)
#POST
#Path("saveObj")
#Produces({"application/json"})
public Response createProjectComment(MyObj obj) {
// do something and create myNewId
if (myNewId == null) {
return Response.ok("error").build();
}
return Response.ok(myNewId).build();
}
that would be one way to solve your problem.

Updating Json values with a promise

I want to populate some values in Json that are being calculated with angular-promises and these value should be updated after certain events.
I tried to call the factory which yields the values for example something like below and tried to call the functions GetWeeklyVal and GetDailyVal which are in charge of calculating the values :
this.salesList =
{"sales":[
{ "id":"A1", "dailyValue": GetDailyVal('A1'), "weeklyValue": GetWeeklyVal('A1')},
{ "id":"A2", "dailyValue": GetDailyVal('A2'), "weeklyValue": GetWeeklyVal('A2')}
]}
and in my controller I have:
$scope.sales= salesServices.salesList.sales;
but it didn't work. the values remain zero which is the default value in the application.
Why the values are not being updated and what would be a better solution?
update
This is the portion of the code I call the calculation functions: (I skip the portion to get the values based on passed id in here)
function GetDailyVal(id){
var dValue = 0;
salesService.getSales();
dValue = salesService.totalAmount;
return dValue;
}
this is the factory
.factory('salesService', ['$http', '$q'],
function salesInvoiceService($http, $q) {
var service = {
sales: [],
getSales: getSales,
totalAmount: 0
};
return service;
function getSales() {
var def = $q.defer();
var url = "http://fooAPI/salesinvoice/SalesInvoices"; //+ OrderDate filter
$http.get(url)
.success(function(data) {
service.sales = data.d.results;
setTotalAmount(service.sales);
def.resolve(service.sales);
})
.error(function(error){
def.reject("Failed to get sales");
})
.finally(function() {
return def.promise;
});
}
function setTotalAmount(sales){
var sum = 0;
sales.forEach(function (invoice){
sum += invoice.AmountDC;
});
service.totalAmount = sum;
}
})
I think there are some errors in your code.
I give some sample code here. I think this will help you.
This is a sample code in one of my application. Check it.
service.factory('Settings', ['$http','$q', function($http,$q) {
return {
AcademicYearDetails : function(Details) {
return $http.post('/api/academic-year-setting', Details)
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
return $q.reject(response.data);
}
}, function(response) {
return $q.reject(response.data);
});
},
newUser : function(details) {
return $http.post('/api/new-user', details);
}
}
}]);
The reason why its not working is:
dailyValue: GetDailyVal('A1')
Here, GetDailyVal makes an async ajax call to an api. For handling async requests, you have to return a promise as follows in your GetDailyVal function as follows:
function GetDailyVal() {
salesService.getSales().then(function(data) { //promise
dValue = salesService.totalAmount;
return dValue;
})
}
Same thing need to be done for weeklyValue.

Angularjs Factory deferred's data disapearing

I'm trying to do a caching factory for http requests, so it doesn't make the server do a lot of work for the same request. But It seems my way of using deferred "swallows" the data, and I don't know why.
Console output for below:
data fetched:
Object {state: "OK", data: Object, errorMessage: null, exception: null}
success
undefined
ImportFactory:
factory("importFactory", function ($http, $q, loggingService) {
return{
fetchedData: [],
cacheTransport: function (transportsId, data) {
this.fetchedData.push({"transportsId": transportsId, "data": data});
},
getImport: function (transportsId) {
var factory = this;
var deferred = $q.defer();
var preFetchedTransport = this.findTransport(transportsId);
if (preFetchedTransport === null) {
console.log('fetching from backend');
return $http.post("/import/create/" + transportsId).then(function (data) {
console.log('data fetched:');
console.log(data);
factory.cacheTransport(transportsId, data);
deferred.resolve(data);
});
}
preFetchedTransport = deferred.promise;
return preFetchedTransport;
},
findTransport: function (transportsId) {
for (var i = 0; i < this.fetchedData.length; i++) {
var transportObj = this.fetchedData[i];
if (transportObj.transportsId === transportsId) {
return transportObj.data;
}
}
return null;
}
};
});
Controller
.controller('ImportController', function ($scope, $routeParams, importFactory){
$scope.transportId = $routeParams.id;
importFactory.getImport($scope.transportId).then(function (successData) {
console.log('success');
console.log(successData);
}, function (errorData) {
console.log('error');
console.log(errorData);
});
You basically need this: Demo here.
var cachedPromises = {};
return {
getStuff: function(id) {
if (!cachedPromises[id]) {
cachedPromises[id] = $http.post("/import/create/" + id).then(function(resp) {
return resp.data;
});
}
return cachedPromises[id];
}
};
Now, when you fetch that data, you can manipulate and it will be changed when you access it in the future.
myService.getStuff(whatever).then(function(data) {
data.foo = 'abc';
});
//elsewhere
myService.getStuff(whatever).then(function(data) {
console.log(data.foo); // 'abc'
});
Here's a demo that does this, as well as a view updating trick (bind the object to the view before the data comes in), and an idea of how you could change the data separately from the cache, in case you want to have the original data and the changing data. http://jsbin.com/notawo/2/edit
Remember to avoid that nasty promise anti-pattern. If you already have a promise, use that instead of creating another with $q. $http already returns a promise and that promise is sufficient for whatever you need if you use it properly.
just change the loop condition look like this and then test i think your function and defer is work fine but the loop does not sent the correct data
for(var i = 0; i < this.fetchedData.length; i++) {
if (this.fetchedData[i].transportsId === transportsId) {
return this.fetchedData[i].data;
}
}
return null;
}
The reason you are getting undefined is you are not returning anything from the $http.post().then() !
Also in your getImport() function you are returning an empty promise when the transport is already cached. You need to resolve it to your already cached transport object.
getImport: function (transportsId) {
var factory = this;
var deferred = $q.defer();
var preFetchedTransport = this.findTransport(transportsId);
if (preFetchedTransport === null) {
console.log('fetching from backend');
return $http.post("/import/create/" + transportsId).then(function (data) {
console.log('data fetched:');
console.log(data);
factory.cacheTransport(transportsId, data);
return data; //this was missing
});
}
// resolve it with transport object if cached
deferred.resolve(preFetchedTransport);
return deferred.promise;
},

Javascript scope error when accessing from callback

Below is part of code from angularjs service. It may not a specific question to angular though.
The $http.get('/api/test/1').then ( ... returns promise and I like to process the data returned by the call back. I am getting error when accessing filter method.
Test.filter(data.Root);
TypeError: Object #<Object> has no method 'filter'
But, I could access the data variable in the same scope (previous line) though.
var testApp = angular.module('testApp.services', []);
testApp.factory('Test', function ($http, $rootScope) {
var Test = {};
var data = [];
Test.filter = function (d) {
ret = data.filter(function (el) {
return el.Pid == d.Id;
});
return ret;
};
Test.data = function () {
return data[1];
};
Test.start = function () {
Test.asyncData = $http.get('/api/test/1')
.then(function (response) {
data = response;
return Test.filter(data.Root);
}, function (response) {
Test.error = 'Can\'t get data';
data = 'Error: ' + response.data;
return data;
});
};
return Test;
});
I think your error is coming from:
ret = data.filter(...
The data variable, which you set to the response, doesn't have a filter method.
It is probably either not of the type you think it is, or you meant to call the filter method on something else.

Categories

Resources