Angular Factory and Service work but not as expected - javascript

My app is looking up google place details and displaying some of the information. I have a list of place id's in a json file broken down by type of establishment. A factory accesses and makes available the ids to the controller. I also have a service that loops through all the id's, looking up the details and adding them to an object that is made available to the controller.
I can get it to work in the sense that I can access the json data, look up the details, and return the object. However, no matter how I do it, if I try and return multiple objects, one for each type of business, I get all the businesses together or an error (more on that in a minute).
I have structured this a number of ways but I will show the code for 2 ways that I have tried. I'm new to Angular so I may have this completely wrong and not even using services and factories correctly so please go easy on me.
locations.json
{
"restaurants": {
"Michaels": "ChIJwaTJAL4n5IgRgyJupbpQhjM",
"Collage": "ChIJw5HgNzAm5IgRqbkEqKXIpC4",
"Scarlet": "ChIJT9ImkZUn5IgREb1hYwKA1Nc",
"Maya": "ChIJofgqBJYn5IgRVa-HQvp6KDk",
"Ice": "ChIJnXpQpewn5IgR7k9yxWXUu1M",
"Sangrias": "ChIJITcc_ZUn5IgR90iEna6FRGM",
"Columbia": "ChIJ8xR18JUn5IgRfwJJByM-quU",
"Harrys": "ChIJ8aLBaJYn5IgR60p2CS_RHIw"
},
"bars":
{
"Scarlet": "ChIJT9ImkZUn5IgREb1hYwKA1Nc",
"Lion": "ChIJqVCL_b0n5IgRpVR5CFZWi4o",
"Tradewinds": "ChIJpwF4ZJYn5IgRTDzwBWvlSIE",
"Ice": "ChIJnXpQpewn5IgR7k9yxWXUu1M",
"Stogies": "ChIJlwkiApYn5IgR6XVFMyqLAS4",
"Rondeazvous": "ChIJkz3V7pUn5IgRQhui26imF1k",
"Meehan": "ChIJK8NZGZYn5IgRA91RrGETwrQ",
"Sangrias": "ChIJITcc_ZUn5IgR90iEna6FRGM",
"NoName": "ChIJA-VeCb4n5IgRmbuF8wdOGaA",
"StGeorge": "ChIJ4yo36JUn5IgRXgiRD7KMDe0"
}
}
Method 1
locations.js
angular.module('app.locations', [])
.factory('restsFact', function($http){
var restaurants = [];
return {
getRests: function(){
return $http.get('locations.json').then(function(response){
restaurants = response.data.restaurants;
return restaurants;
});
}
};
})
.factory('barsFact', function($http){
var bars = [];
return {
getBars: function() {
return $http.get('locations.json').then(function(response){
bars = response.data.bars;
return bars;
});
}
};
})
.service('locationsService', function (ngGPlacesAPI) {
var x, id, details, push, placeDetails = [];
// Takes list of specific type of locations as argument and looks up Place details for each location
this.details = function(type) {
for (x in type) {
if (type.hasOwnProperty(x)) {
id = type[x];
ngGPlacesAPI.placeDetails({placeId: id}).then(push);
}
}
return placeDetails;
};
push = function (data) {
details = data;
placeDetails.push(details);
};
});
Controllers
.controller('RestCtrl', function($scope, locationsService, restsFact) {
// Location Details Object
restsFact.getRests().then(function(locs){
$scope.restaurants= locationsService.details(locs);
});
})
//
// Bar Controller
//
.controller('BarsCtrl', function($scope, locationsService, barsFact){
// Locations Details Object
barsFact.getBars().then(function(locs){
$scope.bars = locationsService.details(locs);
});
})
Method 2
With this method I can load one page but if I move to the next I get an error: [$rootScope:inprog] $digest already in progress. I read up on the error and get the idea of why I get it but just not sure how to go about fixing it.
locations.js
angular.module('app.locations', [])
.factory('locationsFact', function($http){
var locations = [];
return {
getlocations: function(){
return $http.get('locations.json').then(function(response){
locations = response;
return locations;
});
}
}
})
.service('locationsService', function (ngGPlacesAPI) {
var x, id, details, push, placeDetails = [];
// Takes list of specific type of locations as argument and looks up Place details for each location
this.details = function(type) {
for (x in type) {
if (type.hasOwnProperty(x)) {
id = type[x];
ngGPlacesAPI.placeDetails({placeId: id}).then(push);
}
}
return placeDetails;
};
push = function (data) {
details = data;
placeDetails.push(details);
};
});
Controller
.controller('locationsCtrl', function($scope, locationsService, locationsFact){
// Locations Details Object
locationsFact.getlocations().then(function(locs){
$scope.restaurants = locationsService.details(locs.data.restaurants);
$scope.bars = locationsService.details(locs.data.bars);
});
})

