How to use angular factory, $resource - javascript

I am building my first app with angular and currently I have my service defined as
angular.module('mean.testruns').factory('Testruns', ['$resource', function($resource) {
return $resource('testruns/:testrunId', {
testrunId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}]);
I have added another url on the rest server as
'/testcases/:testcaseId/testruns'
How do I include this in the above testruns factory function?
I currently have my controller as
$scope.findOfTestcase = function() {
//Need to correct this
Testruns.query({testcaseId:$stateParams.testcaseId}, function(testruns) {
$scope.testruns = testruns;
});
};
$scope.findOne = function() {
Testruns.get({
testrunId: $stateParams.testrunId
}, function(testrun) {
$scope.testrun = testrun;
});
};

Not totally sure about this, but you could try something like:
angular.module('mean.test').factory('Testruns', ['$resource', function($resource) {
return {
runs: $resource('testruns/:testrunId', ...),
cases: $resource('testcases/:testcaseId', ...)
}
}]);
And use it like this:
app.controller('ctrl', ['$scope', 'Testruns',
function($scope, Testruns) {
$scope.testCases = Testruns.cases.query();
}
])

Related

AngularJS Initialize provider data

I have code:
angular.module('admin', [])
.provider('users', function () {
this.users = 'default';
this.$get = function () {
var that = this;
return {
getUsers: function () {
return that.users;
}
}
};
})
.run(function (users, $http) {
users.users = $http('url'); // and others
})
.controller('test', function ($scope, users) {
$scope.users = users.getUsers();
});
I would like to intitalize data in .run() method (I can't use .config() method because it doesn't let to pass any services like $http). I found .run() method, but this code doesn't work... Data aren't saved in provider. Official documentation says:
"Execute this function after injector creation. Useful for application initialization."
I think it's best way to initialize data.
You may want to use an Angular Factory/Service for this kind of need. That is what I do. And pass that into the application. That service will be your singleton or source of truth about the dat.
angular.module('myData.services', [])
.factory('myData', ['$rootScope', '$http' , function($rootScope,$http) {
var factory = {
myData : {}
};
$http('/api/call', function(apiData) {
factory.myData = apiData;
});
return factory;
}]);
You could then use this in your controllers:
angular.module('myApp.controllers', [])
.controller('myCtrl', ['myData', '$scope', function(myData, $scope){
$scope.users = myData;
}]);
Check out the documentation on services: https://docs.angularjs.org/guide/services
Second attempt
angular.module('admin', [])
.factory('users', function ($http) {
var users = {};
var data = [];
$http.get('database.php')
.then(function (response) {
data = response.data;
});
users.getData = function () {
return data;
};
return users;
})
.controller('test', function ($scope, users) {
console.log(users.getData());
});
I would like to have data private. Empty Array returned, reponse comes with all data.
Provider configuration can be doable inside config block only, you can't do that inside run block
Though I don't find a reason to load users object while configuring app. I'd say that you should use either service/factory for this.
Code
angular.module('admin', [])
.service('users', function($http, $q) {
var users = [];
//make an get call to fetch users
function getUsers() {
return $http.get('database.php')
.then(function(response) {
data = response.data;
});
}
//will make a call if users aren't there
this.getData = function() {
// Handled below two conditions
// 1. If users aren't fetched the do an Ajax
// 2. If last ajax doesn't return a data then DO it again..
if (users.length > 0)
return $q.resolve(data); //do return data using dummy promise
return getUsers();
};
return users;
})
.controller('test', function($scope, users) {
users.getData().then(function(data){
console.log(data);
});
});

How to test a factory with $resource which returns a promise in AngularJS?

In my Angular application, I have a controller defined as follows:
angular.module('myApp.controllers')
.controller('AppController', function($rootScope, $scope, CheckResource) {
$scope.check = function(data) {
var promise = CheckResource.query(data).$promise;
promise.then(function(result) {
$scope.value = result;
}, function() {
$scope.value = "default";
});
};
};
And my CheckResource is a Factory as follows:
angular.module('myApp.services', ['ngResource'])
.factory('CheckResource', function($resource) {
return $resource('/check', {}, {
query: {
method: 'POST'
}
});
});
I would like to write a unit test for my controller AppController and somehow mock my factory CheckResource, but I'm not sure how exactly I can achieve this using $q and deferred promise. Can anyone please explain me what's going on this scenario?
What are my options for testing this scenario? Should I mock my service? Should I use spyOn? Should I use $httpBackend? Or, should I do an E2E test?
Assuming you have a controller mocked in your test, you can do something like:
var deferred;
beforeEach(inject(function($q) {
deferred = $q.defer();
myController.CheckResource = {
query: function() {
return {$promise: deferred.promise};
}
};
}));
Then in your test itself:
it('tests CheckResource', function() {
deferred.resolve([]);
expect(something).toHaveHappened();
});
ngResource is defined in the separate module. Please check the documentation http://docs.angularjs.org/api/ngResource In other words you have to include and add ngResource dependency to MyApp.Factory module - angular.module("MyApp.Factory", ["ngResource"])
var factory;
beforeEach(function () {
module("MyApp.Factory");
inject(function (_factory_) {
factory = _factory_;
});
});

Can I get Angular variable of one controller in other (variable by $http)

I am new to Angularjs and studied a lot. But I stuck at a point. Google doesn't help me. I have a controller and I have data in $scope.results
app.controller('manage_categories', function($scope, $http, $filter, $window) {
$scope.results = [];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
});
})
now i want to access the same in other without any other $http call. I have done with another call but i don't want this . because i need this in many other controllers.something like this
app.controller('manage_users', function($scope, $http, $filter, $window,results) {
$scope.results = results;
//~ $http({
//~ url: base_url + 'employee/fetchData?table=results',
//~ method: "POST",
//~ }).success(function(data) {
//~ $scope.results = data;
//~ });
})
or any other method. Thanks.
update
I tried this
var myApp = angular.module('myApp',[]);
myApp.factory('results', function() {
return {
name : [{id:21,name:'this is test'}]
};
});
app.controller('manage_users', function($scope, $http, $filter, $window,results) {
$scope.results = results;
})
This is working fine . But not working with $http call .
var myApp = angular.module('myApp',[]);
myApp.factory('results', function($scope,$http) {
$scope.results=[];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
});
return {
name : results
};
});
update 2
after answers i write it like
var canapp = angular.module('canApp', ["ngRoute", "angularFileUpload"]);
canapp.service('ResultsFactory', ['$http', function($http) {
// http call here
var url=base_url + 'employee/fetchData?table=results';
$http.post(url,data).success(function(data){
this.results = data;
});
}])
call like this
canapp.controller('get_candidates', function($scope, $http, $filter, $timeout, $window, ResultsFactory) {
$scope.check=ResultsFactory.results;
});
but it is not setting the value in template
Use $broadcast to share the data between controllers. Your code will look like this
app.controller('manage_categories', function($scope, $http, $filter, $window, $rootScope) {
$scope.results = [];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
$rootScope.$broadcast("results",data);
});
});
app.controller('otherCtrlr', function($scope, $rootScope) {
$rootScope.$on("results", function(event, data){
$scope.results = data;
});
});
But using a service call in the controller is not a best approach. Create a factory and create a method to call your service.
From controller you need to call this method. But to avoid two service calls, you definitely need to use broadcast/emit(depending on data transfer is from parent or child)
There are various possible way of communicating between two controllers. If you just Google share data between controllers angularjs, you may found various links:
Using Services to Share Data Between Controllers
Sharing Data Between Controllers
Share data between AngularJS controllers
Passing data between controllers in Angular JS?
So, in short, possible ways are:
Using Angular Factories (recommended)
Using $rootScope (not recommended)
Using top most controller's scope as root scope
You can do this:
app.factory('ResultsFactory', resultsFactory);
resultsFactory.$inject = ['$http'];
function resultsFactory = function(){
var self = {};
var results = null;
self.getResults = function(){
if(!results){
$http.post(url,data).success(function(data){
results = data;
});
}else{
return results;
}
}
return self;
}
Only the first time that you call to ResultsFactory.getResults() this executes the $http call.
Here's a small fiddle explaining how to share data between controllers.
https://jsfiddle.net/frishi/zxnLwz6d/10/
(Check the browser console to see that both controllers can access data via the service.)
Basically the premise of a service is that it is a singleton that can be used by all the controllers registered on your module.
You want to make that $http call in a service:
.service('myService', ['$http', function($http) {
this.getData = function(){
// Simple GET request example:
return $http({
method: 'GET',
url: 'https://api.github.com/users/mralexgray/repos' // example API
}).then(function successCallback(response) {
return response;
}, function errorCallback(response) {
// return error message
});
}
}])
In your controller:
.controller('Controller2',['$scope','myService',function ($scope,myService) {
$scope.foo = myService.getData();
//resolve the promise:
$scope.foo.then(function(data){
console.log(data);
})
}
])
It is strongly recommended to use separated services as frishi pointed out. This sample is in single file and module just to make it readeable. Following implementation stores the promise and actual request is only made on the initial call to getFoo. The rest will get the response from the in memory promise.
'use strict';
angular.module('foo', [])
.factory('FooResource', function SessionResource($http) {
var fooPromise;
return {
getFoo: function getFoo() {
if(!fooPromise) {
fooPromise = $http.post('employee/fetchData?table=results');
}
return fooPromise;
}
};
})
.controller('FooController', function($scope, FooResource) {
FooResource.getFoo().then(function getFooSuccess(data) {
$scope.results = data;
});
});
I use this angular code with ionic framework
may be its help you..
my factory is..
angular.module('starter.services', [])
.factory('Chats', function() {
// Might use a resource here that returns a JSON array
// Some fake testing data
var chats = [{
id: 0,
name: 'Ben Sparrow',
lastText: 'You on your way?',
face: 'img/ben.png'
}, {
id: 1,
name: 'Max Lynx',
lastText: 'Hey, it\'s me',
face: 'img/max.png'
}, {
id: 2,
name: 'Adam Bradleyson',
lastText: 'I should buy a boat',
face: 'img/adam.jpg'
}, {
id: 3,
name: 'Perry Governor',
lastText: 'Look at my mukluks!',
face: 'img/perry.png'
}, {
id: 4,
name: 'Mike Harrington',
lastText: 'This is wicked good ice cream.',
face: 'img/mike.png'
}];
return {
all: function() {
return chats;
},
remove: function(chat) {
chats.splice(chats.indexOf(chat), 1);
},
get: function(chatId) {
for (var i = 0; i < chats.length; i++) {
if (chats[i].id === parseInt(chatId)) {
return chats[i];
}
}
return null;
}
};
});
and i use this factory in many controllers
.controller('ChatsCtrl', function($scope, Chats) {
$scope.chats = Chats.all();
$scope.remove = function(chat) {
Chats.remove(chat);
};
})
.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
$scope.chat = Chats.get($stateParams.chatId);
})
in this code factory hit http:// request only one time and i use response on two controllers.
Hope its help you.

