I want to chain 2 resource calls together but keep it invisible from the consuming application/controller.
For example: resource "Person" has multiple "Role" records, however I want to keep "Person" and "Role" as separate resources on the server.
So the application calls the "Person" resource for a list of people but before they are returned the resource's transformReponse method calls the "Roles" resource for each person and adds the roles as an array. Therefore the application simply asks for people and gets all people with associated roles.
I've tried to simplify the scenario in the example code listed below. Here the application calls 1 resource which then calls the 2nd, however the data from the 2nd resource is resolved after the call to the initial resource in the controller returns.
Any ideas how to do this would be much appreciated.
angular.module('services', ['ngResource'])
.factory("someService2", function ($resource) {
return $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
//MESS WITH THE DATA
data = {};
data.coolThing = 'BOOM-SHAKA-LAKA-V2';
return data;
}
}
}
);
});
angular.module('services')
.factory("someService", function ($q, $resource, someService2) {
return $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
data.title1 = "Resource1";
var defer = $q.defer();
// Call 2nd resource
someService2.get(function(d){
data.title2 = d.coolThing;
defer.resolve(data);
});
return defer.promise;
}
}
}
);
});
var app = angular.module('myApp', ['ngResource', 'services']);
app.controller('MainController', ['$scope', 'someService', function ($scope, svc) {
$scope.title1 = 'Transform Test';
$scope.title2 = 'Transform Test2';
var promise = svc.get().$promise.then(function(data){
$scope.title1 = data.title1;
$scope.title2 = data.title2;
});
}]);
The HTML is very simple:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0-rc.2/angular-resource.js" ></script>
<script type="text/javascript" src="JScript.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MainController">
<h1>{{title1}}</h1>
<h1>{{title2}}</h1>
</div>
<div>
</body>
</html>
I have finally come to the conclusion that using the TransformResponse function of the Resource will not work as required as it looks like the resource does some external wrapping of the data further up the promise pipeline. ie. the TransformResponse happens before the promise is resolved internally.
My final solution was to simply abstract the resource call in my own object and chain my promises here. This still allows me to abstract the nested calls from the controller which was my main requirement.
Service2:
angular.module('services', ['ngResource'])
.factory("someService2", function ($resource) {
return $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
//MESS WITH THE DATA
data = {};
data.coolThing = 'BOOM-SHAKA-LAKA-V2';
return data;
}
}
}
);
});
Service1, which encapsulates the nested calls:
angular.module('services')
.factory("someService", function ($q, $resource, someService2) {
var r = $resource(
'/', {}, {
get: {
method: 'GET',
transformResponse: function(data, headers){
//MESS WITH THE DATA
data = {};
data.title1 = 'BOOM-SHAKA-LAKA-V1';
return data;
}
}
}
);
var svc = {
get: function(){
var data;
var defer = $q.defer();
r.get().$promise.then(
function(x){
data = x;
someService2.get().$promise.then(
function(y){
data.title2 = y.coolThing;
defer.resolve(data);
}
);
}
);
return defer.promise;
}
};
return svc;
});
Controller:
var app = angular.module('myApp', ['ngResource', 'services']);
app.controller('MainController', ['$scope', 'someService', function ($scope, svc) {
$scope.title1 = 'Transform Test';
$scope.title2 = 'Transform Test2';
svc.get().then(function(data){
$scope.title1 = data.title1;
$scope.title2 = data.title2;
});
}]);
HTML:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0-rc.2/angular-resource.js" ></script>
<script type="text/javascript" src="http://localhost/Angular/NestedResources/JS.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MainController">
<h1>{{title1}}</h1>
<h1>{{title2}}</h1>
<pre>
{{data}}
</pre>
</div>
<div>
</body>
</html>
Related
I am trying to create an AngularJS interceptor using roughly the recipe at this link - https://thinkster.io/interceptors
My code is below -
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<p>Today's welcome message is:</p>
<h1>{{myWelcome}}</h1>
</div>
<p>The $http service requests a page on the server, and the response is set as the value of the "myWelcome" variable.</p>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http.get("welcome.htm")
.then(function(response) {
$scope.myWelcome = response.data;
});
});
function tokenInterceptor() {
return {
request: function(config) {
config.xsrfHeaderName = 'myToken';
config.headers['myToken'] = "HelloWorld";
return config;
},
requestError: function(config) {
return config;
},
response: function(config) {
return config;
},
responseError: function(config) {
return config;
}
}
}
app
.factory('tokenInterceptor', tokenInterceptor)
.config(function($httpProvider) {
$httpProvider.interceptors.push('tokenInterceptor');
}).run(function($http) {
$http.get('http://127.0.0.1:8082/customURL.htm')
.then(function(res) {
console.log("get request to google");
});
});
</script>
GOOGLE
</body>
</html>
The code inside tokenInterceptor function runs only when the $http.get('http://127.0.0.1:8082/customURL.htm') call inside factory.config.run gets executed. How do I make the code run when any HTTP request is made? - for example when the link to google on this page is executed -
GOOGLE
This is very rough example but you can understand main idea:
//app.js
angular.module('testApp', [])
.config(function($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
})
.factory('myInterceptor', function($q) {
var myInterceptor = {
request: (config)=>{ console.log('myInterceptor request'); return config;},
response: (response)=>{ console.log('myInterceptor response'); return response;},
requestError: (rejectReason)=>{ console.log('myInterceptor requestError'); return $q.reject(rejectReason);},
responseError: (response)=>{ console.log('myInterceptor responseError'); return $q.reject(response);}
};
return myInterceptor;
})
.controller('mainController', function($scope, $http){
$scope.funcGet = ()=>{
$http.get('http://www.google.com')
.then(function (response) {
console.log('resonce - ', responce);
});
}
})
//index.html
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js">
</script>
<script src="app.js"></script>
<body>
<div ng-app="testApp" ng-controller="mainController">
<input type="button" value="Go" ng-click="funcGet()">
</div>
</body>
</html>
I am implementing logic through ui-router, Factory and Directive but getting error: JavaScript runtime error: [$injector:modulerr] in Angular Js.
Ui-Routing was working fine.
Index.html file:
<html><head><title>Employee Management System</title>
<link href="Content/bootstrap.css" rel="stylesheet" />
<script src="Scripts/jquery-1.9.1.min.js"></script>
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/angular-ui-router.min.js"></script>
<script src="Scripts/bootstrap.min.js"></script>
<script src="Scripts/app/EmpRecord.js"></script>
<script src="Scripts/app/GetDataService.js"></script>
<script src="Scripts/app/EmpController.js"></script>
<script src="Scripts/app/EmpApp.js"></script></head>
<body ng-app="EmpApp">
<div class="page-header">Employee Management System
</div><div ng-include="'pageContents/menu.html'"></div>
<ui-view></ui-view></body></html>
EmpApp.js
var app = angular.module("EmpApp", ['ui.router']);
app.factory('EmpFact', ['$http', EmpFact])
.controller('EmpController', ['$scope', 'EmpFact',EmpController])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
templateUrl: '/home.html'
})
.state('Add', {
templateUrl: '/AddEmployee.html'
})
.state('List', {
templateUrl: 'ListEmp.html',
controller: 'EmpController'
}
)
})
.directive('emp-Record', EmpRecord);
ListEmp.html:
<div><div><h3>List of Employees</h3></div>
<div EmpRecord ng-repeat="e in Employees"></div></div>
EmpController
<div><div><h3>List of Employees</h3></div>
<div EmpRecord ng-repeat="e in Employees"></div></div>
GetDataService.js
var EmpFact = function ($http) {
var records = {}
$http.get('http://localhost/EmployeeApi/api/Emp')
.then(function (response) {
records= response.data;
});
return {
GetData: function () {
alert(records);
return records;
}
}
}
All Errors are gone Now but data is not coming.
In short:
Controller:
var EmpController= function ($scope,EmpFact) {
$scope.Employees = EmpFact.GetData();
console.log($scope.Employees);
};
Service:
var EmpFact = function ($http) {
var records = {}
$http.get('http://localhost/EmployeeApi/api/Emp')
.then(function (response) {
records= response.data;
});
return {
GetData: function () {
alert(records);
return records;
}}}
Àpp.js
app.factory('EmpFact', ['$http', EmpFact])
.controller('EmpController', ['$scope','EmpFact', EmpController])
.directive('empRecord', function () {
return {
template: "<tr><td>{{e.empid}}</td><td>{{e.empName}}</td><td>{{e.empEmail}}</td><td>{{e.empSalary}}</td>"
}});
HTML:
<div>
<div><h3>List of Employees</h3></div>
<div emp-Record ng-repeat="e in Employees"></div>
</div>
Ok, so as I suggested in the comment, because the error implies that you haven't injected the EmpFact factory into EmpController, changing
.controller('EmpController', ['$scope', EmpController])
Into:
.controller('EmpController', ['$scope', 'EmpFact', EmpController])
And also injecting it to the controller function:
var EmpController = function ($scope, EmpFact) { ... };
Made the error disappeared, but now you say that "data is not coming".
I suggest another change in your factory, instead of your current code, try this:
var EmpFact = function ($http) {
return {
GetData: function () {
// return a promise which resolve with the actual data returned from the server
return $http.get('http://localhost/EmployeeApi/api/Emp').then(
function (response) {
// return the actual results, instead of the whole response from the server
return response.data;
}
);
}
}
};
Now, in your controller, you should be able to get the data like this:
var EmpController = function ($scope, EmpFact) {
// Call the "GetData" from the factory, which return a promise with the actual results returned from the server
EmpFact.GetData().then(
function(data) {
// in the resolve callback function, save the results data in
// any $scope property (I used "$scope.Employees" so it will be
// available in the view via {{ Employees | json }})
$scope.Employees = data;
}
);
};
By returning a promise you are guaranteed to be able to handle the results returned from an asynchronous request (AJAX). You should be able to use the results in the view like this:
<div emp-Record ng-repeat="e in Employees"></div>
(Note that the above HTML snippet is taken from the comments below this answer)
Edit:
Looking at your directive, it doesn't look like a correct way to construct a table. Change emp-Record to emp-record and wrap it in a <table> tag to make it a valid HTML:
<table>
<tr emp-record ng-repeat="e in Employees"></tr>
</table>
And in your directive's template make sure you close the row tag (Add </tr>):
.directive('empRecord', function () {
return {
template: "<tr><td>{{e.empid}}</td><td>{{e.empName}}</td><td>{{e.empEmail}}</td><td>{{e.empSalary}}</td></tr>"
}
});
Thanks Alon for your help as I am new to Angular, converting my ASP.NET MVC code to HTML5/Angular only.
Finally I am able to resolve it.
Data Service/Factory:
var EmpFact = function ($http) {
return {
GetData: function () {
return $http.get('http://localhost/EmployeeApi/api/Emp');
}
}
}
Controller:
var EmpController = function ($scope, EmpFact) {
//EmpFact.GetData() is a promise.
EmpFact.GetData().then(
function (result) {
$scope.Employees= result.data;
}
);
}
I wanna use multiple ( in this case, 2 ) $http.gets in my service !
As you know the simple form of using $http.get is this :
app.factory('MyService', function ($http, $q) {
return {
getData: function() {
return $http.get('myfile.json')
.then(function(response) {
return response.data;
});
}
};
});
Now I wanna use 2 files ( 2 $http.gets ) and compare them to each other ( with some for loops and etc that I can ... ) !
What can I do now ? :(
use $q.all.
Add $q to controller's dependencies, exemple
$scope.req1 = $http.get('myfile.json');
$scope.req2 = $http.get('myfile2.json');
$q.all([$scope.req1, $scope.req2]).then(function(data) {
// data is array of your files
if ( JSON.stringify(data[0]) === JSON.stringify(data[1])){
console.log('is equal');
}
});
It is an extension of Hajji Tarik's solution. I was able to derive from your comments that you were still not clear with what to code in where. So I have developed a sample application which will assist you for the same.
//--app.module.js--//
angular.module('notesApp', []);
//--app.service.js--//
angular.module('notesApp')
.factory('notesFactory', ['$http',
function($http) {
var notesService = {};
notesService.getData = function(url, method) {
return $http({
url: url,
method: method
});
}
return notesService;
}
]);
//--app.controller.js--//
angular.module('notesApp')
.controller('MainController', ['$scope', '$http', '$log', '$q', 'notesFactory',
function($scope, $http, $log, $q, notesFactory) {
$scope.data = {};
var data1 = notesFactory.getData('http://localhost:3000/api/notes/1', 'GET');
var data2 = notesFactory.getData('http://localhost:3000/api/notes/2', 'GET');
var combinedData = $q.all({
firstResponse: data1,
secondResponse: data2
});
combinedData.then(function(response) {
$log.log(response.firstResponse.data);
$log.log(response.secondResponse.data);
//Write your comparison code here for comparing json results.
}, function(error) {
$scope.data = error;
});
}
]);
<html ng-app='notesApp'>
<head>
<title>
Notes Application
</title>
</head>
<body>
<div ng-controller='MainController'>
</div>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js'></script>
<script src='app.module.js'></script>
<script src='app.controller.js'></script>
<script src='app.service.js'></script>
</body>
</html>
I have created a simple app to store an event to my database. It is working fine but with a wordaround.
I use the following method:
controllers.controller('CalendarAddController', function($scope, $routeParams, Event) {
$scope.addEvent = function() {
$scope.ev=new Event();
$scope.ev.title = $scope.event.title;
$scope.ev.$save().then(function(res) { console.log("success");})
.catch(function(req) { console.log("error saving obj"); })
.finally(function() { console.log("always called") });
}
});
After submitting my form, the function addEvent is called.
If I print the value of $scope.event, it has the right values.
However, when I call .$save() on it, I get the error "TypeError: $scope.event.$save is not a function."
But when I create a new object and assign the values to it, it works fine.
Why isn't it working directly? Always creating a dummy object does not seem to be best practice I suppose.
The service I created
services.factory( 'Resource', [ '$resource', function( $resource ) {
return function( url, params, methods ) {
var defaults = {
update: { method: 'put', isArray: false },
create: { method: 'post' }
};
methods = angular.extend( defaults, methods );
var resource = $resource( url, params, methods );
resource.prototype.$save = function() {
if ( !this.id ) {
return this.$create();
}
else {
return this.$update();
}
};
return resource;
};
}]);
services.factory( 'Event', [ 'Resource', function( $resource ) {
return $resource( '/api/calendar/:id' );
}]);
Update: basic example
Using this example everything works front-end but in my rest API the data is not passed.
<!DOCTYPE html>
<html lang="en-US">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-route.min.js"></script>
<script type="text/javascript">
var app = angular.module('app', ['ngRoute','ngResource'])
.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'pages/home.html',
controller : 'mainController'
})
});
app.factory('Event', function($resource) {
return $resource('/test/api/:id');
});
app.controller('mainController', ['$scope', '$routeParams', 'Event',
function($scope, $routeParams, Event) {
function handleSuccess(data) {
alert("success");
}
function handleError(data) {
alert("error");
}
var event = new Event();
event.title = "a title";
Event.save(event, handleSuccess, handleError);
}]);
</script>
<body>
<div ng-app="app">
<div ng-view></div>
</div>
</body>
</html>
For the back-end I created a very basic script which writes the request to a file:
It always says "array()" so no data is passed.
<?php
$myFile = "testFile.txt";
$fh = fopen($myFile, 'w') or die("can't open file");
$stringData = print_r($_REQUEST,true);
fwrite($fh, $stringData);
fclose($fh);
?>
Where do you initialize $scope.event or is it automatically created by angular? If this is an plain JavaScript object you can't call .$save, but you can use the Event service itself - no need to create a new instance:
Event.create($scope.event)
I would like to ask/discuss wether this is good or bad practise - what are the pros and cons of making a service call insde a controller as clean and short as possible. In other words: not having a callback anymore but make use of the Angular binding principles of Angular.
Take a look at a Plnkr I forked:
http://plnkr.co/edit/zNwy8tNKG6DxAzBAclKY
I would like to achieve what is commented out on line 42 of the Plnkr > $scope.data = DataService.getData(3);.
app.factory('DataService', function($q, $http) {
var cache = {};
var service= {
data:{src:''},
getData: function(id, callback) {
var deffered = $q.defer();
if (cache[id]) {
service.data.src = 'cache';
deffered.resolve(cache[id])
} else {
$http.get('data.json').then(function(res) {
service.data.src = 'ajax';
cache[id] = res.data;
cache[id].dataSource = service.data.src;
deffered.resolve(cache[id])
})
}
return deffered.promise.then(callback);
}
}
return service
})
app.controller('MainCtrl', function($scope, DataService) {
DataService.getData(3, function(result) {
$scope.data = result;
});
//$scope.data = DataService.getData(3);
});
My best practice with regards to services requesting data and returning promises is:
return a promise (in DataService, return deferred.promise)
in the controller, call DataService.getData(3).then(, )
So I would not pass a callback to a service function that uses a promise.
The more difficult question is what should the service function do, and what should the then(function(data) {...}) do. Here are a few guidelines:
Anything that is shared (data / repetitive functionality), implement in the service
Anything related to binding data / functions to UI elements, implement in the controller
Keep your controllers as simple as possible - they should just link between UI elements and the model.
Any model logic, processing, format parsing, etc - implement in the service
I added this part after reading the comments:
If you need to do some processing (like checking a cached result, parsing the result, etc.), then the proper place to do this is in the service.
So I would modify your code as follows:
var service= {
data:{src:''},
getData: function(id) {
var deffered = $q.defer();
if (cache[id]) {
service.data.src = 'cache';
deffered.resolve(cache[id]);
return deferred;
} else {
return $http.get('data.json').then(function(res) {
service.data.src = 'ajax';
cache[id] = res.data;
cache[id].dataSource = service.data.src;
return cache[id]; // this will resolve to the next ".then(..)"
});
}
}
}
AfaIk this is not possible - see this Thread
You can 'automatically' resolve the promise by using angular-route. The resolved promises will be injected into your controller.
You can do like this plunker
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link href="style.css" rel="stylesheet" />
<script data-semver="1.2.4" src="http://code.angularjs.org/1.2.4/angular.js" data-require="angular.js#1.2.x"></script>
<script src="app.js"></script>
<script>
app.factory('DataService', function($q, $http) {
var cache = {};
var service= {
data:{src:''},
getData: function(id, callback) {
var deffered = $q.defer();
if (cache[id]) {
service.data.src = 'cache';
deffered.resolve(cache[id])
} else {
$http.get('data.json').then(function(res) {
service.data.src = 'ajax';
cache[id] = res.data;
cache[id].dataSource = service.data.src;
deffered.resolve(cache[id])
})
}
return deffered.promise;
}
}
return service
})
app.controller('MainCtrl', function($scope, DataService) {
DataService.getData(3).then(function (data) {
$scope.data = data;
});
});
</script>
</head>
<body ng-controller="MainCtrl">
<div>Source: {{data.dataSource}}</div>
<pre>{{data}}</pre>
</body>
</html>