So I read a lot over the last week and learned a lot as well. I completely rewrote that mess up above into something resembling decent code, there were a lot of problems with it originally. I got everything working anyway. Here is how it looks now.
Factory
angular.module('app.factories', [])
.factory('data', function($http){
// Get JSON With Place ID's and create array of
// place id objects for each category
var places = {};
places.ids = function(){
return $http.get('locations.json')
.success(function(data){
places.rests = data.restaurants;
places.bars = data.bars;
places.lodg = data.lodging;
places.att = data.attractions;
});
};
return places;
})
.factory('details', function(ngGPlacesAPI, $q){
var details = {};
// Split ID Array into array of arrays <= 10.
// Google won't return more than 10 details request at one time.
details.process = function(type) {
var idSets = {},
size = 10,
i, j, k;
for (i=0, j=type.length, k=0; i<j; i+=size){
idSets[k] = type.slice(i, i+size);
k++;
}
return idSets;
};
// Lookup Details by Place ID
// Loop through ID array and return array of details objects
details.getDetails = function(idSet, pageNum) {
var page = idSet[pageNum],
promises = [];
for(var i=0; i<page.length; i++) {
promises.push(ngGPlacesAPI.placeDetails({placeId: page[i][i]}));
}
return $q.all(promises);
};
// Return Details Object
return details;
});
Controller
//
// Restaurants Controller
//
.controller('restaurantsCtrl', function(details, data, $scope) {
var vm = this;
// Get JSON file with placeIds and set some variables
data.ids().then(function() {
var page = details.process(data.rests),
pageNum = 0,
numPages = page.length;
vm.moreData = true;
// Loads more place details on scroll down
vm.loadMore = function() {
if (pageNum <= numPages - 1) {
pageNum++;
details.getDetails(page, pageNum).then(function(response) {
vm.rests.push(response);
vm.$broadcast('scroll.infiniteScrollComplete');
});
}else{vm.moreData=false}
};
// Load first page of place details
details.getDetails(page, pageNum).then(function(response){
vm.rests = response;
console.log(vm.rests);
});
// Watches for when to load more details
$scope.$on('$stateChangeSuccess', function(){
vm.loadMore();
});
});
})

Related

Angular Js how to refresh a new factory instance

I am using a factory to get list of folders and display it in the front end. Also in the front end i have form where i can add new folder to the existing list. After adding a folder i want to refresh my factory instance and display the updated folder list.
// factory
angular.module('myapp').factory('archiveService', ['$http', 'ApiUrl', function($http, ApiUrl) {
var archiveService = function() {
this.repos = [];
this.busy = false;
this.page = 0;
};
archiveService.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
var url = ApiUrl.url + '/folders/?page=' + this.page;
$http.get(url).then(function(res) {
console.log(res);
this.repos = res.data;
if (items.repos == 0) {
return;
}
this.page += 1
this.busy = false;
}.bind(this)).catch(function(data) {
}.bind(this));
};
return {
archiveService: archiveService,
}
}]);
// this is my controller
angular.module('myapp').controller('archiveModalController', ['$rootScope', '$scope','archiveService', function($rootScope, $scope, archiveService) {
// I want to refresh this and show new data on update
$scope.archivelist = new archiveService.archiveService();
}])
I would like to know how can i refresh so i can get the new updated data
$scope.archivelist = new archiveService.archiveService();
Angular services follow the singleton pattern meaning the instantiation of a class is restricted to a single object.
Also, since you are using a factory:
angular.module('myapp').factory('archiveService', [<dependencies>, function () {
function nextPage() {
//code here
);
return {
nextPage: nextPage
}
}]);
Then in your controller you simply:
archiveService.nextPage();
Seeing the variables in place I believe nextPage could simply receive the page as a parameter and since repos is an array I guess you meant to add the new fetched data to that array?
Which would be:
this.repos.push(res.data;) instead of this.repos = res.data;
Point is, each time you want to request new data you should call the correct service/factory method from the controller.
So at your controller init, you only need:
(function init() {
$scope.archivelist = archiveService.nextPage();
})();
Although like I stated, you should probably have an initial value like nextPage(1) and from there send the desired page you want to the factory method to be handled correctly.

