I have an Angular service/provider that serves json data to my controller which works great:
angular.module('myApp.services', ['ngResource']).
factory("statesProvider", function($resource){
return $resource('../data/states.json', {}, {
query: {method: 'GET', params: {}, isArray: false}
});
});
But I also need to serve json data to the same controller from another file counties.json.
Where can I find out how to I write a service that serves both files to my controller?
You can update service to return a hash of resources, not a single one:
angular.module('myApp.services', ['ngResource']).
factory("geoProvider", function($resource) {
return {
states: $resource('../data/states.json', {}, {
query: { method: 'GET', params: {}, isArray: false }
}),
countries: $resource('../data/countries.json', {}, {
query: { method: 'GET', params: {}, isArray: false }
})
};
});
You will be able to use it adding .query() at the end your function name i.e. geoProvider.states.query() and geoProvider.countries.query() and myApp.services has to be injected into your controller, then inject geoProvider service into controller itself as well.
I'm assuming you want to execute some code when both files have loaded. Promises work really well for this. I don't think resources return promises, but you can use the $http service for simple ajax calls.
Here I define one service each for the two data files, and a third service that returns a promise that gets fulfilled when both files are done loading.
factory('states',function($http) {
return $http.get('../data/states.json');
}).
factory('countries',function($http) {
return $http.get('../data/countries.json');
}).
factory('statesAndCountries', function($q, states, countries) {
return $q.all([states, countries]);
});
Then in your controller:
statesAndCountries.then(function(data) {
var stateData = data[0];
var countryData = data[1];
// do stuff with stateData and countryData here
});
Related
First, I'm new to angularjs. I've create a factory to handle most of my data named "store". Here is an example:
app.factory('store', function ($rootScope, $http, $q, api) {
var data = {};
return {
setData: function () {
var deferred = $q.defer();
$http({
method: 'GET',
url: api.getData()
}).then(function successCallback(response) {
// handle data
$rootScope.broadcast('store:data', data);
deferred.resolve();
}, function errorCallback(reponse) {
// do stuff
deferred.reject();
});
},
getData: function () {
return data;
},
addData: function (newData) {
// do stuff
},
editData: function (newData) {
// do stuff
},
deleteData: function (newData) {
// do stuff
}
};
});
I'm initializing this data inside my app.run function. BUT, I don't want my app to wait my data to be initialized first to render the controller. I want it to be rendered first and wait for updating when the data is initialized.
store.setData()
.then(function (response) {
// do stuff
})
.catch(function (response) {
// do stuff
});
Here is how I'm getting the data updated inside my controller to be rendered
$scope.data = store.getData();
$rootScope.$on('store:data', function (event, data) {
$scope.data = data;
})
SO my problem is that I don't want to wait my data to be initialized to render my controller.
Is there a solution to this problem ?
Thanks a lot.
EDIT May 20 2021
Btw if what I'm doing is wrong and there is better things to do, I'm open to any suggestions ! Thnx
EDIT June 9 2021
Now I'm using $resource, but I don't know how can I get the new version of my list of data when I add new element to it.
agents: $resource(
api.getAgents(),
{},
{
get: {method: 'GET', isArray: false, cache: true},
add: {method: 'POST', url: api.addAgent(), hasBody: true},
edit: {method: 'PUT', url: api.editAgent(), params: {agentId: '#id'}, hasBody: true},
delete: {method: 'DELETE', url: api.deleteAgent(), params: {agentId: '#id'}},
}
),
Waiting for an answer. Thank you vm !
There are a couple options you can consider, but first a note on best practices in AngularJS and JavaScript: avoid the deferred antipattern. The $http service returns a promise. You should work with that rather than creating a new promise with $q.defer.
The first option is to change the getData method to return a promise instead of the actual data. It is a good idea to always design your data retrieval services to return promises, even when you intend to pre-retrieve and cache the data. This provides the cleanest way to ensure that the data is available before you try to use it. In your example, you should be able to internally cache the promise rather than the data. So your code would change to something like this:
app.factory('store', function ($rootScope, $http, api) {
var dataPromise;
return {
setData: function () {
dataPromise = $http({
method: 'GET',
url: api.getData()
}).then(function successCallback(response) {
// handle data
$rootScope.broadcast('store:data', data);
}, function errorCallback(reponse) {
// do stuff
});
},
getData: function () {
if (!dataPromise) {
this.setData();
}
return dataPromise;
},
// etc.
};
}
You will of course have to change the code that calls the getData method to work with the promise instead of working directly with the data.
Another option is to use an AngularJS Resource. This feature works very much like your original intent by returning an instance of an object that at some point will get populated with data. It takes advantage of the AngularJS change detection to render the data once it becomes available. Resources also have the ability to cache responses internally so that the call to the server is only made once. Rewriting your service as a resource would look something like this:
app.factory('store', function ($rootScope, $resource, api) {
return $resource(
api.getData(), // the base URL
{}, // parameter defaults
{ // actions
getData: {
method: 'GET',
cache: true
},
// etc.
}
);
}
My approach is using AngularJS Service instead of factory along the project. Now I want to build an object called MyData which should be used as array in angularJS controller, such as:
vm.datas= MyData.query({}, function (datas) {
vm.thisData = {selected: datas[0].id};
});
As I searched in questions, I understood I can use factory as below:
angular.module('myDataService', ['ngResource']).
factory('MyData', function($resource){
return $resource(myURL, {}, {
query: {method:'GET', isArray:true}
});
});
What should I do if I want to use service. Is the code correct, below? What is the best practice?
angular
.module('app')
.service('myDataService', myDataService);
myDataService.$inject = ['ngResource'];
function myDataService($resource) {
var self = this;
self.MyData = $resource(myURL, {}, {
query: { method: 'GET', isArray: true }
});
}
I try to get response data from angular service created by factory and $resource. I want to send POST request to server to create object in db and received created ID.
So I create service like this:
angular.module('sample')
.factory('Client', function ($resource) {
return $resource('api/clients/:id', {}, {
'query': { method: 'GET', isArray: true},
'get': {
method: 'GET',
transformResponse: function (data) {
data = angular.fromJson(data);
return data;
}
}
});
});
I use Client service in controller:
$scope.create = function () {
Client.save($scope.client,
function (response) {
$state.go("clientDetail", {'id' : whereIsMyId.id? })
});
};
Unfortunately, I do not know how to read data in response.
As response I just get number like "6" but this could be any JSON.
Thank you.
Here's the issue, I have multiple API calls to make along the lines of:
www.domain.com/foo/bar?token=12345
There may be more subdirectories in-between, there may be less.
I'm currently using $resource
agentApp.factory('apiFactory', function($resource) {
return $resource('www.domain.com/v1/' + ':folder',
{
'query': {
method: 'GET',
isArray: true
}
});
Which gets called as follows:
apiFactory.query({folder: 'foo', token: '12345'}, function() {...})
I would like to make this more extensible as I have the need to occasionally change the isArray value to false, and the amount of subdirectories in the URL are unknown on occasion, so I'd prefer to not use $resource's :token structure, and rather just have one that takes a string.
Is there a service I could create that would allow me to make the call as follows:
apiService.query(urlStringAndQueries, booleanForIsArray).then(function(response) { ...do something with response });
So far I've attempted the following, which obviously doesn't give me what I want, I'm not sure how to get $resource to actually kick off the API call, I put this down to a fundamental misunderstanding of how $resource works:
agentApp.factory('apiService', ['$resource', '$q', function ($resource, $q) {
var factory = {
query: function (urlStringAndQueries, isArray) {
return $q(
function() {
$resource('www.domain.com/v1/' + ':location', { location: urlStringAndQueries }, {
'query': {
method: 'GET',
isArray: isArray
}
});
}
)
},
return factory;
}]);
Which I attempt to call as follows:
apiService.query('/foo/bar?token=12345', true)
.then(function(response) { ...do something with response });
Any help and/or advice is very much appreciated.
Thanks!
EDIT: Here's my solution until a more generic pattern comes along
I couldn't simply provide an extra entity to the base URL string externally, e.g. foo/bar?token=12345, due to $response inherently encoding URL, so the characters (/?=) get converted into their encoded counterparts. Hence the separating of strings in the pattern:
agentApp.factory('apiService', ['$resource', function($resource) {
var factory = {
resourceObj: function(isArray, urlStringAndQueries) {
/* urlStringAndQueries will be variadic parameters,
so we deconstruct them for use in the URL */
var location1, location2, location3, locations = [];
angular.forEach(arguments, function(path) {
locations.push(path);
});
return $resource(vapiURL + vapiVer + '/' + ':location1' + '/' + ':location2' + '/' + ':location3' + '/' + ':location4', {
location1: locations[1],
location2: locations[2],
location3: locations[3],
location4: locations[4],
}, {
'query': {
method: 'GET',
isArray: isArray
},
'save': {
method: 'POST',
isArray: isArray
}
})
}
};
return factory;
}]);
This solution still assumes I'll have a finite amount of paths, which isn't ideal but at least it's easy to reuse.
Still open to more and better suggestions :)
I don't understand why you are returning promise object from your service method again, while $resource return promise itself. Don't do that
Code
agentApp.factory('apiService', ['$resource', '$q', function($resource, $q) {
var factory = {
resourceObj: function(urlStringAndQueries, isArray) {
return $resource('www.domain.com/v1/' + ':location', {
location: urlStringAndQueries
}, {
'query': {
method: 'GET',
isArray: isArray
}
});
)
}
}]);
Call factory method then you'll get access to resource object then call its query of that resource object method
Controller
apiService.resourceObj('/foo/bar?token=12345', true).query()
.then(function(response) { ...do something with response });
I'm trying to create a Service in Angularjs to make use of various oEmbed providers including YouTube.
...
myServices.factory('YouTubeService', function ($resource) {
//how can I make the URL part dynamic?
return $resource('http://www.youtube.com/oembed/', {}, {
query: { method: 'GET', isArray: true },
})
});
...
The oEmbed URL structure is http://www.youtube.com/oembed?url=<url_of_video>
How can I make this service work with any YouTube URL provided by the user? In other words, can I call this Service from my Controller and pass in the URL in some way?
YouTubeService.query(<url here maybe>)
Here you go, this should work, I think.
myServices.factory('YouTubeService', function ($resource) {
var youtubeservice = {};
youtubeservice.query = function(urlProvided){
return $resource('http://www.youtube.com/oembed?url=:urlProvided', {}, {
query: { method: 'GET', isArray: true },
});
}
return youtubeservice;
});
Invoke:
YouTubeService.query(<url here>)
I am not sure if you can access external url like this(may throw cross domain error)
But for your question, why don't you use a service instead of factory like this
myServices.service('YouTubeService', function ($resource) {
//how can I make the URL part dynamic?
this.getStuff = function(url){
return $resource(url, {}, {
query: { method: 'GET', isArray: true },
}).query();
}
});
And invoke it like
YouTubeService.getStuff (dynamicUrl);