Resolve objects not available in $injector - angularjs

I have a route definition as follows:
.state('user_login', {
url: '/user/login',
templateUrl: 'login.tpl.html',
controller: 'AuthenticationCtrl',
resolve: {
practice: ['$q', function($q) {
return $q.when({});
}]
}
})
Things work as expected when I inject "practice" into the controller. When I use the $injector, service however:
$injector.get('practice')
I get an unknown provider exception. Are resolve objects not available to the $injector? How I can expose them in the controller without explicitly injecting them in the controller definition?
Note: I am using Angular 1.2.x

No, you cannot get them separately via $injector. And you cannot even inject them separately as well in other places, say the same controller (AuthenticationCtrl) instantiated by ng-controller directive.
Resolve objects are not any service or any other entity which can be injected separately. It is a special dependency injected by the router when the controller AuthenticationCtrl is bound via the router. You cannot get the instances separately. Only router knows about the resolve properties and while the router instantiates the controller (once all the resolve dependencies are resolved) it looks for resolve properties in the annotation (of the dependency list specified via explicit/implicit dependency annotation in the definision of AuthenticationCtrl) of the route-bound controller and injects them as required.
This kind of special implementation can be found in other components as well like, angular-ui-modal, ui-state-router, angular-router's routeprovider etc..

Related

Defining controllers using ui-router

I am creating web application using Angular js in ES6. I just started learning angular. I have following questions which I couldn't understand much from resources in internet.
1) I am using ui-router for routing based on states. I have following code in my controller
myApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home);
$stateProvider
.state('contact', {
url: '/contact',
templateUrl: 'contact.html',
controller: myContactController
});
};
Contact.html:
<div ng-controller=”myContactController”>
….
</div>
Question:
a) I have specified the controller in my state in js. Should I need to specify the controller using ng-controller in my view also? What is the difference and why its necessary ?
2) I have a base module for my app.
Base module - Index.js :
import subapp1 from ‘./subApp1/index’;
angular.module(“myapp”,[subapp1]);
subApp1/index.js
Export default function(){
Angular. module(“subApp1”,[]);
};
Question:
a) Is this the right way of injecting sub module dependency in to base module?If not which is the best way to inject module dependency in to base module?
b) I would appreciate if I can get best links to understand dependency injection and different scopes in angular js in basic way.
I have specified the controller in my state in js. Should I need to specify the controller using ng-controller in my view also? What is the difference and why its necessary ?
You don't need to use ngController in HTML. Router will fetch HTML template and compile it with specified controller.
I have a base module for my app...
You specify dependent module by its name, so your setup could look like this (note, how you export name property of the Angular module):
export default angular.module('subApp1', [])
.factory('someService', semeService) // For example, attach some module service
.directive('someDirective', someDirective) // ... or some components
.name;
and then
import subapp1 from './subApp1/index';
import subapp2 from './subApp2/index';
angular.module('myapp', [
subapp1,
subapp2
]);
1.a) no, you shouldn't. If you do, you'll have two instances of the controller
2.a) no. First you don't "inject" a module into another module: a module depends on another one, that's all. That has nothing to do with dependency injection. And the syntax for that is
angular.module('myapp', ['subApp1']);
I.e. the elements of the array must be names of module you depend on. And of course, these modules must themselves be defined (before or after, it doesn't matter), using
angular.module('subApp1', []);
2.b) https://docs.angularjs.org/guide

Angular: Optionally configure a service

I have a service that has an internal list of directive names (lets call it listService). In the spirit of loosely coupled applications I want other modules to be able to add their own directives to that list instead of statically defining it in the service module.
I understand that I can create a provider in order to configure a service like this:
app.provider("listService", ServiceProvider);
app.config(["listServiceProvider", function(listServiceProvider) {
listServiceProvider.add("myDirective");
}]);
At the same time, I don't want all the modules that define directives to depend on the service. So what I would like to do is "if this service is used in this project: configure it; else: ignore".
Unfortunately, the above code produces a module error if listServiceProvider is not available. I also tried to use a decorator with the same result.
So how can I optionally configure a service in order to have a loosely coupled application?
Like I mentioned in the comment, it is important to have a broader context for why you need to register directives (or their names).
In absence of a broader understanding, if, generally speaking, you need to conditionally check for existence of a service (or service provider, in config), you can use the $injector:
.config(function($injector){
var listServiceProvider;
if ($injector.has("listServiceProvider")) {
listServiceProvider = $injector.get("listServiceProvider");
}
if (listServiceProvider){
listServiceProvider.add("myDirective");
}
});
If I understand correctly; you want a service that tracks which of your directives have been added into the Angular app, and for the directives and the service to be decoupled from each other so they can be included on demand.
I don't think the module pattern will do this for you, since the services and directives are injected at load time. Optional dependency injection is not possible.
However, you could fire an event from your directive and pick it up in your service, removing the need for dependency injection altogether.
myDirective
.run(['$rootScope', 'LIST_SERVICE_EVENT',
function($rootScope, LIST_SERVICE_EVENT) {
$rootScope.$emit(LIST_SERVICE_EVENT, 'myDirective');
}]);
listService
.run(['listService', '$rootScope', 'LIST_SERVICE_EVENT',
function(listService, $rootScope, LIST_SERVICE_EVENT) {
$rootScope.$on(LIST_SERVICE_EVENT, function(ev, name) {
listService.add(name);
});
}]);
Fiddle: http://jsfiddle.net/bdpxhLg3/4/.

