I'm following scotch.io's tutorial on building a RESTful API while trying to get familiar with the MEAN stack.
I've followed pretty much everything so far, and got my RESTful API sending out JSON as intended. Should I try to access it via browser address bar or try it out with Postman it works.
I'm having problems with the consumption of said JSON response.
According to the tutorial, the Angular app is divided in controllers and services. The service uses $http to call the RESTful endpoint. My doubt is where and how should I use that service to call for the data.
Is it in the controller? Is the service exposed in a way that I can add its response to $scope?
I'm new to Angular/client-side routing, so please be gentle:) My code is below.
(Blog) Controller:
angular.module('BlogCtrl', []).controller('BlogController', function($scope, $http) {
$scope.tagline = 'Blog page!';
// can and should I call the service here?
});
Service:
angular.module('BlogService', []).factory('Post', ['$http', function($http) {
return {
// call to get all posts
get : function() {
return $http.get('/api/blog');
}
}]);
Routes:
angular.module('appRoutes', []).config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
// blog page that will use the BlogController
.when('/blog', {
templateUrl: 'views/blog.html',
controller: 'BlogController'
})
$locationProvider.html5Mode(true);
}]);
Angular App:
angular.module('myApp', ['ngRoute', 'appRoutes', 'MainCtrl', 'BlogCtrl', 'BlogService']);
Yes, you can make $http call in your BlogController.
However if you want to use your 'Post' factory, you should inject it to controller
angular.module('BlogCtrl', []).controller('BlogController', function($scope, Post) {...}
and make the request
Post.get().then(
function(response){console.log(response.data)},
function(errorResponse){/*...*/}
);
(I think you should also read about $resource (https://docs.angularjs.org/api/ngResource/service/$resource). Maybe it is something what you could use to replace your Post factory ;))
You want to inject the service into controller ( or anywhere else you would use it) and then make the function call using the injected service object
angular.module('BlogCtrl', [])
.controller('BlogController', function($scope, Post) {
$scope.tagline = 'Blog page!';
// Use service to get data
Post.get().then(responsePromise){
$scope.someVariable = responsePromise.data;
}).catch(function(err){
console.warn('Ooops error!')
});
});
Related
I am trying to run an $http function when my AngularJS application first loads.
This $http function needs to finish before any of the controllers in my application could properly function. How would I go about doing this? This sounds like a promise, but it sounds like I would be creating a promise in each controller...
I currently have the function that I want to run first like this:
app.run(function() {
$http.get('link').success(function(data) {
// success function. The data that I get from this HTTP call will be saved to a service.
}).error(function(error) {
});
});
However, sometimes the controller will load before the http call finishes.
The problem
Angular is not dynamic, you cannot add controller dynamically neither factory, etc. Also you cannot defer controller bootstrap, angular loads everything together, and it's quite disadvantage (will be fixed in Angular 2)
The cure
But javascript itself has very important feature - closure, which works anywhere, anytime.
And angular has some internal services that can be injected outside of angular ecosystem, even into browser console. Those services injected as shown below. We technically could use anything else (jQuery.ajax, window.fetch, or even with XMLHttpRequest), but let's stick with total angular solution
var $http_injected = angular.injector(["ng"]).get("$http");
The act
First of all, we defer whole angular app bootstrap, inject http service. Then you make your needed request, receive data and then closure get's to work, we pass received data into some service, or we could also assign in to some angular.constant or angular.value but let's just make demo with angular.service, so when your service has data, bootstrap whole app, so that all controllers get initialized with your needed data
Basically that kind of tasks solved like this
<body>
<div ng-controller="Controller1">
<b>Controller1</b>
{{text}}
{{setting.data.name}}
</div>
<hr>
<div ng-controller="Controller2">
<b>Controller2</b>
{{text}}
{{setting.data.name}}
</div>
<script>
//define preloader
var $http_injected = angular.injector(["ng"]).get("$http");
$http_injected.get('http://jsonplaceholder.typicode.com/users/1').then(function(successResponse) {
//define app
angular.module('app', []);
//define test controllers
//note, usually we see 'controller1 loaded' text before 'settings applied', because controller initialized with this data, but in this demo, we will not see 'controller1 loaded' text, as we use closure to assign data, so it's instantly changed
angular.module('app').controller('Controller1', function($scope, AppSetting) {
$scope.text = 'controller1 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
angular.module('app').controller('Controller2', function($scope, AppSetting) {
$scope.text = 'controller2 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
//define test services, note we assign it here, it's possible
//because of javascript awesomeness (closure)
angular.module('app').service('AppSetting', function() {
this.setting = successResponse;
});
//bootstrap app, we cannot use ng-app, as it loads app instantly
//but we bootstrap it manually when you settings come
angular.bootstrap(document.body, ['app']);
});
</script>
</body>
Plunker demo
You can do this when you configure your routes
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: 'main.html',
resolve: {
data: ['$http',
function($http)
{
return $http.get('/api/data').then(
function success(response) { return response.data.rows[0]; },
function error(reason) { return false; }
);
}
]
}
});
}]);
Similar question:
AngularJS - routeProvider resolve calling a service method
AngularJS: $routeProvider when resolve $http returns response obj instead of my obj
Heres a plunkr I found using a service, which is what I would recommend.
http://plnkr.co/edit/XKGC1h?p=info
So I've started to work on an own project, where I'm in the middle of developing the front-end of my website. I started out with an PHP Laravel back-end and I've setted up an API service for my database.
With a hybrid app in mind, i started using angularjs for my front-end web application. For the communication with my API using REST, I've came across restangular, which is pretty nice because it was exactly what I hoped for.
There is only one issue that bothers me, there is no real "guide" how to setup a maintainable module/factory/provider/service to replicate your api with a system that stores the data in local storage or setup simple system where you could inject the "Model" into an controller and just do Model->getAll() to fetching all models.
Because I'm new to angularJS, and therefor my knowedge on how to appeach this, is fairly limited. So far I've made this:
main application
var client = angular.module('clientApp', ['angulartics', 'angulartics.google.analytics', 'ngRoute', 'restangular']);
client.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/', {
controller: 'flongsController',
templateUrl: '/client_partials/Homepage.html'
})
.when('/flongs/:slug', {
controller: 'flongsController',
templateUrl: 'client_partials/Flong.html'
})
.otherwise({
redirectTo: '/'
});
}]);
flongsController
client.controller('flongsController', ['$scope', 'Restangular', '$routeParams', function ($scope, Restangular, $routeParams) {
//controller variables
var baseFlongs = Restangular.all('flongs');
$scope.flongs = {};
init();
function init() {
baseFlongs.getList().then(function(flongs){
$scope.flongs = flongs;
});
}
}]);
So, my question is simple:
How can i improve this code so that its more efficient and more maintainable?
Thanks in advance,
Nick van der Meij
First of all do not use service logic on the controller, instead use angular services for this purpose.
Let me share you how I build my projects,
First build Restangular Service :
angular.module('example').factory('exampleService', ['Restangular', function(Restangular){
// this is service object with list of methods in it
// this object will be used by controller
var service = {
getExamples: getExamples,
getExample: getExample
};
// get examples from server by using Restangular
function getExamples(){
return Restangular.all('examples').getList();
}
// get example with given id from server by using Restangular
function getExample(exampleId){
return Restangular.one('examples', exampleId).get();
}
return service;
}]);
here we build exampleService now let's inject it into a controller
angular.controller('ExampleCtrl', ['exampleService', function(exampleService){
// get examples by using exampleService
exampleService.getExamples().then(function (examples) {
$scope.examples = examples;
});
// get example with given id by using exampleService
exampleService.getExample('1234').then(function (example) {
$scope.example = example;
});
}]);
This is how I use it basically. For more advanced usage you can look the examples in Restangular Github Page.
I have a service called 'api', registered something like this:
angular
.module('myApp')
.factory('api', ['$http', function ($http) {
// do stuff with $http.get() etc here.
}]);
... and $http is being customized like this elsewhere:
angular
.module('myApp')
.factory('httpInterceptor', ['$rootScope', function ($rootScope) {
// do stuff to intercept http requests and auth things here
}]);
angular
.module('myApp')
.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
});
The interceptors are working when I directly inject $http into a controller, but when I use the api service in my controller, the $http customizations are not working. Looks like Angular does not add the intercepts by the point it's required by the factory I created.
How do I solve this?
The question was based on a wrong premise. The interceptors are in fact working, I was just using the wrong hook point. Instead of customizing the responseError property of the interceptor object, I was trying to intercept errors from the response property itself. This is why I thought the interceptors weren't working at all.
This problem does not really exist. The provider's interceptors are working correctly even in a factory.
You can try to make the httpInterceptor a variable inside the config instead of making it a factory:
angular
.module('myApp')
.config(function ($httpProvider) {
var httpInterceptor = ['$rootScope',
function($rootScope) {
// do stuff to intercept http requests and auth things here
}
];
$httpProvider.responseInterceptors.push(httpInterceptor);
});
Please note that I've also changed $httpProvider.interceptors to $httpProvider.responseInterceptors and the parameter passed to push() is not a string.
Let me know if that helps.
EDIT:
You can also consider using this plugin: https://github.com/witoldsz/angular-http-auth. It has this cool feature after the interception: "The authService will then retry all the requests previously failed due to HTTP 401 response."
I am relatively new to Angular but I am quite an experienced developer. So far I have made quite some progress in building my application to work with a CMS. I am a bit lost however on what the 'correct' approach would be to handle data in my model.
This is best described with an example:
Because I am hooking up my angular frontend with a CMS, the routing (pages) exist only in the CMS context. This means that the routing should be dynamic as well. I have managed to get the dynamic routes thing to work, but when I try to do things the right way (actually getting data from a server) I run into some issues...
app.config(function($provide, $routeProvider) {
$provide.factory("$routeProvider", function() {
return $routeProvider;
});
});
// Load the dynamic routes from the API...
app.run(function($routeProvider, $http, $scope, logger, siteRoutes) {
$routeProvider.when('/', { templateUrl: '__views/', controller: 'ContentPageController' });
$routeProvider.otherwise({redirectTo: '/'});
});
In other words, I inject a service into my app.run method (siteRoutes) and this one should connect to the API.
So my siteRoutes is a service:
cmsModule.service('siteRoutes', function siteRouteFactory(apiConnection, logger)
// SNIP
And in this service I inject my generic apiConnection service:
cmsModule.factory('apiConnection', ['$q', '$http', '$timeout', 'logger', function apiConnectionService($q, $http, $timeout, logger)
What I want is this:
I would like the siteRoutes service to load the data once and not execute the connection every time. I did this in the following way:
bla.service('example', function() {
var service = {
get: function(apiStuff) { // DO API CONNECT WITH .THEN HERE },
data: {}
}
service.get();
return service;
}
I would like one entry point towards the Api that handles all the $q stuff (my factory) I assumed I need to handle all the .then() stuff in my siteRoutes object, which is what I did.
Now, what happens in my app.run method is that I don't get the siteRoutes object with any data. So I recon I need to do a .then there as well?
But that made me question the entire design of putting all logic in a separate factory for the connection, because I basically like my app to just use the data and have my library deal with the async stuff (if you get what I am saying)...
Hope this is clear.
TL;DR -> How to make your services / factories handle async stuff without making your 'app' deal with it?
The templateUrl property can also be a function that takes the url parametes as input.
In the example below all routes will load a template with same name.
Eg. domain.com/#/blabla.html will load the view blabla.html from the server.
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/:templateName',
{
templateUrl: function (params) {
return params.templateName + ".html";
}
}
)
.otherwise({ redirectTo: '/main' });
}]);
I'm getting started with node/express/Angular by using the MEAN stack at mean.io.
I don't understand how the Angular controller calls the express controller to fetch data.
What I have is public/js/controllers/index.js:
angular.module('mean.system').controller('IndexController', ['$scope', 'Global', 'Tabs',
function ($scope, Global, Tabs) {
$scope.global = Global;
Tabs.query(function(tabs) {
$scope.tabs = tabs;
});
}]);
But I'm confused what exactly 'Tabs' is. I know that somehow, magically, eventually this method is called - I think this is the Express controller?
app/controllers/tabs.js:
exports.all = function(req, res) {
Tab.find().sort('artist').select("-content").populate('user').exec(function(err, tabs) {
if (err) {
res.render('error', {
status: 500
});
} else {
res.jsonp(tabs);
}
});
};
But I don't understand how it gets called. What I want to do is call a different method in app/controllers/tabs.js instead - namely, this:
exports.newest = function(req, res) {
Tab.find().sort('-created').limit(10).select("-content").exec(function(err, tabs) {
...
But I don't understand how to "wire up" the AngularJS controller with the express controller.
i.e. what do I have to do so that I can do something like this in my controller:
angular.module('mean.system').controller('IndexController', ['$scope', 'Global', 'Tabs',
function ($scope, Global, Tabs) {
$scope.global = Global;
Tabs.newest(function(tabs) { // this won't work
$scope.tabs = tabs;
});
}]);
In MEAN, The Articles service is an angular service that returns a $resource object you can usually find in the public/js/services folder.
$resource is a wrapper around $http AJAX service that comes with angularjs, it enables you to connect with RESTful endpoints if your REST service is built in a specific manner.
The connection to the the node.js controller happens using the routes.js object found in the config folder that binds routes to specific module methods.
further reading:
http://docs.angularjs.org/api/ngResource.$resource
http://expressjs.com/api.html#app.VERB