I want to develop set of functions(sort of library) for CRUD in AngularJS so I can reuse them for couple of entities of my project. For server communication I made factory of $resource and using accordingly. $resource factory looks like this:
Model File:
var get_entity_model = angular.module("app.getentity", []).factory('getEntity', ['$resource', function($resource) {
return{
entity_view: $resource(baseurl+'/rest/'+serviceName+'/entity/:id/?app_name='+appName+'&fields=*', null, {'update': { method:'PUT' }})
}
}]);
And here how I'm using it in controller
Controller File:
getEntity.entity_view.get(
function(entity_list){
},
function(error){
}
)
Here entity_view is the table name. I'm passing all related functions like pagination and sub request to get the data of related tables etc code I put into success function of above request.
Now I want to make a library where I can define all this stuff and simply by calling the function I should be able to get all this stuff like:
entity.getEntity()
Should return same result as above code.
I tried with creating factory for above task but seems it need callback function and function at factory will return only data which I'm already getting from my model file so I need to make it compact and easy to use.
Factory Code at factory file:
var api = angular.module("app.entity_api", []).factory('entity_factory', ['$resource','getEntity',function($resource,getEntity) {
var entity_factory = {};
entity_factory.get_entity = function(callback){
getEntity.entity_view.get().$promise.then(
function(data){
callback(data.record);
}
);
}
return entity_factory;
}]);
And here how I call the function in controller:
Controller code:
api.controller("sample",['entity_factory','getEntity','$scope',function(entity_factory,getEntity,$scope){
$scope.init = function(){
entity_factory.get_entity(
function(data){
console.log(data);
}
);
}
$scope.init();
}])
Problem is that my entity_factory code will return only the data from server rest of the additional code I've to do in callback function which seems not much difference than my current exercise. So, the question is how can I achieve my goal to make a library of functions with additional code which return complete compiled result to make the code reusable for other entities and compact.
I like that you're a thinking of making a library but in this case, don't reinvent the wheel and save your precious time. Check out Restangular and your task will be a lot easier. Restangular is an AngularJS service that simplifies common GET, POST, DELETE, and UPDATE requests with a minimum of client code. It's a perfect fit for any WebApp that consumes data from a RESTful API.
Related
I'm testing a service that uses another service for API calls, let's call this the data service. The data service is tested elsewhere, so I've abstracted it away with a simple implementation that contains empty functions; I'm returning data via a deferred object and Jasmine's spyOn syntax.
The trouble I'm finding with this approach is when the data is returned, it's not immediately available on the calling object, as it would be if I used $httpBackend. Aware I could just use $httpBackend, but I'd like to know if I've missed something (simple or otherwise) in this approach.
Example section of code I'm trying to test:
storeTheData = dataService.getSomeData();
storeTheData.$promise.then(function(data) {
/*this would work*/
console.log(data);
/*but this would not, when testing using $q*/
_.forEach(storeTheData, function(storedData) {
/*do something with each object returned*/
});
});
As a side note, I don't think the situation is helped by the ...$promise.then on another line, but ideally I wouldn't change the code (I'm providing test coverage to something written a while ago...)
Example of the test:
beforeEach(
...
dataService = {
getSomeData: function () { }
};
getSomeDataDeferred = $q.defer();
spyOn(dataService, "getSomeData").and.returnValue({$promise: getSomeDataDeferred.promise});
...
);
it(...
getSomeDataDeferred.resolve([{obj: "obj1"}, {obj: "obj2"}]);
$scope.$apply();
...
);
With the test described above, the console.log(data) would be testable as the data is accessible from being passed into the .then(). But the data is not immediately available from storeTheData, so storeTheData[0].obj would be undefined. On debug, I can see the data if I go through the promise that was attached to storeTheData via storeTheData.$$state.value
Like I said, I know I could use $httpBackend instead, but is there any way to do this with $q without changing the code under test?
I've not found a way to do this with $q.resolve, but I do have a solution that doesn't involve using the data service or changing the code under test. This is as good, because the main things I wanted to avoid were testing the data service as a side effect and changing the code.
My solution was to create a $resource object via $injector...
$resource = $inject.get("$resource");
...then return that in my basic implementation of the data service. This means I could use $httpBackend to respond to the request to an end point that isn't reliant on the data service's definition staying consistent.
dataService = {
getSomeData: function () {
/* new code starts here */
var resource = $resource(null, null, {
get: {
method: "GET",
isArray: true,
url: "/getSomeData"
}
});
return resource.get();
/* new code ends here */
}
};
...
$httpBackend.when("GET", "/getSomeData").respond(...;
I'm using Angular to develop commenting functionality for a web app.
Currently there are two sections in the application were a user can comment:
Category
Product
About 90% of the commenting functionality is the same for both sections and as such I would like to make this reusable - i.e write some service or controller that I can reference/use as a base.
So far, my research seems to point to using a factory service but unfortunately this doesn't seem to work (I've spent the whole day running through various tutorials).
It is quite possible that I am over thinking this and making it far too complicated but I honestly don't know which way to turn anymore.
Herewith a quick and dirty overview of what I have so far:
HTML view for the category
Controller for the category (receives data from service and posts data to service in order to bind data to model)
Service for the category (retrieve and stores all the necessary
data)
The product uses the same logic and a lot of the code in the service and controller will be duplicated.
I've merged the two services into one service successfully but I'm having trouble doing the same for the controller.
Do I:
Write a base controller that will communicate with the above mentioned service and that will hookup with the two existing controllers
OR
Write a factory/provider service that hooks up to the two existing controllers as well as the above mentioned service.
If you go the route of using a factory, you could put all the common functionality into its return object and reference that from your controllers.
Factory
angular.module('myModule').factory('CommonFunctions', function(){
return {
foo : foo,
bar : bar
}
function foo(){
console.log('foo');
};
function bar (){
console.log('bar');
};
}
Controller
angular.module('myModule')
.controller('myController', ['CommonFunctions', function(CommonFunctions) {
var vm = this;
vm.foo = CommonFunctions.foo();
vm.bar = CommonFunctions.bar();
}
angular's separation of service types ie:
for specific values
constant
value
(constant for specific values needed before other services are created)
for functions
factory
service
provider
(provider for specific instances when you need a services before other services are created, usually taking advantage of constants)
allow the ability to share data and ways to process that data between controllers and or directives, anything that can be a value can also be a constant, the only difference there being where they can be injected. Similarly any service can be rewritten to a factory or a provider, it is more your specific use case / what your more comfortable writing that would determine which to use, but really the best way to think about it is if you have a value that needs to be shared but is not needed inside angular.module.config then use value, otherwise use constant, now if you have a single function that you want to share, (maybe it processes that value in some way or maybe it just does something else) you should write it as a factory, then when you have a few of those factory's that deal with either that value, or anything else, you can combine them into a service or configure and combine them using a provider. here is a simple example (note i am using the recommended syntax for writing angular services):
'use strict';
var app = angular.module('test.app',[]);
app.constant('configureableValue',{defaultValue:55});
app.value('editableValue',{defaultValue:100,editedValue:null});
app.provider('configureValue',configureValueProvider);
configureValueProvider.$inject - ['configureableValue'];
function configureValueProvider(configureableValue){
var defaultVal = configureableValue.defaultValue,
originalVal = defaultVal;
return {
getValue:getValue,
setValue:setValue,
resetValue:resetValue,
'$get':providerFunc
};
function getValue(){
return defaultVal;
}
function setValue(val){
defaultVal = val;
}
function providerFunc(){
return {
get:function(){ return getValue(); },
reset:function(){ resetValue(); }
};
}
function resetValue(){
defaultVal = originalVal
}
}
// this factory is an example of a single function service, this should almost always be defined as a factory
app.factory('getEditableValue',getEditableValue);
getEditableValue.$inject = ['editableValue'];
function getEditableValue(editableValue){
return function(){
return editableValue.editedValue ? editableValue.editedValue : editableValue.defaultValue;
};
}
// same with this one
app.factory('setEditableValue',setEditableValue);
setEditableValue.$inject = ['editableValue'];
function setEditableValue(editableValue){
return function(val){
editableValue.editedValue = val;
}
}
// now this is an example of a service service collecting the factorys for an object with all the related behavior we need
app.service('editableService',editableService);
editableService.$inject = ['getEditableValue','setEditableValue'];
function editableService(getEditableValue,setEditableValue){
var self = this;
self.setVal = setEditableValue;
self.getVal = getEditableValue;
}
app.config(appConfig);
appConfig.$inject = ['configureValueProvider'];
function appConfig(configureValueProvider){
configureValueProvider.setValue('i changed '+ configureValueProvider.getValue() +' to this!!!!');
}
app.run(appRun);
appRun.$inject = ['configureValue','editableService'];
function appRun(configureValue,editableService){
console.log('before editing: ',editableService.getVal());
editableService.setVal('changed!!!');
console.log('after editing: ',editableService.getVal());
console.log('we changed this in the config func: ',configureValue.get());
configureValue.reset();
console.log('and now its back to the original value: ',configureValue.get());
}
i know thats a lot for a simple example, but there are a lot of features provided by angular, and many ways to use them, hopefully this helps.
If I user factory in angularjs for $http call then I would use it like below,
app.factory('myDataService', function($http){
return{
getProducts:function()
{
return $http.get('api/Product');
}
}
});
and use it in controller like this,
app.controller('appCtrl',function($scope,myDataService){
//Get Products
myDataService.getProducts().success(function(data,status){
//some stuff
}).error(function(data,status){
//some stuff
})
});
This is a simple way to make call to web api......
But
here i want is to add new layer called myDataService layer which will be helpful to define get, put ,post, delete methods in simple way like this,
Note:(Here I'm giving only concept. I dont know real implementation of it.
I dont know what should i use here .factory .service or javascript simple function. But let's say I'm using factory as below
myDataService
app.factory("myDataService", function (myHttPLayer) //myHTTPLayer is injected
{
//below code is not correct (I want to connect it to myHTTPLayer somehow)
return{
var myVar={
get:{
products:'/api/Product',
companies:'/api/Companies'
},
post:{
product:'/api/Product',
company:'/api/Companies'
}
}
}
});
here what i want is to introduce new layer like this which only gives information about get,put,post methods and api calls....
I don't want to write $http.get, $http.post and all everytime in angular controller. I want to write them at one place only one time. like this,
some.js...
myHTTPLayer
app.factory('myHttPLayer',function($http){
return {
get:function()
{ return $http.get(url);},
post:function(obj)
{ return $http.post(url,obj)
}
});
I want to connect both factories and by using them or writing them at angular controller side, i want to make web api call.
Note: This is just a concept.
please help me ....
In my mind what i want to implement is,
in angular controller something like,
app.controller("appCtrl",function($scope,myDataService){
//Connect myDataService to myHTTPLayer internally so I can use them as below....
//I want my api to be called when i write myDataService.getProducts() shown as below ....
myDataService.getProducts().success(function(){})
.error(function(){});
})
A big note : edit
Dont consider single line of code of myDataSevice. Its totally wrong. Just consider that i have all posible http methods in one factory with their proper prototyes which can return promise in myHTTPLayer. I will not temper it , once it is written correctly. What i want to do is to write myDataService code such a way that i can use it in angular controller without using myHTTP layer stuff in controller. When i use myDataService in controller, it must use httplayer behind the scene and it must make angular http call to defined web api. This is just a concept to be implemented.I want to develop such architecture. But unable to find the answer.
#stackg91 posted a great answer, but I believe it can be simplified further with regard to the else block. The below will work no matter what your 'variable' is ('products', 'company', etc.).
app.factory('myHttPLayer',function($http){
return {
get: function(variable) {
if(variable == null){
return $http.get(url);
} else {
return $http.get(url + '/api/' + variable);
}
}
}
});
Then you could simply call myHTTPLayer.get(products) in your controller for example. I don't believe you need three layers to get the functionality you're looking for here. I think a controller which passes a parameter to a service making '$http calls' utilizing that parameter is enough.
I normally do it like that, hope this helps you
app.factory('myDataService',function($http){
return {
getProducts:function(variable, callback) // this variable can be products or companies
{
if(variable == null){
return $http.get(url).success(callback).error(callback);
}else if(variable == products){
return $http.get(url+'/api/Product').success(callback).error(callback);
}else if(variable == companies){
return $http.get(url+'/api/Companies').success(callback).error(callback);
}
}
});
In COntroller it would look like this
$scope.test = myHttPLayer.get(products).onsuccess(function(){
Do Something ....
});
I am trying to make Breeze.JS to make use of angular's http service for ajax calls. I followed the the docs (http://www.breezejs.com/documentation/customizing-ajax) and applied it. However it doesn't work.
Further more when I checked breeze source code I saw the following:
fn.executeQuery = function (mappingContext) {
var deferred = Q.defer();
var url = mappingContext.getUrl();
OData.read({
requestUri: url,
headers: { "DataServiceVersion": "2.0" }
},
function (data, response) {
var inlineCount;
if (data.__count) {
// OData can return data.__count as a string
inlineCount = parseInt(data.__count, 10);
}
return deferred.resolve({ results: data.results, inlineCount: inlineCount });
},
function (error) {
return deferred.reject(createError(error, url));
}
);
return deferred.promise;
};
It simply calls OData.read without doing anything about http service. Thus OData makes use of builtin ajax. I don't understand with above code, how it is possible to customize ajax of Breeeze.JS
The problem is that the Breeze OData path does NOT use the Breeze Ajax adapter. Changing the Breeze Ajax Adapter (as the "Breeze Angular Service" does) won't help.
At the moment, both the "OData" and "webApiOData" DataService Adapters delegate to the 3rd party datajs library for AJAX services (and for other OData-related support).
You could replace its odata.defaultHttpClient with a version of your own based on $http. That's not a trivial task. Look here for the source code; it's roughly 160 lines.
I suppose we could write one. It hasn't been a priority.
Until somebody does it or we abandon datajs (not soon if ever), you're stuck with the datajs ajax.
Sorry about that.
p.s. Just about everyone who talks to OData data sources uses the datajs library. Maybe you can talk to the authors of that library and try to get them to support$http.
Quick and dirty hack to simulate $http service
I ran into this issue today. Since the external datajs AJAX methods are used rather than Angular's $http service (as explained by Ward), Breeze queries do not trigger a digest and the models do not get updated.
As with any external-to-angular changes, the simple solution is to wrap any assignments from your queries in a $scope.$apply() function. However, this will quickly clutter up your app so it's a bad idea.
I came up with a quick and dirty hack that so far seems to work well:
I have a dataContextservice which encapsulates all my Breeze queries and exposes methods like getCustomers(), getProducts() etc (inspired by the example on the Breeze site).
When any of these data-access methods completes (ie the promise resolves), I call a triggerAngularDigest() method.
This method simple calls $rootScope.$apply() inside a $timeout().
The $timeout() causes Angular to run the digest on the next tick, i.e. after the data from your Breeze query has been assigned to your models.
All your models update just like when you use $http, no need to call $apply() in your controllers.
Simplified version:
function dataContext($rootScope, $timeout, breeze) {
// config of entity manager etc snipped
return {
getCustomers: function () {
return breeze.EntityQuery.from('Customers')
.using(manager)
.execute()
.then(function(data) {
triggerAngularDigest(); // <-- this is the key
return data;
});
}
};
function triggerAngularDigest() {
$timeout(function() {
$rootScope.$apply();
}, 0);
}
}
myApp.factory('dataContext', dataContext);
Then:
// some controller in your app
dataContext.getCustomers().then(function(data) {
scope.customers = data;
});
I am using AngularJS Services in my application to retrieve data from the backend, and I would like to make a loading mask, so the loading mask will start just before sending the request. but how can I know when the request ends?
For example I defined my servive as:
angular.module('myServices', ['ngResource'])
.factory('Clients', function ($resource) {
return $resource('getclients');
})
.factory('ClientsDetails', function ($resource) {
return $resource('getclient/:cltId');
})
So I use them in my controller as:
$scope.list = Clients.query();
and
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
});
So the question would be, how to know when the query and get requests ends?
Edit:
As a side note in this question I've been using using angularjs 1.0.7
In AngularJS 1.2 automatic unwrapping of promises is no longer supported unless you turn on a special feature for it (and no telling for how long that will be available).
So that means if you write a line like this:
$scope.someVariable = $http.get("some url");
When you try to use someVariable in your view code (for example, "{{ someVariable }}") it won't work anymore. Instead attach functions to the promise you get back from the get() function like dawuut showed and perform your scope assignment within the success function:
$http.get("some url").then(function successFunction(result) {
$scope.someVariable = result;
console.log(result);
});
I know you probably have your $http.get() wrapped inside of a service or factory of some sort, but you've probably been passing the promise you got from using $http out of the functions on that wrapper so this applies just the same there.
My old blog post on AngularJS promises is fairly popular, it's just not yet updated with the info that you can't do direct assignment of promises to $scope anymore and expect it to work well for you: http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/
You can use promises to manage it, something like :
Clients.query().then(function (res) {
// Content loaded
console.log(res);
}, function (err) {
// Error
console.log(err);
});
Another way (much robust and 'best practice') is to make Angular intercepting your requests automatically by using interceptor (see doc here : http://docs.angularjs.org/api/ng.$http).
This can help too : Showing Spinner GIF during $http request in angular
As left in a comment by Pointy I solved my problem giving a second parameter to the get function as following:
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
}, function(){
// do my stuff here
});