I need to update a variable on a per template basis.
My main index file (the layout file) displays this value. I can use ng-init, but that only works if I call ng-init before the expression.
If I use ng-init on the template pages, it does not work (I suspect due to code order).
ng-ignit
<div ng-init="amount='4'"></div>
Index.html (layout file)
<span class="amount">{{amount}}</span>
Main_controller.js
$scope.amount = 1; //default value
How can I have multiple templates with different values of the amount variable?
There are so many ways of achieving what you desire. But the most relevant way to what you're looking for is using a resolve.
Use a resolve in your routeProvider
var sampleApp = angular.module('phonecatApp', []);
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/page1', {
templateUrl: 'templates/page1.html',
controller: 'myController',
resolve: {
data: 1
}
}).
when('/showOrders', {
templateUrl: 'templates/page2.html',
controller: 'ShowOrdersController',
resolve: {
data: 2
}
}).
otherwise({
redirectTo: '/page1'
});
}]);
In your controller
sampleApp.controller('myController', function($scope, data){
$scope.amount = data;
})
Related
I'm trying to setup one of my first angular projects and am having trouble getting to grips with the routing.
On page load I see the initial template that has been set by the preferencesDirective, which is great.
When I click the "Change Template" button I want it to change to another template but nothing happens. If I set the template url's in the $routeProvider to something invalid then I see a 404 error in the debugger which tells me something must be working but nothing happens when the template url is valid.. How do I get it to change correctly?
Thanks.
<div id="PreferencesApp" class="" ng-app="clientPreferencesModule">
<preferences-directive factory-settings="clientPreferences"></preferences-directive>
Change Template
</div>
<script>
var app = angular.module("clientPreferencesModule", ["ngResource", "ngRoute"]);
//var app = angular.module("clientPreferencesModule", ["ngRoute"]);
app.config(function ($routeProvider) {
$routeProvider.when("/", { controller: "clientPreferencesController", templateUrl: '/AngularTemplates/ClientPreferences/PreferencesTemplate.html' });
$routeProvider.when("/Preferences/:id", { controller: "clientPreferencesController", templateUrl: '/AngularTemplates/ClientPreferences/PreferencesTemplate.html' });
$routeProvider.when("/Preferences", { controller: "clientPreferencesController", templateUrl: '/AngularTemplates/ClientPreferences/PreferencesTemplate.html' });
$routeProvider.when("/Details", { controller: "clientPreferencesController", templateUrl: '/AngularTemplates/ClientPreferences/DetailsTemplate.html' });
});
app.controller('clientPreferencesController', clientPreferencesController);
clientPreferencesController.$inject = ["$scope", "$resource", "$rootScope", "$http", "$route", "$location"];
function clientPreferencesController($scope, $resource, $rootScope, $http, $route, $location) {
this.model = #Html.Raw(JsonConvert.SerializeObject(Model));
$scope.location = $location.path();
}
app.directive('preferencesDirective', preferencesDirective);
function preferencesDirective() {
return {
restrict: 'EA',
scope:
{
factorySettings: '='
},
controller: 'clientPreferencesController',
controllerAs: 'pc',
bindToController: true,
templateUrl: '/AngularTemplates/ClientPreferences/PreferencesTemplate.html'
}
}
</script>
For routing to work you've to create different views along with its associated controller & then have directive inside that view. And also you'll need ng-view directive in index.html in which all the routes view going to be loaded. And also preferencesDirective should only contain the reusable unique functionality, & the complete app view, so that you can have it different views with different data sets alongside different view components.
So, your template can be:
<div id="PreferencesApp" class="" ng-app="clientPreferencesModule">
Change Template
<div ng-view></div>
</div>
Now for different routes you can have each different controllers or if you want to handle it in one controller the have only one, but different from directive's controller, so it can be,
app.config(function ($routeProvider) {
$routeProvider.when("/", { controller: "viewController", templateUrl: '/AngularTemplates/ClientPreferences/PreferencesTemplate.html' });
$routeProvider.when("/Preferences/:id", { controller: "viewController", templateUrl: '/AngularTemplates/ClientPreferences/PreferencesTemplate.html' });
$routeProvider.when("/Preferences", { controller: "viewController", templateUrl: '/AngularTemplates/ClientPreferences/PreferencesTemplate.html' });
$routeProvider.when("/Details", { controller: "viewController", templateUrl: '/AngularTemplates/ClientPreferences/DetailsTemplate.html' });
});
Have preferencesDirective in all these templates. (This will now potentially change the directive's template but you can have changing dom of each view in views's templates & keep directive's template constant)
Now in viewController by making use of $routeParams you can check the current route & send different data to preferencesDirective's controller.
Now if you must want to change directives template conditionally then make use of ng-include inside directive's template.
function preferencesDirective() {
return {
restrict: 'EA',
scope:
{
factorySettings: '=',
templateSrc: '='
},
controller: 'clientPreferencesController',
controllerAs: 'pc',
bindToController: true,
templateUrl: '<ng-include src="pc.template()"></ng-include>'
}
}
function clientPreferencesController($scope, $resource, $rootScope, $http, $route, $location) {
this.model = #Html.Raw(JsonConvert.SerializeObject(Model));
$scope.location = $location.path();
$scope.template = function(){
if($scope.templateSrc) {
return '/AngularTemplates/ClientPreferences/'+ $scope.templateSrc + '.html';
}
}
}
Here share that templateSrc from viewController based on current route.
I have just started learning angularJS but i can notice the same thing so many time that at some places when we start writing a function in angularJS i noticed that some people define the function they are going to use like this
var mainApp = angular.module("mainApp", ['ngRoute']);
mainApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/addStudent', {
templateUrl: 'addStudent.htm',
controller: 'AddStudentController'
}).
when('/viewStudents', {
templateUrl: 'viewStudents.htm',
controller: 'ViewStudentsController'
}).
otherwise({
redirectTo: '/addStudent'
});
}]);
But the same function is working fine if we just write the function without this ['$routeProvider' like this
var mainApp = angular.module("mainApp", ['ngRoute']);
mainApp.config(function($routeProvider) {
$routeProvider.
when('/addStudent', {
templateUrl: 'addStudent.htm',
controller: 'AddStudentController'
}).
when('/viewStudents', {
templateUrl: 'viewStudents.htm',
controller: 'ViewStudentsController'
}).
otherwise({
redirectTo: '/addStudent'
});
});
I know there is no big difference when coming to code writing but still is there any difference in both the ways. If yes, then is it about minifying? and is there any negative point other that that of using it?
Thanks in advance!
mainApp.config(['$routeProvider', function($routeProvider) {
}]);
This type define a controller is callled Inline Array Annotation. And It is min-safe. min-safe mean if you minify your code then it will still work.
mainApp.config(function($routeProvider) {
});
This type of define a controller is called 'Implicit Annotation'. And its not min-safe. min-safe mean if you minify your code then it will not work.
And there a another way to declare a controller $inject Property Annotation
var MyController = function($scope, greeter) {
// ...
}
MyController.$inject = ['$scope', 'greeter'];
someModule.controller('MyController', MyController);
read more info click here
Wondering if there's an 'angular specific' way to try and achieve this.
I have a page with some views. When a user clicks an anchor, the views change, simple enough. What I'm curious is, if when the user clicks, is it possible to store a variable (say the span) then pass it to the $routeProvider to load the content a bit more dynamically?
so for anchor tag #name1, when that view is displayed, I can pass "name1" into nameHolder as a variable to load some files with same name.
HTML
<span>name1</span>
JS
var nameHolder = [];
var sampleApp = angular.module('sampleApp', ['ngRoute']);
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/' + nameHolder, {
templateUrl: 'templates/individkcd.html',
controller: 'individ',
nameofuser: nameHolder
}).
when('/', {
templateUrl: 'templates/home.html'
});
}]);
sampleApp.controller('individ', function($scope, $route) {
$scope.img = $route.current.nameHolder;
});
Thanks for any info.
UPDATED:
Solution at new thread.
angular js - passing a variable into $routeProvider
You need to add the parameter to your $routeProvider template:
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/' + nameHolder, {
templateUrl: 'templates/individkcd/:nameHolder',
controller: 'individ'
}).
when('/', {
templateUrl: 'templates/home.html'
});
}]);
Then in your target controller use $routeParams:
sampleApp.controller('individ', function($scope, $routeParams) {
$scope.img = $routeParams.nameHolder;
});
From the $routeProvider docs linked above:
path can contain named groups starting with a colon: e.g. :name. All
characters up to the next slash are matched and stored in $routeParams
under the given name when the route matches
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).
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;
}
})();