Setting templateUrl in an AngularJS Module to increase modularity - angularjs

I have nested angularJS modules EventList and EventPage.
The directory structure looks like this:
app.js
EventForm
|_ EventList.js
eventListView.html
EventPage
|_EventPage.js
eventPageView.html
Eventlist.js:
angular.module('EventList', ['EventPage'])
.config([ '$routeProvider', function config($routeProvider){
$routeProvider.when(Urls.EVENT_LIST, {
templateUrl: 'app/EventList/event-list.html',
controller: 'EventListCtrl'
});
}])
.controller('EventListCtrl', ['$scope', '$location', '$http', function EventListController($scope, $location, $http) {
}]);
EventPage.js:
angular.module('EventPage', [])
.config([ '$routeProvider', function config($routeProvider){
$routeProvider.when(Urls.EVENT_PAGE + '/:id', {
templateUrl: 'app/EventList/EventPage/event-page.html',
controller: 'EventPageCtrl'
})
}])
.controller('EventPageCtrl', ['$scope', '$routeParams', '$http', function EventPageController($scope, $routeParams, $http) {
}]);
Obviously the templateUrl is hardcoded right now. My question is what is the best way to set the templateUrl in the routeProvider so the modules aren't dependent on the hard coded directory structure and can be taken out and reused in other AngularJS projects without breaking? Is there a way to just get insertViewNameHere.html in the current folder?

If you're using Grunt, this is easy. Not because modularizing AngularJS is trivial, but because all the hard work has already been done for you by the good folk doing angular-bootstrap...
When I faced this problem I simply downloaded their Gruntfile and changed every ui.bootstrap to my-angular-module. Since I mimicked their basic directory structure, it worked right out of the box.
Also, you might want to update the Angular and Bootstrap versions. E.g., change
ngversion: '1.0.8',
bsversion: '2.3.1',
to
ngversion: '1.2.3',
bsversion: '3.0.2',

Related

How to reuse Angular directive in an ASP.NET MVC application?

I have an ASP.NET MVC application with a lot of Areas and models, views and controllers inside them. I have a small calculator that I want to write in Angular because it will be easier for me, as a developer, and cooler for the user (instead of using jQuery only).
The thing is I want to have this calculator on different views in some of my areas in the MVC app. The best solution that I could came up with is make an Angular directive and then use it in every view that I need it. My question is whether this will work and whether I would have to make a different Angular module for every view that will use the directive.
Any better solutions and proposals are welcome.
Example from Angular documentation: https://docs.angularjs.org/tutorial/step_07
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
you can config as many controlles that you like per page(html). and each htm will include your custom calculator directive.
All the controllers can be registered to the same module:
var phonecatControllers = angular.module('phonecatControllers', []);
phonecatControllers.controller('PhoneListCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
$scope.orderProp = 'age';
}]);
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.phoneId = $routeParams.phoneId;
}]);
Here's my take on it: You don't need a separate module per view. If you are only using this to provide your calculator directive then you could just do something like:
angular.module('calculator', [])
.directive('onscreenCalculator', function () { ... });
I don't know if you are using partial views, but if you are it could be problematic if you use the ng-app = "calculator" directive at the view level. Personally, I would put it in your _Layout.cshtml and then you know you're only going to have one instance.
Your biggest challenge (in my opinion) is going to be how to get the result of the calculation back into your view since you're not truly writing an Angular app, but just using a directive. I'm sure it can be done with some playing around, though.

Angular JS problems with Ng-Route, Gulp, and Json