Angular: Inject dynamically into my controllers

I need to inject an extra object to my controllers dynamically so I thought it'd be best to do it in the run function like so:
angular.module("app").run([
"$rootScope", "$inject", "repository.user", function ($rootScope, userRepository) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
var controller = next.$$route.controller;
userRepository.getSession(function(data) {
// What do do now?
});
});
}
]);
I'd like to inject that returned data into my controllers but I'm not sure how to do it?
Take a look at the $routeProvider documentation, specifically the resolve configuration option for a route. Have you thought about using this to dynamically resolve your dependency?
resolve - {Object.=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired. The map object is:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}: If string then it is an alias for a service. Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before its value is injected into the controller. Be aware that ngRoute.$routeParams will still refer to the previous route within these resolve functions. Use $route.current.params to access the new route parameters, instead.
If I understand correctly, you are trying to collect user login state for every route change. Why not just save the data in $rootScope as something like $rootScope.currentUser = data.
This way you can access $rootScope.currentUser from any controller.

Responsibility of a controller in AngularJS - More than two views?

I come from the Rails world, where a controller is responsible to do business logic but a single controller can render several views, depending on the action that is supposed to do.
However, and after doing some research on AngularJS, I have the feeling that a controller will just have one responsability (associated with a single view). So, for example, if we have an application that lists restaurants, we would have the following:
#restauranteur.config(['$routeProvider', ($routeProvider) ->
$routeProvider.
when('/restaurants', {
templateUrl: '../templates/restaurants/index.html',
controller: 'RestaurantIndexCtrl'
}).
when('/restaurants/:id', {
templateUrl: '../templates/restaurants/show.html',
controller: 'RestaurantShowCtrl'
}).
otherwise({
templateUrl: '../templates/home.html',
controller: 'HomeCtrl'
})
])
One controller would be used for the 'index' and another for the 'show'. Is this the correct approach/suggested approach in Angular?
As you can read in official documentation, in general, a Controller shouldn't try to do too much. It should contain only the business logic needed for a single view.
The most common way to keep Controllers slim is by encapsulating work that doesn't belong to controllers into services and then using these services in Controllers via dependency injection.
In Angular, a Controller is a JavaScript constructor function that is used to augment the Angular Scope.
When a Controller is attached to the DOM via the ng-controller directive, Angular will instantiate a new Controller object, using the specified Controller's constructor function. A new child scope will be available as an injectable parameter to the Controller's constructor function as $scope.
Use controllers to:
Set up the initial state of the $scope object.
Add behavior to the $scope object.
Do not use controllers to:
Manipulate DOM — Controllers should contain only business logic. Putting any presentation logic into Controllers significantly affects its testability. Angular has databinding for most cases and directives to encapsulate manual DOM manipulation.
Format input — Use angular form controls instead.
Filter output — Use angular filters instead.
Share code or state across controllers — Use angular services instead.
Manage the life-cycle of other components (for example, to create service instances).
To answer shortly your question - yes, this is correct approach

Angular RouteProvider - resolve not passing object to controller

Take a look at the following plunker: http://plnkr.co/edit/uEbEdNifuBReENxzhb6H?p=preview
The expected behaviour for the routeProvider resolve (to my understanding) is for the resolved object to be injected into the controller. However Angularjs throws an "Unknown provider" error.
I have seen suggestions to inject the original service, but that will result in a deferred object, which renders the routeProvider resolve rather useless.
You should specify the controller with the root provider - not in the template (or the controller will be used regardless of the path, which may result in the dependencies not resolving):
$routeProvider.when('/',{controller:'TestCtrl', ...
I've updated your plunker: http://plnkr.co/edit/mSb58e8cGDNYU27xSizk?p=preview

Resources