"Cannot read property 'untilResolved' of undefined" in angular-route-segment? - javascript

I am setting up a simple routing configuration with angular-route-segment and Angular is throwing this error on app load:
TypeError: Cannot read property 'untilResolved' of undefined
Neither Angular nor angular-route-segment provide anything more helpful in the way of error messages.
app.js:
(function () {
'use strict';
var app = angular.module('emailHandlerApp', [
// Angular modules
'ngAnimate', // animations
'ngRoute', // routing
'ngSanitize', // sanitizes html bindings (ex: topnav.js)
// 3rd Party Modules
'ui.bootstrap', // ui-bootstrap (ex: carousel, pagination, dialog)
'route-segment', // angular-route-segment
'view-segment', // angular-route-segment
]);
console.log("app loaded"); // just for debug!
})();
config.route.js:
(function () {
'use strict';
var app = angular.module('emailHandlerApp');
// Configure the routes and route resolvers
app.config(function ($routeSegmentProvider, $routeProvider) {
$routeSegmentProvider.options.autoLoadTemplates = true;
$routeSegmentProvider.options.strictMode = true;
$routeSegmentProvider
.when('/', 'emailHandler')
.when('/unsubscribe', 'emailHandler.unsubscribe')
.when('/redirect', 'emailHandler.redirect')
// Base shell
.segment('emailHandle', {
templateUrl: 'app/shell.html',
controller: 'baseController',
controllerAs: 'vm',
})
.within()
// Dashboard
.segment('unsubscribe', {
templateUrl: 'app/unsubscribe/unsubscribe.html',
controller: 'unsubscribeController',
controllerAs: 'vm',
dependencies: ['emailId']
})
// Recruiting
.segment('redirect', {
templateUrl: 'app/redirect/redirect.html',
controller: 'redirectController',
controllerAs: 'vm',
dependencies: ['rdId']
})
;
$routeProvider.otherwise({ redirectTo: '/' });
})
})();

Answering my own question because I'm 99% sure I'll probably Google this again a month from now.
This one is super simple - just a matter of code blindness and a slightly uninformative error message. angular-route-segment throws this when there's a name out of place somewhere in your segment tree.
In my case, I had mistyped .segment('emailHandle', { ... }) when I meant emailHandler, and then it barfed once it hit the .within(). I felt pretty dumb once I saw it.

Related

Loading modules in Angularjs

I have a problem trying to load some modules.
controller1.js:
angular.module('LPC')
.controller('lista_peliculas_controller', ['$scope', function($scope) {
$scope.hola="hola peliculas";
}]);
And app.js:
var app = angular.module('mis_peliculas', []);
app.config(function($routeProvider){
$routeProvider
.when("/pagina_principal",{
templateUrl: "views/pagina_principal.html",
controller: "lista_peliculas_controller"
})
.when("/lista_peliculas",{
templateUrl: "views/lista_peliculas.html",
controller: "lista_peliculas_controller"
})
.when("/lista_series",{
templateUrl: "views/lista_series.html",
controller: "lista_series_controller"
})
.otherwise({
redirectTo: "/pagina_principal"
})
});
The console says that there is a problem with the injector.
Can you find the error?
You must add angular-route.js . Reference
The ngRoute module provides routing and deeplinking services and
directives for AngularJS apps.
How do you fix it?
var app = angular.module('mis_peliculas', ['ngRoute','LPC']);
And
angular.module('LPC', [])
Update your code to use right module name:
angular.module('mis_peliculas')
.controller('lista_peliculas_controller', ['$scope', function($scope) {
$scope.hola="hola peliculas";
}]);
and if you want to use separate modules, you need to initiliaze it first and inject it into your main module
angular.module('LPC',[])
.controller('lista_peliculas_controller', ['$scope', function($scope) {
$scope.hola="hola peliculas";
}]);
var app = angular.module('mis_peliculas', ['LPC']);
I assume your routing is already set correctly.
without the complete log of the error i can't be more precise, but i think that the injection error could be related to your module not being instantiated.
try to change
angular.module('LPC') //here you get a reference to a module, that could cause your error
to
angular.module('LPC', []) //here you instantiate a module
You need to pass in the 'LPC' Module to your app 'mis_peliculas' module for it use the 'lista_peliculas_controller' Controller which is in 'LPC' Module.
Try this code
angular.module('LPC',[])
.controller('lista_peliculas_controller', ['$scope', function($scope) {
$scope.hola="hola peliculas";
}]);
This should be your controller1.js and should be defined before you define your app. Now the app.js should look like
var app = angular.module('mis_peliculas', ['LPC']);
app.config(function($routeProvider){
$routeProvider
.when("/pagina_principal",{
templateUrl: "views/pagina_principal.html",
controller: "lista_peliculas_controller"
})
.when("/lista_peliculas",{
templateUrl: "views/lista_peliculas.html",
controller: "lista_peliculas_controller"
})
.when("/lista_series",{
templateUrl: "views/lista_series.html",
controller: "lista_series_controller"
})
.otherwise({
redirectTo: "/pagina_principal"
})
});
This should remove all the errors and you should be able to use the controller from other module.
Hope it helps

