Wire up AngularJS controller to express controller - angularjs

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

Related

Angular Services within Modules

Having difficulty injecting a service into another service. I want a service hierarchy, where I can pass/request logic to/from the parent service, for sake of brevity and encapsulation.
So for example, I have a userService, and I want the userService to manage the user's toDoList. So I created a toDoService, and I want controllers to add a toDo for the user by passing the request to the userService, which relays to the toDoService. Here's what I have:
// app.js
angular.module('myApp', [
// other dependencies...
'myApp.myServices'
]);
// services/toDoService.js
angular.module('myApp.myServices', [])
.factory('toDoService', function($http) {
getStuff = function(userId) {
// returns $http call
};
addStuff = function(userId, data) {
// returns $http call
};
});
// services/userService.js
angular.module('myApp.myServices', [])
.factory('userService',
['$http', 'toDoService', function(
$http, toDoService) {
addToDo = function(data) {
toDoService.addStuff(user.uid, data)
.then(function(success) {
// apply bindings
})
.catch(function(error) {
// display error
});
};
getToDos = function(data) {
toDoService.getStuff(user.uid)
.then(function(success) {
// apply bindings
})
.catch(function(error) {
// display error
});
};
}]);
The controller works with the userService, and code from toDoService worked when it was originally in userService. But when I create the toDoService and move that code there and encapsulate it, angular complains about toDoService.
Error: [$injector:unpr] Unknown provider: toDoServiceProvider <- toDoService <- userService
I've checked script references, and all scripts are properly included. e.g. <script src='/[..]/toDoService.js' /> etc...
So I'm wondering is it even possible to inject a service into another service within the same module? Is it an issue with my naming convention?
angular.module('myApp.myServices', [])
This line in userService.js defines the module myApp.services, overwriting the one that has previously been defined in toDoService.js.
Define the module only once (in a separate file). Get a reference to this previously defined module using
angular.module('myApp.myServices')
i.e. without the empty array as second argument.

Consuming RESTful response via Angular service

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!')
});
});

Using Angular $Resource what is the angular way to create a service using multiple api calls?

I am working on an angular project and I am using $ resource for the first time. I currently have just as a test to get data out of my database a service that has one call to $resource
Here is my service:
(function() {
"use strict";
angular.module("commonServices")
.factory("ptaResource",
["$resource", ptaResource]);
function ptaResource($resource) {
return $resource("http://localhost:55928/api/excercises", {}, { 'query': { method: 'GET', isArray: true } });
}
})();
My question is this. I have a lot of calls to make to various controllers and methods within those controllers. I cant wrap my head around how to structure a service that allows me to call it with an endpoint listed
I tried doing something like this:
var services =[
getExercises: getExercises,
doSomething: doSomething
];
return service;
function getExercises (){
return $resource request here
}
But that did not work, I have looked on line but any tutorial is only exposing one of each type of request. I am going to have several get requests to controllers, some with different query strings. I will be querying different controllers as well. The purist in me tells me that all of this belongs in one place.
What would be a good method for doing this one large service with a way to call each request individually. Should I break them into a service per web api controller. Any input would be appreciated.
Thanks,
John
If you wanted to wrap your resources in a service, you could do something like this:
angular
.module('commonServices', ['ngResource'])
.factory('ptaResource', ['$resource', function($resource) {
return $resource('http://localhost:55928/api/excercises/:excerciseId', {
excerciseId: '#id'
});
}])
.service('CommonService', ['ptaResource', function(ptaResource) {
return {
getExercises: function() {
return ptaResource.query().$promise;
}
};
}]);
Then you could call it like so:
angular
.module('app', ['commonServices'])
.controller('SomeController', ['$scope', 'CommonService', function($scope, CommonService) {
$scope.exercises = CommonService.getExercises();
// or
CommonService.getExercises().then(function(exercises) {
$scope.exercises = exercises;
}).catch(function(err) {
// handle error here
});
}]);

Angular.JS share a single JSON object between controllers

I´m trying to code a CRUD app with Angular.JS, and I need your help to move on.
This is the scenario:
View 1 (index) gets JSONP data from a remote API and stores it.
View 2 (master) shows data filtered on a grid
View 3 (detail) shows an specific item selected on View 2
I did it already, but requesting the very same JSON object on each view, , but I think one only api call is enough.
I can´t figure out how to properly share this JSON object for all the controllers. I tried several tutorials on ngResource, $http, factories and services but still have not a clear path to go through.
How can I do this?
Any snippet or code sample you may share will be very useful to keep on tryin this thing...
Thanks in advance,
Ariel
You can implement a base controller to store common functionality that's shared between the controllers. I wrote a blog post about it recently, here's the code snippet showing how it works:
'use strict';
angular.module('Diary')
// base controller containing common functions for add/edit controllers
.controller('Diary.BaseAddEditController',
['$scope', 'DiaryService',
function ($scope, DiaryService) {
$scope.diaryEntry = {};
$scope.saveDiaryEntry = function () {
DiaryService.SaveDiaryEntry($scope.diaryEntry);
};
// add any other shared functionality here.
}])
.controller('Diary.AddDiaryController',
['$scope', '$controller',
function ($scope, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
}])
.controller('Diary.EditDiaryController',
['$scope', '$routeParams', 'DiaryService', '$controller',
function ($scope, $routeParams, DiaryService, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
DiaryService.GetDiaryEntry($routeParams.id).success(function (data) {
$scope.diaryEntry = data;
});
}]);
Using services to cache and share the data across controllers would be the way to go. Since services in angular are singleton, the same copy of data can be shared. A service such as
angular.module('myApp').factory('dataService', function($q, $resource) {
var items=[];
var service={};
service.getItems=function() {
var itemsDefer=$q.defer();
if(items.length >0)
itemsDefer.resolve(data);
else
{
$resource(url).query({},function(data) {
items=data;
itemsDefer.resolve(data)
});
}
return itemsDefer.promise;
}
return service;
});
Now in the controller you can inject the dataService and call the getItems method. This method returns a promise, which is either resolved using the cached data or by making remote request.
And the controller code would look something like
angular.module('myApp').controller('MyCtrl', function($scope,dataService) {
dataService.getItems().then(function(items) {
$scope.items=items;
}
});

AngularJS application architecture

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' });
}]);

Resources