Architecture of single method for ajax in Angularjs

I'd like to understand how can i design single ajax-method for several controllers, which also can influence on user interface ('loading' animation, for example).
Idea is (without promises):
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl',
function myCtrl($scope, myFactory){
$scope.loading = false;
$scope.someStuff = myFactory.getStuff(params);
});
myApp.factory('myFactory', function(myService){
return{
getStuff: function(params){
return myService.ajax(params);
}
}
});
myApp.service('myService', function($http) {
this.ajax = function(params){
// switch $scope.loading = true;
// make request
// return $http result
// switch $scope.loading = false;
};
});
As i know, i need use $scope for UI changes and ajax-method should be taken out to custom service. Services in Angularjs does not work with $scope and i have no idea how can i solve this problem.
I think, there must be a service with chain of promises.
How can it be designed?
Upd: I hope, with the time the documentation will be more complete and clear. But community of angular users is already great. Thanks.
In my project I have defined a service called appState which has (among other) methods: showGlobalSpinner and hideGlobalSpinner which modify a variable on the $rootScope.
Basically:
(…)
.factory('appState', ['$rootScope', function ($rootScope) {
return {
showGlobalSpinner: function () {
++$rootScope.loadingInProgress;
},
hideGlobalSpinner: function () {
if ($rootScope.loadingInProgress > 0) {
--$rootScope.loadingInProgress
}
}
};
}]);
What I do next is I show spinners wherever I need them using ng-show directive:
<div class="spinner" ng-show="loadingInProgress"></div>
Before each AJAX I just call AppState.showGlobalSpinner() and after success/error I call AppState.hideGlobalSpinner()
You could add this method to any of your controllers:
.controller('HeaderCtrl',['$scope','httpRequestTracker', function($scope,httpRequestTracker) {
$scope.hasPendingRequests = function () {
return httpRequestTracker.hasPendingRequests();
};
}]);
angular.module('services.httpRequestTracker', []);
angular.module('services.httpRequestTracker').factory('httpRequestTracker', ['$http', function($http){
var httpRequestTracker = {};
httpRequestTracker.hasPendingRequests = function() {
return $http.pendingRequests.length > 0;
};
return httpRequestTracker;
}]);
You can try a more generic approach by using an HTTP interceptor, some events and a directive:
Javascript
app.factory('myHttpInterceptor', function($q, $rootScope) {
return function(promise) {
$rootScope.$broadcast('RequestStarted');
var success = function(response) {
$rootScope.$broadcast('RequestFinished', true);
};
var error = function(response) {
$rootScope.$broadcast('RequestFinished', false);
return $q.reject(response);
};
promise.then(success, error);
return promise;
}
})
.config(function($httpProvider) {
$httpProvider.responseInterceptors.push('myHttpInterceptor');
})
.directive('loading', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-show="visible" ng-transclude></div>',
controller: function($scope) {
$scope.visible = false;
$scope.$on('RequestStarted', function() {
$scope.visible = true;
});
$scope.$on('RequestFinished', function() {
$scope.visible = false;
});
}
};
});
HTML
...
<loading><h1>Loading...</h1></loading>
...
You can see a working demo here.
By using an HTTP interceptor, you'll be able to track every HTTP request made by the $http service ($resource included) across you Angular application and show the load animation accordingly.

