Angular.js create routes from json file - angularjs

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.

Related

using ui.router to dynamic load pages

I'm a newbie in AngularJS, and I was using ui.router.
What I am trying to do is load html from different portals, like:
/s3/home
/goo/home
/gle/home
All prefix like s3, goo, gle are from backend, I have to get it first then load my pages, any idea for this, any way to put a variable in relative path, like
/{{portal}}/home
Mockup for expression:
angular.module('app')
.config(function ($stateProvider) {
$stateProvider
.state('{{portal}}.home', { //portal from backend
url: '/',
views: {
'content#': {
templateUrl: 'scripts/app/{{portal}}/home.html', //portal from backend
controller: 'MainController'
}
}
});
})
Find a way to do it, dynamic templateURL will do the same thing
http://fredparke.com/blog/angularjs-directive-dynamic-template
I want to use it in Javascript, is it possible
No i'm afraid not. Use Curly braces to bind expressions to elements in your html. I think you need to rethink more fundamentally your application and why you want to have "variable" template urls in such a way. Try and read up on REST as that's probably what you want to have for your backend. The frontend can then parameterise requests via url/query parameters

Defining routes outside app.js

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();
}
);

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?

Do I just have to include the ui.router module just once or in each module I set up?

I have the following:
var app = angular
.module('app', ['ui.router', 'admin', 'home', 'questions', 'ui.compat', 'ngResource', 'LocalStorageModule'])
.config(['$stateProvider', '$locationProvider', function ($stateProvider, $locationProvider) {
Then in the modules like 'admin' I have:
angular
.module('admin', ['ui.router'])
.config(['$stateProvider', '$locationProvider',
function ($stateProvider, $locationProvider) {
}])
Rather than doing this in two files for example would it also be better to chain everything into the one file? Also do I need to set up my include of 'ui.router' in the admin module if it's already in the main module?
Can someone tell me if this is the correct way for me to set up my modules.
The AngularJS App can give you a reference. I think that approach is correct, but as you probably figured it out, you don't need to include the dependency twice.
In your example, you can freely remove ui.router as admin already has it. If you leave it like that and you ever want to even change the name of the ui.router, you need to go through all files that have it (in your case two files instead of one. It's no fun when you have a hundred). Leaving it the way it is atm, however, is harmless (as far as I know), and it might even be more readable for your team.

Otherwise on StateProvider

Using angular-ui-router, How can I use the otherwise method on $stateProvider or how can I use it at all ?
You can't use only $stateProvider.
You need to inject $urlRouterProvider and create a code similar to:
$urlRouterProvider.otherwise('/otherwise');
The /otherwise url must be defined on a state as usual:
$stateProvider
.state("otherwise", { url : '/otherwise'...})
See this link where ksperling explains
You can with $stateProvider using the catch all syntax ("*path"). You just need to add a state config at the bottom of your list like the following one:
$stateProvider.state("otherwise", {
url: "*path",
templateUrl: "views/error-not-found.html"
});
All the options are explained in https://github.com/angular-ui/ui-router/wiki/URL-Routing#regex-parameters.
The nice thing of this option, as opposed to $urlRouterProvider.otherwise(...), is that you 're not forced to a location change.
You can also manually inject $state into the otherwise function, which you can then navigate to a non-url state. This has the benefit of the browser not changing the addressbar, which is helpful for handling going back to a previous page.
$urlRouterProvider.otherwise(function ($injector, $location) {
var $state = $injector.get('$state');
$state.go('defaultLayout.error', {
title: "Page not found",
message: 'Could not find a state associated with url "'+$location.$$url+'"'
});
});
I just want to chime in on the great answers that are already provided. The latest version of #uirouter/angularjs marked the class UrlRouterProvider as deprecated. They now recommend using UrlService instead. You can achieve the same result with the following modification:
$urlService.rules.otherwise('/defaultState');
Additional methods: https://ui-router.github.io/ng1/docs/1.0.16/interfaces/url.urlrulesapi.html
Ok, the silly moment when you realize that the question you asked is already answered in the first lines of the sample code! Just take a look at the sample code.
angular.module('sample', ['ui.compat'])
.config(
[ '$stateProvider', '$routeProvider', '$urlRouterProvider',
function ($stateProvider, $routeProvider, $urlRouterProvider) {
$urlRouterProvider
.when('/c?id', '/contacts/:id')
.otherwise('/'); // <-- This is what I was looking for !
....
Take a look here.
The accepted answer references angular-route asks about ui-router. The accepted answer uses the "monolithic" $routeProvider, which requires the ngRoute module (whereas ui-router needs the ui.router module)
The highest-rated answer uses $stateProvider instead, and says something like .state("otherwise", { url : '/otherwise'... }),
but I can't find any mention of "otherwise" in the documentation it links. However, I see that $stateProvider is mentioned in this answer where it says:
You can't use only $stateProvider. You need to inject $urlRouterProvider
That's where my answer might help you. For me, it was sufficient to use $urlRouterProvider just like this:
my_module
.config([
, '$urlRouterProvider'
, function(
, $urlRouterProvider){
//When the url is empty; i.e. what I consider to be "the default"
//Then send the user to whatever state is served at the URL '/starting'
//(It could say '/default' or any other path you want)
$urlRouterProvider
.when('', '/starting');
//...
}]);
My suggestion is consistent with the ui-router documentation, (this specific revision), where it includes a similar use of the .when(...) method (the first call to the function):
app.config(function($urlRouterProvider){
// when there is an empty route, redirect to /index
$urlRouterProvider.when('', '/index');
// You can also use regex for the match parameter
$urlRouterProvider.when(/aspx/i, '/index');
})

Resources