Defining routes outside app.js - angularjs

I have been told that in AngularJS, you are supposed to do all your routing in one file. Now, I feel like there's a fundamental problem with that. If you have a large web application, it doesn't seem to make sense to send every single route possible to the user. This would mean, that your main JavaScript file could end up with routes that look like path/:to/a/part/:of/my/application.
I'm used to a different way of routing, the main router has certain routes (e.g. /admin, /blog, /about). All the logic, such as further routing, for the blog is handled in (e.g.) blog.js. Within blog.js, there are routes such as /latest, ending up as the final route /blog/latest.
How would one do this properly within Angular? Or am I completely missing the point with Angular?

You don't have to do all your routing in one file, or in app.js for that matter. It just has to be done in a config block. Since your Angular app should be defined in the global namespace, you could have a separate file that starts like this:
yourAngularApp.config([
'$routeProvider',
'$locationProvider',
function ($routeProvider, $locationProvider) {
// Your routing code
]);
Thus you could separate your routing code however you like.
See more anout config blocks here under the headings "Module Loading & Dependencies" and "Configuration Blocks:" https://docs.angularjs.org/guide/module
The thing I am trying to achieve is only exposing the /blog/latest
route once you request the page /blog, it's not relevant to know
/blog/latest exists if you're not interested in /blog either.
If navigating to /blog caused the page to refresh, this goal would not be difficult to accomplish. You could structure your app in such a way that
your main routes are defined in a single file, and then
your further, /blog-related routes are in a separate file that only gets downloaded when you're in that area of that application.
However, if your application is a full-on SPA, and Angular does not get bootstrapped again when the /blog page is requested, this approach would not work. There is a more-complex approach I've read about that involves saving a reference to the routeProvider for later use:
yourAngularApp.config([
'$routeProvider',
'$locationProvider',
function ($routeProvider, $locationProvider) {
// Your routing code
yourAngularApp.routeProvider = $routeProvider;
]);
Then, you can utilize yourAngularApp.routeProvider later on (in a controller, for instance) to further configure your routing.
I found an even cleaner solution solution than that here:
Deferring Route Configuration Using Decorators And Route Resolution In AngularJS
The gist of that approach is that you can create a $route decorator during the configuration phase, and use that to expose the $routeProvider functionality.
For completeness, here are the main bits of that code. First you define the decorator to modify $route during the configuration phase, exposing whatever functionality from $routeProvider you like (my example just shows .when()):
yourAngularApp.config([
'$routeProvider',
'$locationProvider',
function ($routeProvider, $locationProvider) {
$provide.decorator( "$route", routeDecorator );
function routeDecorator( $delegate ) {
var $route = $delegate;
$route.when = function( path, route ) {
$routeProvider.when( path, route );
return( this );
};
return( $route );
}
]);
Then you can inject $route into other areas, like controllers and services:
yourAngularApp.controller(
"YourController",
function($scope, $route) {
// code that modifies routing here
// ...
$route.reload();
}
);

Related

Defining controllers using ui-router

I am creating web application using Angular js in ES6. I just started learning angular. I have following questions which I couldn't understand much from resources in internet.
1) I am using ui-router for routing based on states. I have following code in my controller
myApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home);
$stateProvider
.state('contact', {
url: '/contact',
templateUrl: 'contact.html',
controller: myContactController
});
};
Contact.html:
<div ng-controller=”myContactController”>
….
</div>
Question:
a) I have specified the controller in my state in js. Should I need to specify the controller using ng-controller in my view also? What is the difference and why its necessary ?
2) I have a base module for my app.
Base module - Index.js :
import subapp1 from ‘./subApp1/index’;
angular.module(“myapp”,[subapp1]);
subApp1/index.js
Export default function(){
Angular. module(“subApp1”,[]);
};
Question:
a) Is this the right way of injecting sub module dependency in to base module?If not which is the best way to inject module dependency in to base module?
b) I would appreciate if I can get best links to understand dependency injection and different scopes in angular js in basic way.
I have specified the controller in my state in js. Should I need to specify the controller using ng-controller in my view also? What is the difference and why its necessary ?
You don't need to use ngController in HTML. Router will fetch HTML template and compile it with specified controller.
I have a base module for my app...
You specify dependent module by its name, so your setup could look like this (note, how you export name property of the Angular module):
export default angular.module('subApp1', [])
.factory('someService', semeService) // For example, attach some module service
.directive('someDirective', someDirective) // ... or some components
.name;
and then
import subapp1 from './subApp1/index';
import subapp2 from './subApp2/index';
angular.module('myapp', [
subapp1,
subapp2
]);
1.a) no, you shouldn't. If you do, you'll have two instances of the controller
2.a) no. First you don't "inject" a module into another module: a module depends on another one, that's all. That has nothing to do with dependency injection. And the syntax for that is
angular.module('myapp', ['subApp1']);
I.e. the elements of the array must be names of module you depend on. And of course, these modules must themselves be defined (before or after, it doesn't matter), using
angular.module('subApp1', []);
2.b) https://docs.angularjs.org/guide

Angular.js create routes from json file