Link module from external file in AngularJS

I'm working on new project and I want to use two modules placed in separated files. Unfortunetaly, when I try to declare second in my root one I receive an error
Uncaught Error: [$injector:modulerr]
So, here is my root file, app.js:
var beerMe = angular.module("beerMe", ["admin", "ngRoute"])
.config(["$routeProvider", function($routeProvider) {
$routeProvider
.when("/home", {
templateUrl: "../views/home/home.html",
controller: "MyCtrl"
})
.otherwise({
redirectTo: "/home"
})
}])
.controller("MyCtrl", ["$scope", function($scope) {
}]);
And second one, which I want to bind, admin.js:
var admin = angular.module("admin", ["firebase", "ngRoute"]);
admin.config(["$routeProvider", function($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "views/add.html",
controller: "AdminCtrl"
}).when ("/edit", {
templateUrl: "views/edit.html",
controller: "AdminCtrl"
}).otherwise({
redirectTo: "/"
})
}]);
admin.controller("AdminCtrl", ["$scope", "$firebaseArray", function($scope, $firebaseArray) {
// my code goes here
}]);
In admin.js I connnect to firebase and I want to have access to all data from my module "beerMe".
I'd be very grateful if you could help me to figure out why there's a problem with binding these two.
Change var admin = angular.module("admin", ["firebase", "ngRoute"]);
to var admin = angular.module("admin");
and change root module like this
var beerMe = angular.module("beerMe", ["admin", "ngRoute", "firebase"])
It looks like your dependencies are the wrong way round. Having admin as a dependency of beerMe will not make the beerMe module available in your admin module.
If you want your admin module to be able to access data from inside the beerMe module then beerMe should be a dependency of admin.
var admin = angular.module('admin', ['firebase', 'ngRoute', 'beerMe']);

Basic Angular app using ui-router won't load login

I'm working on a POC for work and I can't find whatever stupid mistake is hiding here. I have a bare-bones/basic angular app, using ui-router. Plunker can be found here.
In the chrome console I can see my app.module is being created, however the login page never appears. This is a very, very basic app so I figure it must be something simple I'm missing. Any suggestions? Below is a quick sample:
config.js
(function() {
'use strict'
var app = angular.module('app.core');
app.config(AppRouter);
AppRouter.$inject = ['$stateProvider', '$urlRouterProvider'];
function AppRouter($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('login');
$stateProvider
.state('/', {
templateUrl: 'login.html',
controller: 'LoginController',
controllerAs: 'vm',
})
.state('login', {
templateUrl: 'login.html',
controller: 'LoginController',
controllerAs: 'vm',
});
}
})();
login.controller.js
(function() {
'use strict';
var app = angular.module('app.login');
app.controller('LoginController', LoginController);
LoginController.$inject = ['$location', '$filter', '$window', '$rootScope'];
function LoginController($location, $filter, $window, $rootScope) {
var init = function() {
console.log('here');
};
init();
}
})();
app.module.js
(function() {
var app = angular.module('app', ['app.core', 'app.login']);
})();
login.module.js
(function(){
'use strict'
var app = angular.module('app.login', ['app.core']);
})();
core.module.js
(function(){
'use strict'
var app = angular.module('app.core', ['ui.router']);
})();
After few changes it worked for me (see forked plunk: https://plnkr.co/edit/D5AL8DTnqYI2g23wjE7W?p=preview):
You forgot to load AngularJS (between <head></head).
You forgot to add ng-app, I add it on body.
And I made some changes in config.js:
function AppRouter($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('/', {
templateUrl: 'login.html',
controller: 'LoginController',
controllerAs: 'vm',
})
.state('login', {
url: '/login',
templateUrl: 'login.html',
controller: 'LoginController',
controllerAs: 'vm',
});
}
I add url to login state, and I made changes to otherwise function: I changed login to /login cause it needs URL, not state name.
I think you overconfigured you app a little bit (too much modules), and it makes it hard to read for me, but it's only my opinion and to be frankly, maybe it's only because I don't use modules very frequently. Try to add this changes to your Plunk and please write if it won't work, casue I could accidentally skip something.

how to call angular factory method from module setup?

I have a module App and factory i18n, what is the best way to call i18n.load
method form App (config? run? etc?)
angular
.module('App', [
'ngRoute',
'service.i18ndb'
])
.config(function ($routeProvider) {
//want to i18n.load() here somehow
$routeProvider
.when('/signin', {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl'
})
.when('/mix', {
templateUrl: '../views/mix.html',
controller: 'MixCreateCtrl'
})
.otherwise({
redirectTo: '/signin'
});
});
angular.module('App')
.factory('service.i18ndb', function() {
return {
load: function() { console.log("Busy"); }
}
}
);
The problem you will always have if you use .run is having to deal with a page that has no i18n loaded. This means you will need to have a way to deal with your view when their is no i18n loaded. You can either hide it or the text will flash with the wrong values at first.
However, AngularJS gives you a wonderful feature to make sure it is loaded before your view is loaded: the resolver!
Here is how to do it.
var i18nResolver = function(service.i18ndb) {
return service.i18ndb.promise;
};
$routeProvider
.when('/signin' {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl',
resolve: {
i18n: i18nResolver
}
});
You can fix this code to use the correct promise of your HTTP request or whatever service you are using.
One of the benefits of using this way is you can have a different labels for a different page for your i18n and use the i18n service to recover them no matter where you are.
You are defining your app module twice. One you create your factory, it can be injected to the controller and used there. You could try something like this:
angular.module('App', ['ngRoute','service.i18ndb'])
.factory('service.i18ndb', function() {
return {
load: function() { console.log("Busy"); }
}
})
.config(function ($routeProvider) {
//want to i18n.load() here somehow
$routeProvider
.when('/signin', {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl'
})
.when('/mix', {
templateUrl: '../views/mix.html',
controller: 'MixCreateCtrl'
})
.otherwise({
redirectTo: '/signin'
});
})
.controller('SigninCtrl', function($scope, service.i18ndb) {
// Call your factory function here
service.i18ndb.load();
// If the function returns a value you could assign it to a scope
// variable so it can be used in your template 'sign-in.html'
$scope.your_variable = service.i18ndb.load();
});
angular
.module('App', [
'ngRoute'
])
.config(function ($routeProvider) {
//want to i18n.load() here somehow
$routeProvider
.when('/signin', {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl'
})
.when('/mix', {
templateUrl: '../views/mix.html',
controller: 'MixCreateCtrl'
})
.otherwise({
redirectTo: '/signin'
});
})
.run(['i18ndb', function(i18ndb) {
i18ndb.load();
}])
.factory('i18ndb', function() {
return {
load : function() {console.log('test')}
};
});
);
You were requiring a module which has not been defined (as far as I can tell). The factory you were adding was on the 'App' module not the 'service.i18ndb'.
You then need to dependency inject the i18ndb factory in to the run method to call it from there (presuming that you want to call that function to bootstrap your app).