I'm pretty new to the world of front end development and I'm working through my first project with AngularJS. I'm also using Yeoman, Gulp, Bower to set up my project, which is also bran new to me... I've kind of crafted a build from the yo generator Gulp Angular and put my own personal touches to it. I'm sure I did more harm than good :p but I'm learning.
Anyways I've been coding all day and am really stumped why my project is having trouble when I use the ng-route. The home display works correctly but when I try to click on a link to a deeper page it just refreshes back to the home. I'm using Json files rather than a server and the Gulp Angular set up has all my files compiled to another folder when launching a server. Is there any chance the issue could lie within the compiler?
I'm starting to go crazy so I think I'm gonna call it quits for the night but if anyone has the time and the generosity to look over my github repo I would be over joyed :)
Thanks
https://github.com/jleibham/BhamDesigns.git
App Module
(function() {
'use strict';
var bhamDesignsApp = angular.module('bhamDesignsApp', ['ngAnimate', 'ngTouch', 'ngSanitize', 'ngMessages', 'ngAria', 'ngRoute', 'mm.foundation', 'appControllers']);
bhamDesignsApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/projects', {
templateUrl: 'partials/projects.html',
controller: 'ProjectsController'
}).
when('/projects/:projectId', {
templateUrl: 'partials/gallery.html',
controller: 'GalleryController'
}).
otherwise({
redirectTo: '/projects'
});
}]);
})();
App Controller
(function() {
'use strict';
var appControllers = angular.module('appControllers', []);
appControllers.controller('ProjectsController', ['$scope', '$http',
function ($scope, $http) {
$http.get('app/json/projects.json').success(function(data){
$scope.projects = data;
});
$scope.orderProp = '-year';
}]);
appControllers.controller('GalleryController', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.projectId = $routeParams.projectId;
}]);
})();
You are calling the wrong url and your routes do not recognize the url you do call with your href, so it redirects you. In you are going to call this:
href="#/json/galleries/(what ever the project.id is)
Then your routing should look similar to this:
when('/json/galleries/:projectId', { /// the rest of your code
You are going to want to use $routeParameters with ngRoute. here is a great example

how to execute the same init code on each controller instantiated by $routeProvider?

In my .config I have a router that instantiate a pair controller-router:
angular.module('reporting', ['ng', 'ngRoute', 'ngResource', 'reporting.directives', 'reporting.controllers', 'reporting.config', 'ngGrid', 'ui.bootstrap'])
.config(["$routeProvider", "$provide", function ($routeProvider, $provide) {
$routeProvider
.when('/dealersReq', {
templateUrl: 'reporting/partials/dealersReqs.html',
controller: 'DealersCtrl'
})
.when('/lmtReq', {
templateUrl: 'reporting/partials/lmt.html',
controller: 'lmtCtrl'
})
.when('/leadsCreated', {
templateUrl: 'reporting/partials/leadsCreated.html',
controller: 'LeadsCreatedCtrl'
})
...
but each controller share the same initialization code (think about it like a constructor) that sets in the rootScope some variable like a title and other useful information for some controllers outside the <view>:
.controller('DealersCtrl', ['$scope','$rootScope', 'CONFIG',
function($scope, $rootScope, CONFIG) {
//////////// duplicated code
var key = 'qtsldsCrtSncheQ';
$rootScope.openReport.key = key;
$rootScope.openReport.title = CONFIG.reports['' + key].title;
//////////// duplicated code
console.log('Initialized! Now I do what a controller should really do');
}]);
What I would like to do is finding a way to move that code - which is duplicated into every controller at the moment - into something smarter and neater. Soemthing that the route can call during the routing instanciation for example. Of course each controller should have a different key, but that one could be exactly the controller name actually. I really don't know how to improve this. Any suggestion?
Why don't create a method on the $rootScope which does that, and then call it from each controller, i.e.: $rootScope.init().
You could use a Service for shared code but you should avoid to use $rootScope
https://stackoverflow.com/a/16739309/3068081

How to separate Controller file in angularjs

I have looked at various examples of separating the files. I can understand them but the problem comes with the way my code is. I want separate these controllers in different files.
'use strict';
angular.module('myModule', ['ui.bootstrap']);
var myApp = angular.module('myApp', []);
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}]);
myApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.when('/getplaces', {
templateUrl: 'templates/getplaces.html',
controller: 'ListCtrl'
})
The below controller needs to be in a different file.
///////////// MONTHLY DATA /////////////////////////////////////
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/getmonth', {
templateUrl: 'templates/getmacs.html',
controller: 'MonthlyCtrl'
})
}])
.controller('MonthlyCtrl', function ($scope, $http) { $scope.visible = true;
})
I have more than 20 controllers like above. but how do I separate them.
Here is how you should do it,
first declaration
angular.module('appName', ['Module1', 'Module2', 'Service1']);
subsequent declarations
here all the controllers and service can be in separate files.
angular
.module('Module1', [])
.controller('AbcController', ['$scope', '$timeout', 'Service1', function ($scope, $timeout, service1) {} ]);
angular
.module('Module2', [])
.controller('EfgController', ['$scope', '$timeout', 'Service1', function ($scope, $timeout, service1) {} ]);
angular.module('Service1', [])
.service('XYZService', ['$http', function LmnoService($http) {
} ]);
This should easily be done, I would organize my application route configurations into the main app.js file.
myApp.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.when('/getplaces', {
templateUrl: 'templates/getplaces.html',
controller: 'ListCtrl'
}).when('/getmonth', {
templateUrl: 'templates/getmacs.html',
controller: 'MonthlyCtrl'
})
}])
Then when you create a separate controller in each file just reference the application name as such:
myApp.controller('MonthlyCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.visible = true;
}])
You will also notice I am using the array initializer way, this will save you some hastles when you are doing minification.
You can follow this convention:
First load all the library dependencies like angular, angular-routes etc
then your config holder js file which contains your module declaration.
then all other files with controller methods.
I would map specific modules to functionality (and not by layers), each one containing its concerned controllers, services and appropriate directives.
You would have one module called 'places.list' for instance, containing all controllers, services/factories and directives associated to it.
The rule is: one module, one file, otherwise you would be forced to declare those files in order... (first modules declaration..then controllers etc.. ugly)
If you split your modules in the right way, you will notice that each one is easy to maintain and doesn't contain in general a huge amount of code.
Even each route declaration (.config) would be split across those modules.
=> All the route concerning places would be declared inside the module places.list.
Indeed, it would be ugly (and difficult to maintain) to declare the whole navigation rules in your main module..
Thus, each module would be easily testable by loading only specific module's dependencies that are relevant for the test.

