I have an AngularJS controller that makes an HTTP get request to an API. The API can either return a res.json(true) or a res.json(false) value based on some condition.
However, the controller seems to be ignoring this and I'm just curious to see how someone else would implement this:
function MyCtrl($scope, $http) {
$http.get('/api/call').
success(function(data, status, headers, config) {
console.log(data); // --> this prints out false
if (data) { // --> this evaluates and I'd expect this to fail, however
console.log("true"); --> this also shown
}
});
Should I modify my response from the api that it returns something else other than res.json(false)?
Should I modify the code above to say something like if (data === false)?
res.json(true) // not JSON
res.json({ status: true }) // JSON
Related
Hii I m using following code. I am reading a json file name is "users.json". If i read this file in controller through $http everything works fine. but i want to use the data that i read from file, again and again in different controller so i made a factory for this. but in factory when i read data from that json file through $http.get() and in return when i call that service method in my controller and it returns Object { $$state: Object }
app.factory('AboutFactory',['$http',function ($http) {
return {
getter: function () {
return $http({
method : 'GET',
url : '/home/penguin/Modeles/users.json',
cache : true
})
.then(function (response) {
return response.data
})
}
}
}])
Result of getter function is a promise. so you should use it like this:
AboutFactory.getter().then(function(res)
{
console.log(res);
});
That's because the $http service returns a promise as mentioned in the documentation:
The $http API is based on the deferred/promise APIs exposed by the $q
service. While for simple usage patterns this doesn't matter much, for
advanced usage it is important to familiarize yourself with these APIs
and the guarantees they provide.
You can think of a promise as if you give a top secret message to someone to deliver personally to a friend, then when delivered, report back to you with a message back from your friend.
You provide the message (the request object) to the person so that they can attempt to make the delivery of the message (send the request).
The attempted delivery has taken place (the request has been sent), it either:
a) was delivered successfully (successful response) or
b) your friend was not in so the letter could not be delivered (non success response).
You can then act depending on the response you get back
a) Message was delivered (it was a successful request) and you got a letter back (do something with the response) or
b) Message failed to get delivered (request wasn't successful), so you can maybe try again later or do something else as you don't have the information you requested
Here is an example of using the $http service with the $q service:
// app.js
(function() {
'use strict';
angular.module('app', []);
})();
// main.controller.js
(function() {
'use strict';
angular.module('app').controller('MainController', MainController);
MainController.$inject = ['AboutFactory'];
function MainController(AboutFactory) {
var vm = this;
AboutFactory.getter().then(function(data) {
// do something with your data
vm.data = data;
}, function(error) {
// give the user feedback on the error
});
}
})();
// about.service.js
(function() {
'use strict';
angular.module('app').factory('AboutFactory', AboutFactory);
AboutFactory.$inject = ['$http', '$q']
function AboutFactory($http, $q) {
var service = {
getter: getter
};
return service;
function getter() {
// perform some asynchronous operation, resolve or reject the promise when appropriate.
return $q(function(resolve, reject) {
$http({
method: 'GET',
url: 'https://httpbin.org/get',
cache: true
}).then(function(response) {
// successful status code
// resolve the data from the response
return resolve(response.data);
}, function(error) {
// error
// handle the error somehow
// reject with the error
return reject(error);
});
});
}
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController as MainCtrl">
<pre>{{MainCtrl.data | json}}</pre>
</div>
Try this approach. It will work as per your expectation.
Read JSON file in controller through $http service as it is works fine.
For sharing the response data from one controller to another you can create a service and store the data into that service.
Service :
app.service('setGetData', function() {
var data = '';
getData: function() { return data; },
setData: function(requestData) { data = requestData; }
});
Controllers :
app.controller('myCtrl1', ['setGetData',function(setGetData) {
// To set the data from the one controller
var data = 'Hello World !!';
setGetData.setData(data);
}]);
app.controller('myCtrl2', ['setGetData',function(setGetData) {
// To get the data from the another controller
var res = setGetData.getData();
console.log(res); // Hello World !!
}]);
Here, we can see that myCtrl1 is used for setting the data and myCtrl2 is used for getting the data. So, we can share the data from one controller to another controller like this.
I am using AngularJs 1.5 RC build.
I have a view wherein I am using ng-repeat to iterate over a collection , like so
<tr ng-repeat="user in users">
<td>{{user.JobID}}</td>
<td>{{getManager(user.userID)}}</td>
<td>{{user.StatusDesc}}</td>
<td>{{user.StartedAt}}</td>
</tr>
The idea here is to use the getManager function to get the name of the manager for each and every user in the users collection.
As an aside , I have to use this approach since the API is not returning me all the information.
This is how the getManager function looks like now.
$scope.getManager = function($id) {
return "John Doe";
}
The entire controller looks as follows
var app = angular.module('userapp', ['ui.bootstrap']);
app.controller('MainController', ['$scope','$http', function($scope, $http) {
$scope.getUsers = function() {
$http.get("http://localhost/getUsers").
success(function(data, status, headers, config) {
$scope.users = data.resource;
}).
error(function(data, status, headers, config) {
// log error
console.error("An error as encountered. Error follows:");
console.error(data);
});
}
$scope.getManager= function($id) {
return "John Doe";
}
}]);
So in my page view, I am getting "John Doe" as a manager for all my users.
Problem
The problem begins whenever I try to get the real manager for a user. So if i replace my dummy getManager with the following function
$scope.getManager = function($id) {
$http.get("http://localhost/user/manager/"+$id).
success(function(data, status, headers, config) {
return (data.resource[0].ManagerName);
}).
error(function(data, status, headers, config) {
// log error
console.error("An error as encountered. Error follows:");
console.error(data);
});
}
AngularJs starts complaining and fails with the following
https://docs.angularjs.org/error/$rootScope/infdig?p0=10&p1=%5B%5D
Can you please let me know what might be happening here.
Please note , I am an Angular noob, hence your patience will be well appreciated.
Thanks
You should call ajax in that way inside {{}} interpolation. It will get called on each digest cycle and will throw $rootScope/infdig error.
So I'd suggest you to call the getManager method as soon as you retrieve a data from server. Then after getting data from a server you need to call getManager method just by passing UserId(look I change getManager implementation to return managerName by returning data). After getting managerName you need to bind that manager name to user object & use {{user.ManagerName}} on the HTML.
Markup
<tr ng-repeat="user in users">
<td>{{user.JobID}}</td>
<td>{{user.managerName}}</td>
<td>{{user.StatusDesc}}</td>
<td>{{user.StartedAt}}</td>
</tr>
Code
$scope.getUsers = function() {
$http.get("http://localhost/getUsers")
.success(function(data, status, headers, config) {
$scope.users = data.resource;
angular.forEach($scope.users, function(user){
(function(u){
$scope.getManager(u.UserID).then(function(name){
u.ManagerName = data;
})
})(user);
})
})
};
$scope.getManager = function($id) {
return $http.get("http://localhost/user/manager/"+$id).
then(function(response) {
var data = response.data;
return (data.resource[0].ManagerName);
},function(error) {
console.error("An error as encountered. Error follows:");
});
};
Side Note
Don't use .success & .error function on $http calls as they are
deprecated.
This is a really bad practice. If you have N users, you will send N queries to fetch every data. You should return this data in http://localhost/getUsers response.
You can try:
$scope.getManager = function($id) {
return $http.get("http://localhost/user/manager/"+$id)
...
}
My $http functions can return the following errors:
POST http://foobar.dev/foobar 500 (Internal Server Error)
POST http://foobar.dev/foobar 401 (Unauthorized)
Isn't there a way I can catch all status codes?
$http.post('/foobar', form)
.success(function(data, status, headers, config) {
console.info(data);
})
.error(function(data, status, headers, config) {
console.error(data);
if(status === 401) {
$scope.setTemplate('show-login');
}
if(status === 500) {
$scope.setTemplate('server-error');
}
}
);
Where $scope.setTemplate() is a function inside the Controller that sets a view.
But then I have to do this for each error() function and there are a lot functions like this which also not making it DRY code :P
What I want is to catch the error and do an action based on the status code returned in the error.
FYI: I'm not using Angulars $routeProvider().
You can use the Angular $http interceptor for this like #Dalorzo explained:
var myApp = angular.module("myApp", [], ['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$rootScope', '$q', function($rootScope, $q) {
return {
'responseError': function(response) {
var status = response.status;
// Skip for response with code 422 (as asked in the comment)
if (status != 422) {
var routes = {'401': 'show-login', '500': 'server-error'};
$rootScope.$broadcast("ajaxError", {template: routes[status]});
}
return $q.reject(response);
}
};
}]);
});
Then receive it in your controller:
$scope.$on("ajaxError", function(e, data) {
$scope.setTemplate(data.template);
});
Now, you don't have to put in your each error function.
How about something like this instead:
var routes = {'401':'show-login', '500': 'server-error'};
$scope.setTemplate(routes[status]);
Where routes is a dictionary with your error codes and desired routing.
This is exactly what $http interceptors are for. See the interceptors section here: $http
Basically, you create common functionality for all $http requests, in which you can handle different statuses. For example:
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2){
return {
response: function(response){
// do something for particular error codes
if(response.status === 500){
// do what you want here
}
return response;
}
};
});
// add the interceptor to the stack
$httpProvider.interceptors.push('myHttpInterceptor');
What I would say initially is to create a decorator for the $http service or create a service that would serve as a wrapper for the $http service.
In my app (generated with yeoman) i have such structure:
view:
index.html with ng-view for rendering controller view
controller:
every controller try to get some data with api urlm like:
$scope.getArticles = function() {
$http.get(settings.apiBaseUri + '/app/articles/', {
headers: {
'Content-Type': 'application/json',
'Pragma': 'no-cache',
'Cache-Control': 'no-cache',
'If-Modified-Since': ''
}
})
.success(function(response) {
/* do some magic here :) */
});
};
$scope.getArticles();
and so in every controller (+ many other queries)
and now i'm a little bit confused: how to do not render anything, until my first request get 200 but not 401?
here is my interceptor:
var deleteAuth = function(){
$location.path('/signin');
delete $localStorage.authStatus;
};
var responseError = function(rejection) {
...
if (rejection.status === 401 || rejection.status === 403) {
authStatus = $localStorage.authStatus;
if (authStatus && authStatus.isAuth && authStatus.authToken && !ipCookie('authToken') && !renewTokenAttempt) {
deleteAuth();
}
}
...
return $q.reject(rejection);
};
and all works almost 'as must be', but...
every time, i get my index.html loaded (with sidebar, logo etc) and only then i try to get (for example) my articles, and then, if i get 401 i'm redirected to auth-page and this is not so good: becouse i see small blink with all styles, and only then login page.
Is it real, to check first, if i get 200, and only then render page with sidebar etc, and if not: then auth-page.
How to do that?
i saw some solutions in web, but all they are huge, i need something really fast and simple)
Ok, I guess you try to build an SPA and you have routes in your app. You can use resolve before you render something. Take a look here http://www.johnpapa.net/route-resolve-and-controller-activate-in-angularjs/
You will have something like this:
'use strict';
var yeomanTestApp = angular.module('yeomanTestApp', [])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
resolve : {
// add your code here to see it the user has rights or not
}
})
.otherwise({
redirectTo: '/'
});
}]);
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.
The resolve property is a map object. The map object contains key/value pairs of:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}:
If string, then it is an alias for a service.
Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before the controller is instantiated and its value is injected into the controller.
My basic premise is I want to call back to the server to get the logged in user in case someone comes to the site and is still logged in. On the page I want to call this method. Since I am passing the user service to all my controllers I don't know which controller will be in use since I won't know what page they're landing on.
I have the following User Service
app.factory('userService', function ($window) {
var root = {};
root.get_current_user = function(http){
var config = {
params: {}
};
http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
Here is an empty controller I'm trying to inject the service into
app.controller('myResourcesController', function($scope, $http, userService) {
});
So on the top of my index file I want to have something along the lines of
controller.get_current_user();
This will be called from all the pages though so I'm not sure the syntax here. All examples I found related to calling a specific controller, and usually from within another controller. Perhaps this needs to go into my angularjs somewhere and not simply within a script tag on my index page.
You could run factory initialization in run method of your angular application.
https://docs.angularjs.org/guide/module#module-loading-dependencies
E.g.
app.run(['userService', function(userService) {
userService.get_current_user();
}]);
And userService factory should store authenticated user object internaly.
...
if (data.success == true) {
root.user = data.user;
}
...
Then you will be able to use your factory in any controller
app.controller('myController', ['userService', function(userService) {
//alert(userService.user);
}]);
You need to inject $http through the factory constructor function, for firsts
app.factory('userService', function ($window, $http) {
var root = {};
root.get_current_user = function(){
var config = {
params: {}
};
$http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
in your controller you can say
$scope.get_current_user = UserService.get_current_user();
ng attributes in your html if needed. besides this, i am not sure what you need.