AngularJS dynamic routing

I currently have an AngularJS application with routing built in.
It works and everything is ok.
My app.js file looks like this:
angular.module('myapp', ['myapp.filters', 'myapp.services', 'myapp.directives']).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', { templateUrl: '/pages/home.html', controller: HomeController });
$routeProvider.when('/about', { templateUrl: '/pages/about.html', controller: AboutController });
$routeProvider.when('/privacy', { templateUrl: '/pages/privacy.html', controller: AboutController });
$routeProvider.when('/terms', { templateUrl: '/pages/terms.html', controller: AboutController });
$routeProvider.otherwise({ redirectTo: '/' });
}]);
My app has a CMS built in where you can copy and add new html files within the /pages directory.
I would like to still go through the routing provider though even for the new dynamically added files.
In an ideal world the routing pattern would be:
$routeProvider.when('/pagename', { templateUrl: '/pages/pagename.html', controller: CMSController });
So if my new page name was "contact.html" I would like angular to pick up "/contact" and redirect to "/pages/contact.html".
Is this even possible?! and if so how?!
Update
I now have this in my routing config:
$routeProvider.when('/page/:name', { templateUrl: '/pages/home.html', controller: CMSController })
and in my CMSController:
function CMSController($scope, $route, $routeParams) {
$route.current.templateUrl = '/pages/' + $routeParams.name + ".html";
alert($route.current.templateUrl);
}
CMSController.$inject = ['$scope', '$route', '$routeParams'];
This sets the current templateUrl to the right value.
However I would now like to change the ng-view with the new templateUrl value. How is this accomplished?
angular.module('myapp', ['myapp.filters', 'myapp.services', 'myapp.directives']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/page/:name*', {
templateUrl: function(urlattr){
return '/pages/' + urlattr.name + '.html';
},
controller: 'CMSController'
});
}
]);
Adding * let you work with multiple levels of directories dynamically.
Example: /page/cars/selling/list will be catch on this provider
From the docs (1.3.0):
"If templateUrl is a function, it will be called with the following
parameters:
{Array.} - route parameters extracted from the current
$location.path() by applying the current route"
Also
when(path, route) : Method
path can contain named groups starting with a colon and ending with a star: e.g.:name*. All characters are eagerly stored in $routeParams under the given name when the route matches.
Ok solved it.
Added the solution to GitHub - http://gregorypratt.github.com/AngularDynamicRouting
In my app.js routing config:
$routeProvider.when('/pages/:name', {
templateUrl: '/pages/home.html',
controller: CMSController
});
Then in my CMS controller:
function CMSController($scope, $route, $routeParams) {
$route.current.templateUrl = '/pages/' + $routeParams.name + ".html";
$.get($route.current.templateUrl, function (data) {
$scope.$apply(function () {
$('#views').html($compile(data)($scope));
});
});
...
}
CMSController.$inject = ['$scope', '$route', '$routeParams'];
With #views being my <div id="views" ng-view></div>
So now it works with standard routing and dynamic routing.
To test it I copied about.html called it portfolio.html, changed some of it's contents and entered /#/pages/portfolio into my browser and hey presto portfolio.html was displayed....
Updated
Added $apply and $compile to the html so that dynamic content can be injected.
I think the easiest way to do such thing is to resolve the routes later, you could ask the routes via json, for example. Check out that I make a factory out of the $routeProvider during config phase, via $provide, so I can keep using the $routeProvider object in the run phase, and even in controllers.
'use strict';
angular.module('myapp', []).config(function($provide, $routeProvider) {
$provide.factory('$routeProvider', function () {
return $routeProvider;
});
}).run(function($routeProvider, $http) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
}).otherwise({
redirectTo: '/'
});
$http.get('/dynamic-routes.json').success(function(data) {
$routeProvider.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
});
// you might need to call $route.reload() if the route changed
$route.reload();
});
});
In the $routeProvider URI patters, you can specify variable parameters, like so: $routeProvider.when('/page/:pageNumber' ... , and access it in your controller via $routeParams.
There is a good example at the end of the $route page: http://docs.angularjs.org/api/ng.$route
EDIT (for the edited question):
The routing system is unfortunately very limited - there is a lot of discussion on this topic, and some solutions have been proposed, namely via creating multiple named views, etc.. But right now, the ngView directive serves only ONE view per route, on a one-to-one basis. You can go about this in multiple ways - the simpler one would be to use the view's template as a loader, with a <ng-include src="myTemplateUrl"></ng-include> tag in it ($scope.myTemplateUrl would be created in the controller).
I use a more complex (but cleaner, for larger and more complicated problems) solution, basically skipping the $route service altogether, that is detailed here:
http://www.bennadel.com/blog/2420-Mapping-AngularJS-Routes-Onto-URL-Parameters-And-Client-Side-Events.htm
Not sure why this works but dynamic (or wildcard if you prefer) routes are possible in angular 1.2.0-rc.2...
http://code.angularjs.org/1.2.0-rc.2/angular.min.js
http://code.angularjs.org/1.2.0-rc.2/angular-route.min.js
angular.module('yadda', [
'ngRoute'
]).
config(function ($routeProvider, $locationProvider) {
$routeProvider.
when('/:a', {
template: '<div ng-include="templateUrl">Loading...</div>',
controller: 'DynamicController'
}).
controller('DynamicController', function ($scope, $routeParams) {
console.log($routeParams);
$scope.templateUrl = 'partials/' + $routeParams.a;
}).
example.com/foo -> loads "foo" partial
example.com/bar-> loads "bar" partial
No need for any adjustments in the ng-view. The '/:a' case is the only variable I have found that will acheive this.. '/:foo' does not work unless your partials are all foo1, foo2, etc... '/:a' works with any partial name.
All values fire the dynamic controller - so there is no "otherwise" but, I think it is what you're looking for in a dynamic or wildcard routing scenario..
As of AngularJS 1.1.3, you can now do exactly what you want using the new catch-all parameter.
https://github.com/angular/angular.js/commit/7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5
From the commit:
This allows routeProvider to accept parameters that matches
substrings even when they contain slashes if they are prefixed
with an asterisk instead of a colon.
For example, routes like edit/color/:color/largecode/*largecode
will match with something like this
http://appdomain.com/edit/color/brown/largecode/code/with/slashs.
I have tested it out myself (using 1.1.5) and it works great. Just keep in mind that each new URL will reload your controller, so to keep any kind of state, you may need to use a custom service.
Here is another solution that works good.
(function() {
'use strict';
angular.module('cms').config(route);
route.$inject = ['$routeProvider'];
function route($routeProvider) {
$routeProvider
.when('/:section', {
templateUrl: buildPath
})
.when('/:section/:page', {
templateUrl: buildPath
})
.when('/:section/:page/:task', {
templateUrl: buildPath
});
}
function buildPath(path) {
var layout = 'layout';
angular.forEach(path, function(value) {
value = value.charAt(0).toUpperCase() + value.substring(1);
layout += value;
});
layout += '.tpl';
return 'client/app/layouts/' + layout;
}
})();

Categories