Match url.html and url.html/ in Angularjs ui-router - javascript

What is a nice way to match url for both /url.html and /url.html/ in Angular.js ui-router?
For now, they are separate like this in my code:
$stateProvider
.state("myUrl1", {
url: "/",
resolve: {
init: function() {
console.log("Triggered resolve myUrl1 /");
return true;
}
}
})
.state("myUrl2", {
url: "^",
resolve: {
init: function() {
console.log("Triggered resolve myUrl2");
return true;
}
}
});
I have tried [^/]*} in the doc https://github.com/angular-ui/ui-router/wiki/URL-Routing but no success.
Any pointer?

AngularUI Router is a routing framework for AngularJS, which allows you to organize the parts of your interface into a state machine. Unlike the $route service in Angular core, which is organized around URL routes, UI-Router is organized around states, which may optionally have routes, as well as other behavior, attached.
You are asking about how to implement a URL route using a plugin that is not designed for handling routes in this way. If you are only interested in URL routing, you should use the Angular core $routeProvider.
http://docs.angularjs.org/api/ngRoute
Also, Angular routes don't normally include .html. And, your two example routes /url.html and /url.html/ appear to be semantically the same.

Related

Grouping routes with Flow Router in Meteor

In Flow Router, I have some routes
/projects/project-name
/projects/project-name/tasks
/projects/project-name/tasks/deleted-tasks
/projects/project-name/tasks/completed-tasks
/projects/project-name/tasks/labels/school
/projects/project-name/tasks/labels/football
/projects/project-name/tasks/labels/training
/projects/project-name/tasks/labels/personal
[...]
So almost all of my routes should share most of the same characteristics.
Are there any tricks to group my routes, so I do now have to check if the project exists in every single route or if can I say that some routes build upon other routes, so I do not have to write the long paths for all the routes?
I have found Flow Router, but it doesn't seem that's right tool to accomplish what I need.
Flow router definitely has the ability to group your routes. You can group them as follows -
var projectRoutes = FlowRouter.group({
prefix: '/projects/project-name',
name: 'projects',
});
To handle routers within this group, you can add
// route for /projects/project-name
projectRoutes.route('/', {
action: function() {
BlazeLayout.render(...);
}
});
// route for /projects/project-name/tasks
projectRoutes.route('/tasks', {
action: function() {
BlazeLayout.render(...);
}
});
This is just an example for grouping your routes.
You can read more here.

lazy loading of components in angular with browserify