Injection of angularFireCollection into angular.js config for usage in resolve

I've been experimenting a little with Angular.js lately. As part of this I created a very simple set of controllers with an ng-view and templates to trigger depending on the route requested. I'm using angularFireCollection just to grab an array from Firebase. This works fine in the thumbnailController which does not form part of the ng-view.
My problem is that in addition to the data flowing into the thumbnailController, I also need the two other controllers to be able to access the data. I initially simply set the data to either be part of $rootScope or to have the ng-view as a child of the div in which the thumbnailController is set.
However, the issue from that perspective is that each sub-controller presumably attempts to set the data in the template before it is actually available from Firebase.
The solution appears to be using resolve as per the answer to this question angularFire route resolution. However, using the below code (and also referencing angularFireCollection) I get an error message of angularFire being an unknown provider. My understanding is the code below should be sufficient, and I would also add that the usage of angularFireCollection in thumbnailController works fine as I say.
I also experimented with injecting angularFire/aFCollection directly into the controllers using .$inject however a similar issue arose in terms of it being considered an unknown provider.
If possible could someone advise on what the issue may be here?
var galleryModule = angular.module('galleryModule', ['firebase']);
galleryModule.config(['$routeProvider', 'angularFire', function($routeProvider, angularFire){
$routeProvider.
when('/', {
controller: initialController,
templateUrl: 'largeimagetemplate.html',
resolve: {images: angularFire('https://mbg.firebaseio.com/images')}
}).
when('/view/:id', {
controller: mainimageController,
templateUrl: 'largeimagetemplate.html',
resolve: {images: angularFire('https://mbg.firebaseio.com/images')}
}).
otherwise({
redirectTo: '/'
});
}]);
galleryModule.controller('thumbnailController', ['$scope', 'angularFireCollection', function($scope, angularFireCollection){
var url = 'https://mbg.firebaseio.com/images';
$scope.images = angularFireCollection(url);
}]);
function initialController($scope,images){
$scope.largeurl = images[0].largeurl;
}
function mainimageController($scope, images, $routeParams){
$scope.largeurl = images[$routeParams.id].largeurl;
}
I got the chance to dig into this a little bit - it seems like regular services cannot be used in .config sections. I'd instantiate angularFire in the controller instead of using resolve, for example:
galleryModule
.value("url", "https://mbg.firebaseio.com/images")
.controller('thumbnailController', ['$scope', 'angularFireCollection', 'url',
function($scope, angularFireCollection, url) {
$scope.images = angularFireCollection(url);
}])
.controller('initialController', ['$scope', 'angularFire', 'url',
function($scope, angularFire, url) {
angularFire(url, $scope, 'images').then(function() {
$scope.largeurl = $scope.images[0].largeurl;
});
}])
.controller('mainimageController', ['$scope', 'angularFire', '$routeParams', 'url',
function($scope, angularFire, $routeParams, url){
angularFire(url, $scope, 'images').then(function() {
$scope.largeurl = $scope.images[$routeParams.id].largeurl;
});
}]);
This is not ineffecient, since the data is only loaded once from the URL by Firebase, and all subsequent promises will be resolved almost immediately with data already at hand.
I would like to see angularFire work with resolve in the $routeProvider, however. You can use this method as a workaround until we figure out a more elegant solution.

Resources