How can I pass params with AngularJS $resource.query?

At the moment, the url localhost/view/titles will use the route, controller and service below, and the server will return a list of all title objects. How do I extend the service to allow for additional query params, such as a result limit etc?
// main app module with route
var app = angular.module('app', ['ngResource']).
config(function ($routeProvider, $locationProvider) {
$routeProvider.when(
'/view/:obj/:limit',
{
templateUrl: '/static/templates/titles.html',
controller: 'titlesController'
}
)})
// list service
var listService = app.factory('listService', function ($q, $resource) {
return {
getList: function (obj) {
var deferred = $q.defer();
$resource('/api/view/' + obj).query(
function (response) {
console.log('good')
deferred.resolve(response);
}
,
function (response) {
console.log('bad ' + response.status)
deferred.reject(response);
}
)
return deferred.promise;
}
}
}
)
// controller
var titlesController = bang.controller('titlesController', function($scope, listService, $routeParams){
$scope.titles = listService.getList($routeParams.obj);
})
Below is the sample code:
angular.module('phonecatServices', ['ngResource']).
factory('Phone', function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
});
This is a broader answer to the question of how to pass params to your backend api with a query string using the ngResource module since I couldn't find straight forward instructions anywhere else.
ngResource Setup:
Install the ngResource package from the command line with bower or npm bower install angular-resource.
In the head element of the index.html page add the angular-resource script
<script src="lib/angular-resource/angular-resource.min.js"></script>
js/app.js: Add the dependencies. I'm leaving out the routes since I use ui-router which is a separate topic.
angular.module('app', ['app.controllers', 'app.services', 'ngResource'])
The view: templates/list.html
<input type="search" ng-model="filters.title" placeholder="Search">
<button ng-click="searchList(filters)">Submit</button>
<div ng-repeat="item in list">
<p>{{item.title}} - {{item.description}}</p>
</div>
The controller: js/controllers.js
.controller('ListCtrl', function($scope, ListService) {
$scope.searchList = function(filters){
$scope.filters = { title: '' }; //This will clear the search box.
$scope.list = ListService.query({title: filters.title});
}
})
The factory: js/services.js. Assumes you also will be doing get requests by the item id. If not leave out /:id, {id: '#id'}
.factory('ListService', function($resource) {
return $resource('http://localhost:3000/api/view/:id', { id: '#id' });
})

Categories

Resources