I am in process of outlining a architecture of a fairly complex application based on angular. So I started with the angular-seed project and it seems to be a good starting point. What bothers me is that angular apps by nature involves loading everything upfront. With script loaders, there doesn't seems to be a clean way around.
I came from a backbone.js background and there it was quiet straight to use the require.js for lazy loading based on the router callbacks. In angular routes are somewhat defined like the below:
// Declare app level module which depends on views, and components
angular.module('myApp', [
'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when({templateURL:'../tmpl.html',controller:'view1Ctrl'})
.when({templateURL:'../tmpl.html',controller:'view1Ctrl'})
.otherwise({redirectTo: '/view1'});
}]);
now here, $routeProvider.when({templateURL:'../tmpl.html',controller:'view1Ctrl'}) I would like to lazily load the controller and the template. I was tempted to use something like:
$routeProvider.when({templateURL:'../tmpl.html',controller:require('view1Ctrl'})
using browserify but then it doesn't seems to be clean and not even with require. I know this question has been asked several times on SO some way or the other but i haven't found a emphatic answer to this.
My preference here is to use the browserify as it supports the much loved cjs modules in browser.
I'm not sure how to do this with Browserify, as I've never tried it myself but I would strongly recommend you look into ocLazyLoad.
As a standalone service, it works wonders with loading files (json, css, js, templates - you name it) and injecting it into your already running angular application.
With that said, it works even better(imo) coupled with a router (the default angular one, or ui-router).
There are some 'seed projects' that showcase how one could do it with ocLazyLoad coupled with SystemJS.
https://github.com/Swimlane/angular-systemjs-seed
https://github.com/lookfirst/systemjs-seed
https://github.com/lookfirst/ocLazyLoad-SystemJS-Router
https://github.com/kasperlewau/ng-jspm-seed
But you don't even need that.
If you go with ui-router, ui-router-extras and ocLazyLoad you can put something like this together to lazy load states:
main.js
/**
* Inject the needed dependencies into our main module.
*/
var main = angular.module('main', [ 'ui.router', 'ct.ui.router.extras.future', 'oc.lazyLoad' ]);
/**
* Define the lazy loaded states.
*/
main.constant('lazyLoadedStates', [
{
name: 'about',
url: '/about',
type: 'lazy',
src: [
'/path/to/about.module.js',
'/path/to/AboutController.js'
]
}
]);
/**
* Setup the behaviour for when we hit a futureState with the 'lazy'
* type.
*
* 1. Setup a deferred object.
* 2. Resolve the promise when every file defined in the futureState.src has been loaded.
* 3. Return the promise.
*/
main.config(function ($futureStateProvider, lazyLoadedStates) {
$futureStateProvider.stateFactory('lazy', function ($q, $ocLazyLoad, futureState) {
var deferred = $q.defer();
$ocLazyLoad.load(futureState.src).then(function () {
deferred.resolve();
});
return deferred.promise;
});
lazyLoadedStates.forEach($futureStateProvider.futureState);
});
That's the 'framework' out of the way - now you just need to keep adding more modules, with more code, and match a real state definition with the dummy one in the lazyLoadedStates constant.
about.module.js
/**
* Setup the _real_ '/about' state in this lazy loaded file.
*/
angular.module('about', []).config(function ($stateProvider) {
$stateProvider.state('about', {
url: '/about',
controller: 'AboutController',
template: 'some_template.html'
});
});
AboutController.js
/**
* Register the AboutController in a lazy loaded file. This could be done in about.module.js aswell,
* but we'll do it here to separate stuff and showcase loading of multiple files.
*/
angular.module('about').controller('AboutController', function ($state) {
console.log('Im on a lazy loaded state!', $state.current);
});
I hope that gives you the rough idea of how to setup lazy loaded states in Angular. I have yet to find an easier way to do this, but I'm sure there are articles out there on how to couple ui-router (or the default angular router) with some other 'lazy-loader'.
Doc links:
ocLazyLoad
ui-router-extras#future

AngularJS and Webpack Integration

I am looking for some help with using webpack for a large AngularJS application. We are using folder structure based on feature (each feature/page has a module and they have controllers, directives). I have successfully configured webpack to get it working with Grunt, which produces one single bundle. I want to create chunks as its going to be a large app, we would like to load modules (page/feature) artifacts asynchronously.
I am going through some of the webpack example to use 'code splitting' using require([deps],fn ) syntax. However I couldn't get the chunks lazy-loaded. First of all, I don't know where exactly, I would need to import these chunks before AngularJS would route the user to next page. I am struggling to find a clear separation of responsibility.
Did someone point me to an example AngularJS application where webpack is used to load controllers/directives/filters asynchronously after each route?
Few of the links I am following:
Should I use Browserify or Webpack for lazy loading of dependancies in angular 1.x
https://github.com/petehunt/webpack-howto#9-async-loading
http://dontkry.com/posts/code/single-page-modules-with-webpack.html
Sagar Ganatra wrote a helpful blog post about code splitting.
Suprisingly code splitting isn't really supported by angular's module system. However, there is a way to achieve code splitting by saving a reference to angular's special providers during the config-phase.
[...] when Angular initializes or bootstraps the application, functions - controller, service etc,. are available on the module instance. Here, we are lazy loading the components and the functions are not available at a later point; therefore we must use the various provider functions and register these components. The providers are available only in the config method and hence we will have to store a reference of these providers in the config function when the application is initialized.
window.app.config([
'$routeProvider',
'$controllerProvider',
'$compileProvider',
'$filterProvider',
'$provide',
function ($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
$routeProvider.when('/login', {
templateUrl: 'components/login/partials/login.html',
resolve: {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var deferred = $q.defer();
// lazy load controllers, etc.
require ([
'components/login/controllers/loginController',
'components/login/services/loginService'
], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
});
//store a reference to various provider functions
window.app.components = {
controller: $controllerProvider.register,
service: $provide.service
};
}
]);
Now inside your loginController for instance you write
app.components.controller('loginController');
to define your new controller lazily.
If you want to lazy-load your templates too I recommend to use the ui-router. There you can specify a templateProvider which is basically a function to load templates async
This is a quote from
https://github.com/webpack/webpack/issues/150
webpack is a module bundler not a javascript loader. It package files from local disk and don't load files from the web (except its own chunks).
Use a javascript loader, i. e. script.js
var $script = require("scriptjs");
$script("//ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js", function() {
// ...
});

Angular $routeProvider override-able fallback routes

I'm creating my first sizable angular project, so I've opted to break out my angular application code into directories like so:
app/
calendar/
calendar.js
contacts/
contacts.js
companies/
companies.js
...
app.js
Each of these directories will include a handful of files for views (.html), directives, services, etc.
In my app.js file, I want to setup default routes that will be used maybe 80% of the time, like this:
$routeProvider
.when('/:page', {
templateUrl: function($routeParams) {
return 'app/' + $routeParams.page + '/index.html';
},
controller: 'PageController'
})
However, I want the ability to "override" that route from within my modules so I can do things like swap out the controller, do dependency injection stuff, and so on. I would like that sort of logic contained within the module itself to keep things...well...modular, so I would rather not define each module's routing logic within app.js. So for example, in my app/calendar/calendar.js file I want to say:
$routeProvider
.when('/calendar', {
templateUrl: 'app/calendar/index.html',
controller: 'CalendarIndexController',
resolve: { ... }
})
The problem is that the definition in app.js is matched against the location first, so this calendar route is never used.
To achieve this, right now I'm just including an extra file after all of my module javascript files that sets up my fallback routes last, but this seems a little clunky. Is there anyway to define the routes within app.js so that they are override-able?
You might be able to adapt this technique for dynamically loading controllers described by Dan Wahlin here http://weblogs.asp.net/dwahlin/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs combined with your default routes where the parameter controls which view to load. You could pass the same page parameter to the resolve function to control which combination of view and controller to serve up?

How to split AngularJS application into smaller modules and handle routing correctly?

What would be the best way to split AngularJS application into smaller pieces/module? For example if I have a blog post and commenting enabled for that, I think I could break it down to modules like "posts" and "comments"(?) (maybe not the best example, but the idea is to split the application logic into separate modules rather than building a huge one-module-app).
I've tried to bootstrap both modules in the separate DOM nodes and use routing in both of the modules accordingly. There are few problems:
As a "single-page" application I'm bootstrapping comments module to be used even on the front page even though it's not used there.
Since I'm not able to use multiple ng-views inside ng-app, I'm forced to write all the wrappers for my modules in the index.html view and bootstrap them? Should it be like that? Seems a bit wrong. How/where should I bootstrap those?
Are there any tips for the routing? Should I spread those in the modules or should I combine them all together somehow? (creating one "blog" module to include "posts" and "comments" modules as dependencies would still make it hard to define for example the "/post/:id" routing..?)
index.html
<div class="post"><ng-view></ng-view></div>
<div class="comments"><ng-view></ng-view></div>
javascript.js
angular.module('posts', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
'template': 'Showing all the posts',
'controller': 'postCtrl
})
.when('/post/:id', {
'template': 'Showing post :id',
'controller': 'postCtrl
});
}]);
angular.module('comments', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/post/:id', {
'template': 'Showing post :id comments',
'controller': 'CommentsCtrl'
});
}]);
angular.bootstrap($('.post'), ['posts']);
angular.bootstrap($('.comments'), ['comments']);
I would divide the app in "view modules" and these sub modules.
Then I use the $routeProvider to switch between the views. I define the different routing config in each module.
If I need further submodules, I load these with ng-include.
/* App Module */
angular.module('MyApp', ['MyApp.home', 'MyApp.blog'])
.config( function myAppConfig ( $routeProvider ) {
'use strict';
$routeProvider.otherwise({ redirectTo: '/home' });
});
/* home Module */
angular.module('MyApp.home', [])
.config(['$routeProvider', function config( $routeProvider ) {
$routeProvider.when('/home', {
controller: 'HomeController',
template: '<p>This is my Home</p>'
});
}]);
I created a little repository on github to explain this.
You can define routes in the submodules:
angular.module('app', ['ngRoute', 'app.moduleX'])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/home', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
});
//Handle all exceptions
$routeProvider.otherwise({
redirectTo: '/home'
});
})
angular.module('app.moduleX', []).config(function($routeProvider) {
$routeProvider.when('/settings', {
templateUrl: 'partials/settings.html',
controller: 'SettingsCtrl'
});
})
I also wrote a blog post about this topic.
We're doing something similar with a portal app and sub-apps. A few things we've discovered:
Only one "app" can have routes and routeParams. Because of this, if the "sub-app" needs access to the $routeParams, you either have to go "old school" for URL parsing, or use an event service.
Speaking of events, there is no Angular service for the apps to communicate, so you'll need to roll your own event service talking to root scope for both apps and inject it into both apps.
I can't see where we used ng-view for the "sub-app". Apparently bootstrapping directly to an element works similarly.
Because only one app can have routes, the apps should be bootstrapped in order. So something like this:
$( function () {
$.when(angular.bootstrap($('.post'), ['posts'])).done( function() {
console.log('POSTS: bootstrapped');
//Manually add the controller to the comments element. (May or may not be
//necessary, but we were doing something that required it to work.)
$('.comments').attr('ng-controller', 'CommentsCtrl');
$.when(angular.bootstrap($('.comments'), ['comments'])).done( function() {
console.log('COMMENTS: bootstrapped');
});
});
});
I hope you can use "ui-router" routing module.
Here is good tutorial for this http://www.ng-newsletter.com/posts/angular-ui-router.html

Categories

Resources