Multiple api calls using AngularJS

I am fairly new to AngularJS and I am practising below exercise with requirement
1.Using the API to get 20 posts and display them on the page along with the user’s
name who created the post and display them on the page.
For this exercise I am using https://jsonplaceholder.typicode.com/ as the data source.
I need to do 2 api calls in same controller
To get list of 20 posts which has userid in it(https://jsonplaceholder.typicode.com/posts)
Based on the above user Id I need to get username (https://jsonplaceholder.typicode.com/users/userId)
Please see my work done in plnkr, I am able to display Post but not username.
Script.js
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http.get("https://jsonplaceholder.typicode.com/posts").then(function(response) {
$scope.data = response.data;
var postList = [];
for (var i = 0; i < 20; i++) {
var display = {
UserName: $http.get("https://jsonplaceholder.typicode.com/users/" + $scope.data[i].userId).then(function(response) {
$scope.user = response.data;
}),
Post: $scope.data[i].title
}
postList.push(display);
}
$scope.list = postList;
});
});
Index.html
<div ng-repeat="x in list">
Post:{{ x.Post }}
UserName:{{x.UserName}}
</div>
I believe this area is wrong:
.then(function(response) {
$scope.data = response.data;
var postList = [];
for (var i = 0; i < 20; i++) {
var display = {
UserName: $http.get("https://jsonplaceholder.typicode.com/users/"+$scope.data[i].userId).then(function(response){
$scope.user = response.data;
}),
Post: $scope.data[i].title
}
postList.push(display);
}
$scope.list = postList;
});
where you stored a Promise object in your UserName property and produced unexpected result.
to correct this assign the postList after the request has finished:
.then(function(response) {
$scope.data = response.data;
var postList = [];
for (var i = 0; i < 20; i++) {
$http.get("https://jsonplaceholder.typicode.com/users/"+$scope.data[i].userId).then(function(response){
$scope.user = response.data;
var display = {
UserName: "",
Post: $scope.data[i].title
};
$scope.list.push(display);
});
}
$scope.list = postList;
});
Once you implemented this you will encounter a new problem:
since you called $http.get() in a loop and actually used the variable i inside .then() by the time .then() executes the value of i is already in its final form which is i = 20 | data.length which every .then() calls will receive.
in order to overcome this problem the best way I know is to format the entire data first before displaying it:
$http.get("https://jsonplaceholder.typicode.com/posts")
.then(function(response)
{
var data = response.data;
var postList = [];
// this will check if formatting is done.
var cleared = 0;
// create a function that checks if data mapping is done.
var allClear = function () {
if (postList.length == cleared)
{
// display the formatted data
$scope.list = postList;
}
};
for (var i = 0; i < data.length; i++)
{
// create a object that stores the necessary data;
var obj = {
// the ID will be needed to store name;
ID: data[i].userId,
Post: data[i].title,
UserName: ""
};
var url = "https://jsonplaceholder.typicode.com/users/" + obj.userId;
$http.get(url).then(function(response)
{
// find its entry in the array and add UserName;
postList.forEach(function (item)
{
if (item.ID == response.userId)
{
// just add the correct key, but I will assume it is `userName`
item.UserName = response.userName;
// break the loop
return item;
}
});
// increment cleared
cleared++;
// call allClear
allClear();
});
postList.push(obj);
}
}
);
in this way we are sure that the data is complete before displaying it in the view.
as this solution contains a loop to map the result with its original object, we can actually change postList as an object to make it a bit faster:
// var postList = [];
var postList = {};
// instead of pushing we will use the ID as key
// postList.push(obj);
postList[obj.ID] = obj;
and so in this section:
$http.get(url).then(function(response)
{
// instead of looking for the item in .forEach
postList[response.userId].userName = response.userName;
// increment cleared
cleared++;
// call allClear
allClear();
});
hope that helps.
The easy solution would be to add the username to the user object and then push it to the scope list when the promise is resolved
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http.get("https://jsonplaceholder.typicode.com/posts").then(function(response) {
$scope.data = response.data;
$scope.list = [];
for (var i = 0; i < 20; i++) {
$http.get("https://jsonplaceholder.typicode.com/users/" + $scope.data[i].userId)
.then(function(response) {
var user = {
UserName: response.data.username,
Post: $scope.data[i].title
}
$scope.list.push(user);
});
}
});
});

