The question:
Why does angular make you assign a controller to a route?
My thoughts:
Why not just have the controller on the template? Specifically the highest level of that template. What am I missing?
Based on my understanding this way makes more sense!
What I am currently doing:
When the template is rendered it comes with an ng-controller attribute on the highest level (I am using this! ). Is there something going on under-the-hood that I should be reading?
When you declare a simple pair of template-controller like
$routeProvider.when('/', {
templateUrL: '...',
controller: '...'
});
you may painlessly declare your controller in the template. There's no 'under-the-hood' behavior and the final result will be exactly the same. But issues start rising when you need something more complex.
First of all, imagine that your controller requires some condition to be satisfied before in can start.
$routeProvider.when('/', {
templateUrl: '...',
resolve: {
//do something before your controller kicks off
},
controller: '...',
});
This resolve condition might be whatever you want it to be. Say, require user to be logged in. You may handle this logic inside the controller but once you need any other route to resolve same condition you will have to duplicate your code.
Another feature that $routeProvider expose in configuration is controllerAs syntax.
See 'route' section in 'parameters' for further info http://docs.angularjs.org/api/ng/directive/ngController
Related
I'm trying to create a BaseController which my controllers can extend to handle the history action for all models. Currently (for the other routes), I set it up like so:
$routeProvider.when('/', {
redirectTo: '/static/campaigns'
}).when('/static/campaigns/new', {
templateUrl: 'campaigns/new.html',
controller: 'CampaignNewController'
}).....
What I want to do for the history routes would be a general route which would cover all models, but I'm not sure if there's a way to use the route parameters within the setup like below.
when('/static/history/:model_name', {
templateUrl: model_name + '/history.html',
controller: model_name + 'Controller'
})
If you look at documentation :
https://docs.angularjs.org/api/ngRoute/provider/$routeProvider#when
you may notice that, templateurl can be a string or a function. Perhaps if you created a function on the right hand side of templateUrl and injected $routeParams and read the value of model_name and returned a string with model_name, it might just work.
You wont be able to do the same for controller; however, you will have to create a service, that takes model_name as input and does the same logic as multiple controllers would.
I am trying to have dynamic routing in Angular 1.3. Something similar to what it described here and here. The examples suggest something like this:
$routeProvider.when('/:group/:pagename', {
controller: 'RouteCtrl',
templateUrl: 'uirouter.html'
})
and then the controller will have access to group and pagename through $routeParams. That works
I am trying to make this routing a little more dynamic and have template and controller to be selected dynamically as well:
$routeProvider.when('/:group/:pagename', {
controller: $routeParams.group + 'Ctrl',
templateUrl: $routeParams.pagename + '.html'
})
When I put a breakpoint on when I can see that there is $get property with a function that has $routeParams as one of parameters. But I can't figure out how to retrieve its values.
The project is in very early stage - I can go with ui-router or with ng-router if any of them has this functionality.
For the dynamic templateUrl portion you could try:
$routeProvider.when('/:group/:pagename', {
controller: "SomeCtrl",
templateUrl: function(params){return params.pagename + '.html';}
})
Not sure if the same could be done with the controller however.
Instead of a direct value you can declare a function that will return a templateUrl string, i.e.:
$routeProvider.when('/:group/:pagename', {
controller: $routeParams.group + 'Ctrl',
templateUrl: function (routeParams) {
return routeParams.pagename + '.html';
}
});
I guess the same may be true for controller, but you have to test that one out, as I've never used it for controllers.
That being said, if you have so much dynamic logic in this place, maybe instead of treating this as different controllers, you could encapsulate those views as different directives and the ng-if certain directive depending on $routeParams which will be set in one wrapping controller?
I have a large angular app that lives at a URL like so:
http://myangularapp.com/app/index.html#/
The URL I intend on giving out will need a name injected into the URL as this one site will support multiple users.
So, without modifying any directory structure, I'd like to do:
http://myangularapp.com/app/bob/index.html#/
and also:
http://myangularapp.com/app/harry/index.html#/
All my controllers and functionality would ideally stay the same.
Super stumped on this one!
Use the $routeProvider service. You can define where urls go, like so.
$routeProvider.
when('/app/foo/bar/what/about/bob', {
templateUrl: 'app/bob/index.html', <---directory and url don't have to match
controller: 'bobController'
}).
when('/app/harry', { ...
})
I have a AngularJS Application in different languages.
Now I want to preselect a language, when the user calls the site, with following string at the end:
/en, /de, ...
Is this even possible in AngularJS? I also can use some other syntax, if this is needed.
Thank you very much!
If you are wanting to get something from the url to run logic on (i.e. /de in the url)
you can use the $location object
Angular location
If you are wanting dynamic urls so that things like /de, /fr, /es go to the same page/view, you'll need to use the $route object
Angular routes
With the limited amount I could understand from your question..
var app=angular.module("angularapp",['ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/',{
redirectTo: '/en'
})
.when('/:language',{
//do something
});
}]);
you can then get access to {"language":"en"} object
My AngularJS needs to load a mapping (from my API) that is needed by the rest of the application to continue to make API calls. My current solution is saving the Promise that is used to load the map and having making every future API call using promise.then(...). Is this the right solution? Is it ok to keep a promise around and repeatedly call .then() on it?
As #blackhole noted, Promise.then() on an already-resolved promise is fast. It's not zero work, but it's just a quick check.
But if this data is a pre-requisite for your application, it seems a terrible burden in CODE to have to check it every time an API call needs to be made. What if you add a second pre-requisite? It's really messy to have to check this every single time in the future.
Both ngRoute and uiRouter allow you to require a resolved promise before starting a controller. They're great patterns for this in an Angular app because you can be granular - you can have a lot of smaller pre-requisites through the app that need to be resolved before those views start.
Here's a sample for ngRoute:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: 'main.html',
resolve: {
init: ['MyService', function(MyService {
return MyService.getMyData();
}]
}
});
}]);
and here's one for uiRouter:
getMyData.$inject = ['MyService'];
function getMyData(MyService) {
return MyService.getMyData();
}
$stateProvider.state('home', {
url: '^/',
templateUrl: 'main.html',
controller: 'MainCtrl',
resolve: {
myData: getMyData
}
});
Since most apps want some form of routing anyway, it's an easy pattern to let the router do this for you, but as #Dave Newton pointed out, you could also do this with .run() if you want to roll-your-own.
If you cannot or do not want to use one of the routing mechanisms, you can also manually trigger Angular's bootstrap after loading the asset. Documentation for this is provided here:
https://docs.angularjs.org/guide/bootstrap
But basically you're doing this:
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
Then you remove the ng-app directive from your top-level DOM element, wherever you put it.