I have an angular.js application with a CMS(on S3). I would like to be able to create an entire page including the route from the CMS without having to push new code and redeploy the application. Is there some way to do this?
From what I can tell the $routeProvider is initialized in the .config block which only allows providers to be injected. Also from what I can tell I cannot use services in the .config block which basically eliminates the possibility to reference a get request in order to generate the routes from an S3 file.
I am new-ish to angular.js but I feel like I am overlooking something. If anyone can point me in the right direction I would be really grateful! Thanks in advance!
I am not sure if there is a more "Angular" way to solve this but here is what I did.
1)Rather than store the values as .json, I simply created a .js file where I set a variable equal to the route data.
2) I used a script tag in the layout of my Angular.js application that imports the .js file that I stored on S3.
3) In the config block I created the states required like so:
#newco.config(['$provide', '$locationProvider', '$httpProvider', '$stateProvider', '$urlRouterProvider',
($provide, $locationProvider, $httpProvider, $stateProvider, $urlRouterProvider) ->
$locationProvider.html5Mode(false);
$locationProvider.hashPrefix('!');
$urlRouterProvider.otherwise('/404')
_.each routes.routes, (route) ->
$stateProvider
.state(route, {
url: if route == '/' then route else "/" + route,
templateUrl: '/pages/home/new_index',
controller: 'NewHomeController',
resolve:{
pageData: (cmsService) ->
cmsService.pageData route
basePlan: -> 'default'
customerCategoryId: -> 'domestic'
}
})
$stateProvider
.state('/404', {
templateUrl: '404.html'
})
])
If there is a better way to do this I would certainly be interested in hearing it because I almost feel like my solution is 'hackey' but I simply can't find a more appropriate solution because the routes are initialized in during the config block which forbids the use of services.
If someone has a better idea or a reason why my solution is not ideal I would love to hear it.

reset myApp into body by angular.bootstrap

I have a problem.
I wants design multiple modules, each module will config route for itself. And before login I just wants load 3 modules (with 3 route), and after login I continue load 3 module (with 3 route). So I need re-config myApp to add new routes, then re-set into body to apply myApp.
And my solution is:
I create mainModule is myApp, and i config for it then I set into body by angular.bootstrap
angular.element(document).ready(function() {
angular.bootstrap(document.body, ["myApp"]);
});
Then, I need re-config for myApp and I set again.
angular.element(document).ready(function() {
angular.bootstrap(document.body, ["myApp"], true);
});
An error occurs
[ng:btstrpd] App Already Bootstrapped with this Element '<body cz-shortcut-listen="true" class="ng-scope">'(…)
How can I reset myApp into body or any solution ???
Thanks,
While lazy loading is not among listed features in Angular, some of the things which are intrinsic to config phase can be performed at run-time (while they cannot be recommended and belong to 'use at your own risk' category).
When provider is being used to configure the service before its instantiation, most likely (it depends on service implementation) it can be used to configure it after it was instantiated, e.g.
app.config(function ($routeProvider, $provide) {
// now $routeProvider is available for injection during both config and run phases
$provide.constant('$routeProvider', $routeProvider);
});
app.run(function ($routeProvider, $location) {
$routeProvider.when('/brand-new-route', { ... });
$location.path('/brand-new-route');
});
More of this injector trick here.
This method isn't forbidden but relies on current service implementation and undocumented behaviour, so it has to be either tested thoroughly or should be avoided at all.

How to set in angularjs templateUrl to location.pathname in otherwise clause

I'm new to angularjs and want to integrate it in a cakephp app. For some pages I don't have a controller since no javascript is exectuted there or because I still have to create them. I however don't want to list them all in the routes. For this reason i set it like the following:
angular.module('desktop', []).
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/', {templateUrl: 'pages/index', controller: IndexController})
.when('/clubs', {templateUrl: 'partials/clubs.html',controller: ClubListController})
.otherwise({templateUrl: location.pathname});
}]);
This is however not working. When I go to /help, nothing happens. What am i doing wrong?
From my comments:
As far as I know it there is currently no way to do this with just routes. Routes are made to be static, they are defined as the app loads and do not update dynamically as time goes by. So using location.pathname() (or directly checking window.location) won't work since the route be set to whatever the value is when the app starts, and then never change again. It won't update when you load a new path unless you do a full browser reload (this is btw possible, but a hacky sollution).
But people have been working around it using includes, which might work for you depending on what you are after. See this question and the accepted answer for an example of how this works.
AngularJS - How to use $routeParams in generating the templateUrl?

How to update a route from inside an AngularJS controller?

I have an AngularJS application into which I want to load some plugins that are discovered by a controller when it starts up. In order for the plug in to work I have add some routes to the $routeProvider from the controller, but there seems to be no way to do this.
Right now I'm using a pretty ugly hack as below:
var routeProvider;
angular.module('example', [], function($routeProvider) {
routeProvider = $routeProvider;
// Set up other routes
}
function Controller($http, $location, $timeout) {
// Use $http to find some plugin
routeProvider.when(plugin.url, plugin.options);
// Ugly hack so that the plugin appears if $location.path() already points to it
var path = $location.path();
$location.path("/");
$timeout(function() { $location.path(path); }, 10);
}
If I don't do the nonsense with $timeout then if I start (load the page) at the route for the plugin it won't load ($route.current remains blank). With the jump between paths the route gets resolved properly and the plugin view loads as it should.
Is there a better way of doing this?
You could tear $routeProvider from the source and make your own version? :-)
$routeProvider is just a provider made for you. https://github.com/angular/angular.js/blob/master/src/ng/route.js
The way that we did this in the end was to have all of the routes downloaded from services before we start up AngularJS and then use that data structure to set up the routes, then bootstrap Angular once we had the information and everything was set up properly.
The only downside of this is the delay in start up, especially if there are multiple service plug ins that you need to handle.

Resources