Array not showing up in View after being generated from function in AngularJS

So I have this simple function that groups products into their respective categories. You can also see I'm calling the function within a timeout.
Problem is, even though the console.log() works like it should, I can't see anything in my view.
function sortByCategories(){
var temp = [];
angular.forEach(admin.categories, function(category, value){
angular.forEach(admin.products, function(product, value){
angular.forEach(product.categories, function(prodCat, value){
if(prodCat.text == category.text)
{
if(!temp[category.text])
{
temp[category.text] = [];
}
temp[category.text].push(product);
}
})
})
})
admin.sortedProducts = temp;
}
$timeout(function(){
sortByCategories();
console.log(admin.sortedProducts); // This shows me what I expect.
}, 3500);
My html in my view is simply just <pre>{{admin.sortedProducts | json}}</pre>
All I see in my view is []
What am I missing?
UPDATE
I moved this function into a service and placed it as a resolve in my route. I am still facing the same issue. Here is what I have.
.factory('Products', function($rootScope, $timeout, $q, fbUrl, $firebaseArray){
return {
sortByCategories: function() {
var temp = [];
var deferred = $q.defer();
var ref = new Firebase(fbUrl);
ref.once('value', function(snapshot){
var categories = snapshot.child('categories').val();
var products = snapshot.child('products').val();
angular.forEach(categories, function(category, value) {
angular.forEach(products, function(product, value){
angular.forEach(product.categories, function(prodCat, value){
if(prodCat.text == category.url)
{
if(!temp[category.url])
{
temp[category.url] = [];
}
temp[category.url].push(product);
}
})
});
});
deferred.resolve(temp);
})
return deferred.promise;
}
}
})
And then in my route I have this in a resolve:
...
resolve: {
categorySortedProducts : function(Products){
return Products.sortByCategories();
}
}
...
Then that's loaded in my controller
.controller('productsCtrl', function($scope, categorySortedProducts){
var admin = this;
$scope.sortedProducts = categorySortedProducts;
console.log($scope.sortedProducts);
...
})
(^^ The console.log works by the way!)
Then finally in my html view I simply have this:
<pre>{{sortedProducts | json}}</pre>
I do have the controller loaded in my route and it is loaded as admin. (productsCtrl as admin).
UPDATE 2
So it looks like the console.log() is kind of weird. This is what it looks like:
Array[0]
category-one[12]
categroy-two[15]
Is that normal, for the parent to show Array[0]? Just trying to find issues.
I changed in my promise the following.
var temp = [];
to
var temp = {};
And now everything shows up in my View. I'm guessing it's because my data isn't setup sequentially like [] is typically used for.
Your view is looking on the scope but your admin variable is locally-defined and not attached to Angular's scope.
Change your code:
$scope.admin.sortedProducts = temp;
So why did it work in a console.log and not in your view? Because the view is looking on the controller's scope. But your console.log was looking in the local namespace which doesn't need to be on Angular's scope.

AngularJS service inheritance issues

