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.
Related
Currently I developing cordova application by using Angularjs framework. I want to get value return $cordovaSQLite.execute() function. Usually I get the value of return by $cordovaSQLite.execute() by using .then(result()). However, the value result from .then(result()) function cannot used in outside .then(result()). Is it any solution for me to get value like result.rows.item(0).UserName; outside of my .then(result()) function. The following is my code.
var channelNameQuery = "SELECT * FROM chat_friend WHERE UserName=?"
var channelNamePromise = $cordovaSQLite.execute(db, channelNameQuery, [channelName]).then(function (result){
var abc = result.rows.item(0).UserName;
console.log(abc);
})
Next, I try with following code to get ChannelNamePromise value. It failed to get the value.
var abc = channelNamePromise.rows.item(0).UserName;
console.log(abc);
You can do it like this.
Let's just create a global method inside AppCtrl so that you can use it in multiple controllers.
.controller('AppCtrl', function ($scope,$rootScope) {
$rootScope.getFromDB = function (channelName) {
var deferred = $q.defer();
var items = [];
var query = "SELECT * FROM chat_friend WHERE UserName=?";
$cordovaSQLite.execute(db, query, [channelName]).then(function (res)
{
for (var index = 0; index < res.rows.length; index++) {
var item = res.rows.item(index);
items.push(item);
}
deferred.resolve(items);
}, function (err) {
deferred.reject(items);
console.error(err);
});
return deferred.promise;
};
})
Now in your controller
.controller('ExampleCtrl', function ($scope,$rootScope) {
$rootScope.getFromDB('yourChannelName').then(function (data) {
console.log('Result'+data); //success block, do whatever you want
});
})
You can also assign the result to any array, like this
.controller('ExampleCtrl', function ($scope,$rootScope) {
$scope.resultArr = $rootScope.getFromDB('yourChannelName').then(function (data) {
$scope.resultArr = result; //do whatever you want here
});
//outside then block you can use it.
console.log("Result"+ $scope.resultArr);
})
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.
I would like to call a function in mg-repeat and this function call a http request with id for find list data but when i try this i have one error message this.
this a call of function :
<div ng-repeat="ListeReponse in reponsefonction(Listechamps.keyQuestion)" >
<label class="col-xs-4 control-label">
{{ListeReponse.reponse}}
</label>
</div>
this is a function :
$scope.reponsefonction = function(idQuestion)
{
var defer = $q.defer();
return RequestService.get('question/'+idQuestion+'/reponse').success(function(data)
{
defer.resolve(data);
})
return defer.promise;
}
My Service :
app.factory('RequestService', function ($http, WEB_SERVICE_URL)
{
var requestService = {};
requestService.get = function (type)
{
var response = $http.get(WEB_SERVICE_URL.url+type);
return response;
};
// ajout
requestService.post = function (type, data)
{
var response = $http.post(WEB_SERVICE_URL.url+type, data);
return response;
};
requestService.put = function (type, data)
{
var response = $http.put(WEB_SERVICE_URL.url+type, data);
return response;
};
return requestService;
})
error message :
docs.angularjs.org/error/$rootScope/infdig?p0=10&p1=%5B%5D
You created an infitie loop. Each call will trigger another call. Angular evaluates the responseFunction everytime the calls return. You will need to re-work your app a little:
Do not call a function in ng-repeat. Instead simply link it a to a variable. Fill the variable once on startup with a get query.
I don't think ng-repeat works with promises. In this case the ng-repeat is looping through "null/undefined" which causes this error.
Try this:
html:
<div ng-repeat="ListeReponse in getResponses(Listechamps.keyQuestion)" >
<label class="col-xs-4 control-label">{{ListeReponse.reponse}}</label></div>
controller:
var idQuestions = {}; // keep track of the entire list
$scope.getResponses = function(idQuestion) {
var notInitialized = idQuestions[idQuestion] == null;
idQuestions[idQuestion] = notInitialized ? [] : idQuestions[idQuestion]; // initialize to empty array
if (notInitialized) {
$scope.reponsefonction(idQuestion); //kicks off ajax request to get data
}
return idQuestions[idQuestion];
}
$scope.reponsefonction = function(idQuestion) {
RequestService.get('question/' + idQuestion + '/reponse').success(function(data) {
//assuming data is an []
Array.prototype.push.apply(idQuestions[idQuestion], data); //update the same array object to prevent multiple $digest cycles
}
}
}
Here is what my services.js looks like:
var app = angular.module('starter.services', [])
.factory('Studies',function($http,$filter){
var studies = [];
$http.get("studies.json").success(
function(data){
//studies = data;
angular.copy(data, studies);
}
);
single_object = $filter('filter')(studies, function (d) {return d.nodeRef === "56e3382b-9a76-48ee-9c14-907e71b7a184";})[0];
console.log(single_object);
return {
all: function(){
return studies;
}
};
})
So as you can see I do a get request for a json file which contains objects with attributes "nodeRef" happens to be an attribute. I want to filter out a specific object based on its noderef matched. Currently the console.log returns "undefined" I think it is perhaps because it gets called before the json file is even loaded. I would really appreciate it if someone could provide a solution.
You are performing an asynchronous request and firing the filter before the server's response, so that is why you are getting undefined, because at that point, you are applying a filter to an empty array.
You should move your filter call inside the success block to make it works as you want:
var app = angular.module('starter.services', [])
.factory('Studies',function($http,$filter){
var studies = [];
$http.get("studies.json").success(
function(data){
//studies = data;
angular.copy(data, studies);
single_object = filter("56e3382b-9a76-48ee-9c14-907e71b7a184");
console.log(single_object);
}
);
function filter(node) {
if (studies.length > 0) {
return $filter('filter')(studies, function (d) {return d.nodeRef === node;})[0];
}
}
return {
all: function(){
return studies;
},
filtered: filter
};
})
You are right the console shows "undefined" because studies hasn't been populated after the ajax request yet. Move the filter and console log into the .success function:
$http.get("studies.json").success(
function(data){
//studies = data;
angular.copy(data, studies);
single_object = $filter('filter')(studies, function (d) {return d.nodeRef === "56e3382b-9a76-48ee-9c14-907e71b7a184";})[0];
console.log(single_object);
}
);
Also, I think you need a promise here to update studies when it is complete. Check out Angular $q
You would return the promise, and then resolve it inside the $http.success function, like this:
var deferred = $q.defer();
.factory('Studies',function($http,$q,$filter){
var studies = [];
$http.get("studies.json").success(
function(data){
//studies = data;
angular.copy(data, studies);
//At this point you can filter data as required, or not at all. I would suggest returning the entire JSON response and not filtering here at all, let the controllers filter the data as needed to maintain a layer of separation between controller and service.
deferred.resolve(studies);
}
);
return {
all: function(){
return deferred.promise;
}
};
//In your controller, or wherever else...
Studies.all().then(function(studies){ //this will fire when everything is all done. The studies variable is a full JSON object returned from the server (See deferred.resolve() in the service class). Filter here accordingly:
$scope.studies = $filter('filter')(studies, function (d) {return d.nodeRef === "56e3382b-9a76-48ee-9c14-907e71b7a184";})[0];
})
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;
},