If I have a specific route parameter that is always being passed as a query string, is there a difference between retrieving the param using angular's $routeParams service and using its $location.$$search service? If so, is one preferable to the other?
URL: //localhost:80/app/profile?id=42
$routeParams approach:
app.controller('ProfileController', ['$routeParams', function($routeParams) {
console.log($routeParams.id);
}]);
$location approach:
app.controller('ProfileController', ['$location', function($location) {
console.log($location.$$search.id);
}]);
I am already injecting $location as a dependency in the controller that needs to perform the behavior detailed in the question. Additionally, the controller's module does not yet have a dependency on ngRoute.
If your route is of type:
when('page/:id')
then using $location search won't give you the ID in the search result. But $routesParams does.
But in case of query params ex:
when('page?key=value&key2=value2')
In this case, you can go for any of $location.search or $routeParams.
It also seems that the $location way is faster in some sense (which is only natural, since $routeParams uses $location to get its values).
To explain:
My site has a mode that's only meant to be used by kiosk tablets, whereas the normal use case is customers using their own devices. To differentiate, I initiate kiosk tablets by going to https://mysite.url/?kiosk, which triggers this (which runs on load):
if($routeParams.kiosk){
$cookieStore.put("kiosk", true);
}
Now, this tended to fail, since $routeParams hadn't had time to become initialized that close to load. Switching to
if($location.search().kiosk){
$cookieStore.put("kiosk", true);
}
mitigated that issue.
(My site is currently stuck on Angular 1.2.19, and was not put together by people who had all current best practices in mind. My example may or may not be relevant to modern projects coded by competent developers ;-) )
Related
I am quite new to to angular and most things and concepts are completely new for me.
I am working on a page that have a URL as follows:
http://domain/araneum/page/show/1
Is there any easy way to read the '1' from URL?
What I was able to understand there are two ways:
Read it on backend and create JS variable to be used by angular (not sure if that is the best practices)
Pass the parameter as hash. in my case it is not that convenient, because this page is quite complex already and I wanted to have a separate application for it. Also, there are couple of validations that must happen on backend before page rendering.
Is there any other better approach?
Thanks,
I recommend completing the AngularJs tutorial if you haven't already done so. In particular look at https://docs.angularjs.org/tutorial/step_07
Relevant code for you
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.phoneId = $routeParams.phoneId;
}]);
In this example the phoneId will be in place of 1 in your URL.
I have an Angular application. Its working good, but as my application is getting bigger I'm worried about the large number of dependencies that I have to inject in each controller.
for example
app.controller('viewapps',[
'$scope','Appfactory','Menu','$timeout','filterFilter','Notice', '$routeParams',
function($scope,Appfactory,Menu,$timeout,filterFilter,Notice,$routeParams) {
//controller code..
}])
I am sure that the list of dependencies are going to increase in future. Am I doing something wrong here? Is this the right approach? What is the best way to effectively handle this?
It's hard to be specific without an exact use case, or seeing the exact code in your controller, but it looks like your controller might be doing too much (or might end up doing too much as you add things later). 3 things you can do:
Delegate more of the logic to service(s) that are injected in.
Separate out into different controllers, so each only has (just about) 1 responsibility.
Separate out into directives, each with their own controllers and templates, and allow options to be passed in, and output given out, via attributes and the scope option of the directive. This is often my preferred option, as you end up building a suite of reusable components, each with a mini-API.
It is fine for directives to be used like this, at least in my opinion. They aren't just for handling raw Javascript events, or accessing the DOM directly.
I've been playing with the idea of bundling services based on controllers.
So in your example you'd refactor your; AppFactory, Menu, filterFilter and Notice services into a single service e.g. ViewAppsServices.
Then you'd use your services like ViewAppsServices.AppFactory.yourFunction().
As I see it that way you can at least shift your injections into another file cleaning your controller up a bit.
I think readability would suffer a bit since another developer would then have to look at bundles rather than the controller itself.
Here's a JSFiddle I put together to demonstrate how it would work; this is how I'd imagine yours would work.
.service('ViewAppsServices', ['AppFactory', 'Menu', 'filterFilter', 'Notice',
function (AppFactory, Menu, filterFilter, Notice) {
return {
AppFactory: AppFactory,
Menu: Menu,
filterFilter: filterFilter,
Notice: Notice
};
} ])
Try to move as much logic as possible to services, even just make controller methods act as "routing - passing through" methods . After time you will see it very usefull if you will want to use similar methods in other controllers/directives. Anyway, 7 injections is in my opinion not much :)
(edit: see the comment of Matt Way below)
Also, a tip - in newer versions of Angular you don't have to write this array, just:
app.controller('viewapps', function($scope,Appfactory,Menu, $timeout,filterFilter,Notice,$routeParams){
//controller code..
}])
My approach is to use $injector, when there are lots of dependencies:
app.controller('viewapps', ['$scope','$injector',function($scope,$injector){
var Appfactory = $injector.get('Appfactory');
var Menu = $injector.get('Menu');
//etc...
}]);
The advantages:
Code can be minified and obfuscated safely
You don't need to count the index of the dependency, when you declare dependency as a function's parameter
I have an existing angular project that has something really weird with the controller. It looks like the following.
app.controller('AppController', ['$scope', function ($scope) {
var app = app_application;
angular.extend($scope, app);
$scope.itTransports = app.state.itTransports;
}]);
I have a proof of concept for something I am trying to do on this fiddle and an attempt to mix the above controller and my concept in this fiddle but I cant seem to get it to work. I think it is something to do with the weird way the above controller works but I cant break it too badly and cant talk to the previous developer. I would think that if a combination cant be done then I would need some way for one controller to call another one but I want to make sure before I go down that path.
Edit: My main goal is to add functionality to check if the cookie exists. I am trying to do this in the same controller just for simplicity sake, but like I said before I am not adding a new one.
If you have some common functionality that you want to access from multiple controllers (e.g. checking if a cookie exists) then you should put that functionality into a service and inject that service into both controllers.
I'm using an angularJS and requireJS seed which you can download here: LINK
In that seed only the controllers that are called download the relevant controller which is then triggered. I've been trying to call a factory from my controller (with no luck) plus I would like the services/factories only to download the relevant factory if it has been called.
I've attempted to require a function within the factory method (much like the controller) but it is not working.
This is where I left off: Plunkr link
user971824.
I've put together something I call couchPotato that lazy-registers just about anything in angular using requirejs and the resolution features of $routeProvider (or any other router that does lazy promise-based resolution.
I've created a plunker based on yours demonstrating how you could do it with couchPotato. If you take a look at it, I think you'll see that it's a bit simpler because you don't actually create modules for all of the things you register lazily.
couchPotato grew out of some other example apps I found on the web. What I wanted was a tight way to do the lazy registration and a provider/service combo seemed ideal. I also wanted to maintain the ability for one component to depend on another, like in your example, you want the controller to depend on the factory... couchPotato lets you specify those dependencies in requirejs syntax.
So your controller, in my rendition, looks like this:
define(['app', 'myFactory'], function(app) {
app.couchPotato.registerController([
'mycontroller',
[
'myFactory',
function(myFactory) {
var message = myFactory.getCustomers();
alert(message);
}
]
]);
});
In this example, I made the controller, the factory and the value all lazy, but you could pick and choose and have some registered the "old fashioned way" at configuration time and others registered with couchPotato when they're needed for a given route.
http://plnkr.co/edit/Z3v1mszQiiq024po8Ocp?p=preview
A couple of things to note:
1) I put in a default route in order to trigger the lazy loading of your controller, your service (factory) and the version value.
2) I modified your service to append the version just to show how one component can depend on another (the service depends on the version value, the controller depends on the service).
So, within the require configuration, you don't actually specify any of this... it's all done lazily within your route.
$routeProvider.when('/',
$couchPotatoProvider.resolveDependenciesProperty({
templateUrl:'home.html',
controller: 'mycontroller',
dependencies: [
'mycontroller'
]
})
);
Since mycontroller depends on myFactory and myFactory depends on version, by the time your route is displayed they are all available and hooked up. I put some dummy text in the home.html partial just for kicks, but the controller is assigned by the $routeProvider so you don't actually need to specify it in the template.
couchPotato lives at https://github.com/afterglowtech/angular-couchPotato if you'd like to see a couple of other samples. I shim'ed it in dependent on angular because I designed it to be usable in cases where an entire application doesn't necessarily use requirejs... thus if you are loading angular with requirejs, you need to make couchPotato dependent on angular using the shim/deps technique.
LMK if you have any questions/comments... hope this helps!
I wonder what's the best way to configure a route which only purpose is to make use of a service call and then redirect.
I'm currently using this hack:
$routeProvider.when('/talks', {
template: '<div></div>',
controller: ['dateService', '$location', function(dateService, $location){
var nextTalk = dateService.getNextTalkDate();
$location.path('talks/' + nextTalk.format('MM') + '/' + nextTalk.format('YYYY'));
}]
});
Currently I set the controller configuration to an inline controller implementation which has dependencies on the services and then does it's thing and redirects.
However, it feels a bit weird, since I have to set template to some dummy value because otherwise the controller would not be invoked at all.
It feels as if I'm stressing the controller property for something it wasn't intended for.
I guess there should be a way to run some view unrelated code that has access to services on a route. I could right my own $routeProvider I guess but that seems to be a bit heavy for something I would consider should be built in.
Looks like '/talks' is a kinda abstract since its just used to redirect to other routes.. So how about setting up a route like this:
''/talks/:month/:year'
Where :month and :year is optional. If no month or year is given, your service returns a default talk. Which is probably the next talk. If params are given you just fetch the requested data.
So there's no redirect required. Now you specifiy the controller and the needed view and expose your data on the scope. Optionally would it be better to wrap your service call in a promise and resolve it at routeProviders resolve property.
This makes sure that the view only change if everything's resolved fine.
Hope that helps!
There's no way to inject services into anywhere into a route. Whether it be redirectTo or template, any code in there is handled on the module level. This means that the module is loaded first and then when the application has boostrapped itself then the services and injection-level code is executed. So the controller is your best bet (since that does support injection).
Controller is used in a lot of areas in AngularJS and it should work fine for what you're trying to do. You can either handle the redirection like you do in there or you can setup three different routes that point to the same controller (where you build the page).
var handler = { controller : 'Ctrl' };
$routeProvider.when('/talks', handler).
when('/talks/:month, handler).
when('/talks/:month/:year', handler);
And if you do end up using redirection, then just use $location.path(url).replace(). This will make the history stack jump back one level and therefore that the redirection triggering URL won't be in your history.