I have a base service which looks like this:
.service('BaseImageService', ['$q', 'ApiHandler', 'UploadService', function ($q, api, uploadService) {
// Get our api path
var apiPath = 'logos';
// Creates our logo
var _createLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.post(apiPath, model);
});
};
// Edit our logo
var _editLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.put(apiPath, model);
});
};
// Handles our files
var _handleUploads = function (model) {
// Create a promises array
var promises = [];
// Get our file
var file = model.file,
old = model.old;
// If we have a file
if (file) {
// Try to upload the file
promises.push(uploadService.upload(model.file).then(function (response) {
// Update our model
model.path = response.path;
model.thumbnail = response.thumbnail;
}));
// If we have an old model
if (old) {
// Delete both our files
promises.push(uploadService.delete(old.path));
promises.push(uploadService.delete(old.thumbnail));
}
}
// After all promises have completed
return $q.all(promises);
};
// Create our service
var service = {
// Update our api path
updateApiPath: function (path) {
// Set the api path
apiPath = path;
},
// Gets a list of logos
list: function (t) {
if (t) {
console.log(apiPath);
}
// Get our logo
return api.get(apiPath);
},
// Get a single logo
get: function (id) {
// Get our logo
return api.get(apiPath, { id: id });
},
// Create our logo
save: function (model) {
// If we are editing
if (model.id) {
// Edit our logo
return _editLogo(model);
// If we are creating
} else {
// Create our logo
return _createLogo(model);
}
},
// Deletes our logo
delete: function (id) {
// Delete our logo
return api.delete(apiPath, { id: id });
},
// Prepare for editing
prepareForEditing: function (model) {
// Create our old object
model.old = {
path: model.path,
thumbnail: model.thumbnail
};
}
};
// Return our service
return service;
}])
and then I have a few services which "inherit" this service, like this:
.service('ImageService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'images';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
.service('LogoService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'logos';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
.service('PlayerTextService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'playerText';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
I thought that this was working fine. But I have this page that calls all 3 services (ImageService, LogoService and PlayerTextService) sequentially. On the first view of the page everything is fine, if I navigate away and then come back the images service actually pulls back thing from the player text service. Now I know this is because of services being singletons but I am not sure how to fix my issue.
Can anyone give me a hand?
I have added a codepen with an attempted solution:
http://codepen.io/r3plica/pen/ONVBJO
Attempt 2
http://codepen.io/r3plica/pen/jqPeMQ?editors=1010
The solution you tried doesn't work because the BaseService is a singleton. So you inject exactly the same instance into all three service registration functions and all of them configure the same object. So basically the last one wins.
Looks like you want to have separate services with different configurations. This is what providers are used for. They allow a two-step process of building a service instance. Please see this great Stackoverflow answer on the topic:
AngularJS: Service vs provider vs factory
For reference, Restangular is a library that needs to achieve exactly the same as you want. You could use this as a blueprint and see how Restangular handles this requirement:
https://github.com/mgonto/restangular#how-to-create-a-restangular-service-with-a-different-configuration-from-the-global-one
Please be aware that these concepts are based on AngularJS 1 and you need to handle this differently when you want to use AngularJS 2 later on.
After a lot of messing around; I finally found a solution adapting this bit of code
My base service looks like this:
.factory('BaseImageService', ['$q', 'ApiHandler', 'UploadService', 'vectorExtensions', function ($q, api, uploadService, vectorExtensions) {
// Creates our logo
var _createLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.post(BaseImageService.apiPath, model);
});
};
// Edit our logo
var _editLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.put(BaseImageService.apiPath, model);
});
};
// Handles our files
var _handleUploads = function (model) {
// Create a promises array
var promises = [];
// Get our file
var file = model.file,
old = model.old;
// If we have a file
if (file) {
// Try to upload the file
promises.push(uploadService.upload(model.file).then(function (response) {
// Update our model
model.path = response.path;
model.thumbnail = response.thumbnail;
model.fileName = response.fileName;
}));
// If we have an old model
if (old) {
// Delete both our files
promises.push(uploadService.delete(old.path));
promises.push(uploadService.delete(old.thumbnail));
}
}
// After all promises have completed
return $q.all(promises);
};
// Addes a property to the image array to state if they are vector images or not
var _addVectorProperties = function (images) {
// Loop through our images
for (var i = 0; i < images.length; i++) {
// Get our current image
var image = _addVectorProperty(images[i]);
}
// Return our images
return images;
};
// Adds a property to the image to state if it is vector or not
var _addVectorProperty = function (image) {
// Vector flag
var vector = false;
// Get our file extension
var parts = image.path.split('.');
// If we have any parts
if (parts.length) {
// Get our last part
var ext = parts[parts.length - 1],
index = vectorExtensions.indexOf(ext);
// If our extension exists in our vector array
if (index > -1) {
// Change our vector property
vector = true;
}
}
// Update our image with the new property
image.vector = vector;
// Return our image
return image;
};
// Create our service
var BaseImageService = function (path) {
// Set our apiPath
this.apiPath = path;
// Update our api path
this.updateApiPath = function (path) {
// Set the api path
apiPath = path;
};
// Gets a list of logos
this.list = function () {
// Get our logo
return api.get(this.apiPath).then(_addVectorProperties);
};
// Get a single logo
this.get = function (id) {
// Get our logo
return api.get(this.apiPath, { id: id }).then(_addVectorProperty);
};
// Create our logo
this.save = function (model) {
// If we are editing
if (model.id) {
// Edit our logo
return _editLogo(model);
// If we are creating
} else {
// Create our logo
return _createLogo(model);
}
};
// Deletes our logo
this.delete = function (id) {
// Delete our logo
return api.delete(this.apiPath, { id: id });
};
// Set our active image
this.setActive = function (images, image) {
// Loop through our images
for (var i = 0; i < images.length; i++) {
// Get our current image
var current = images[i];
// Set whether we are active or not
current.active = image.id === current.id ? true : false;
}
};
// Prepare for editing
this.prepareForEditing = function (model) {
// Create our old object
model.old = {
path: model.path,
thumbnail: model.thumbnail
};
};
};
// Return our service
return BaseImageService;
}])
and the child services look like this:
.service('ImageService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('images');
// Return our new service
return child;
}])
.service('LogoService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('logos');
// Return our new service
return child;
}])
.service('PlayerTextService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('playerText');
// Return our new service
return child;
}])
That works fine.
Yes it append because it's singleton. You have to perform inheritance if you want to do this.
Here is a code that i use and append to angular :
angular.baseResourceServiceMaker = function(service){
return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint',
function($injector, $resource,TypeService, $http, _, BackEndpoint){
//skipping not interesting code
// sample fields to inherits
this.sample = "test";
this.sampleFN = function(){[...]}
// THE line that does the magic
$injector.invoke(service, this);
}
Now time of usage
.service('MyService',angular.baseResourceServiceMaker(['$http', function($http){
// overriding fields
this.sample="inherits";
this.sampleFN = function(){[...]}
}]));
So basically what do we have here ? A function baseResourceServiceMaker which represent a generic particular service. The $injector that call the service we want to instantiate and set the scope to the generic service, so the this on the child class will bind to the same reference than the generic class. The generic service will be instantiated as many times you call it, no confict.
I personally use this code for the resource module of angular to define some basic methods having a custom serializer / deserializer than handle dates and some other stuffs. In your case the baseResourceServiceMaker will be your baseImageService with ending with the $injector.invoke(service, this).
EDIT : found a link with something probably cleaner : AngularJS service inheritance
It becomes very easy if you use (or switch to) ES6 or TypeScript.
export class Base {
// . . .
}
Then:
import {app} from '../app';
import {Base} from './base';
export class Child extends Base {
// . . .
}
app.service('child', Child);
From your Attempt 1:
your BaseService looking for apiPath at global level,
while angular preparing dependency your last dependency is ImageService,
first it will prepare dependency then it will execute list() method,
all ways your apiPath will refer global level declared value, i.e apiPath = 'images'; from step2
Solution: use this operator in front of apiPath under BaseService and list().
working Plunker

Set factory variable from state

I am using factories to get feed data for different categories in wordpress using the Rest-API and I am wondering if I am making a complete mess of it.
I have around 13 different categories that I am making just a basic call to that look like this:
.factory('Articles1', function ($http) {
var articles1 = [];
storageKey = "articles1";
function _getCache() {
var cache = localStorage.getItem(storageKey );
if (cache)
articles = angular.fromJson(cache);
}
return {
all: function () {
return $http.get("http://www.example.com/wp-json/posts?filter[category_name]=category1").then(function (response) {
articles1 = response.data;
console.log(response.data);
return articles1;
});
},
get: function (articleId) {
if (!articles1.length)
_getCache();
for (var i = 0; i < articles1.length; i++) {
if (parseInt(articles1[i].ID) === parseInt(article1Id)) {
return articles1[i];
}
}
return null;
}
}
})
there is a separate call for each of the 13 categories to get articles.
Is there a way to set the [category_name] to a variable, possibly the state name or something to that I can just make 1 call to wordpress and the url will rely on what state the user has chosen? I have been trying to learn angular and from what I have seen I am sure there must be a more elegant way of doing something like this?
Inject the Angular $location provider and you will have access to every part of your current URL (you can also use it to listen to the state changes). Then just build the URL to Wordpress dynamically:
.factory('Articles1', function ($http, $location) {
// ...
return {
all: function () {
var currentCategory = $location.path().split('/')[1]; // Assuming the category is part of the path and at the second place
var wpUrl = 'http://www.example.com/wp-json/posts?filter[category_name]=' + currentCategory;
return $http.get(wpUrl).then(function (response) { // ...

Categories

Resources