How to inject upgraded Angular 1 service/factory to Angular 2 component in ES5? - angularjs

I have an Angular1 service with name, say, 'myService'
I then upgraded it using the Angular2 UpgradeAdapter like this:
var upgradeAdapter = new ng.upgrade.UpgradeAdapter();
upgradeAdapter.upgradeNg1Provider('myService');
Now I need to inject it to the angular2 component. Official upgrade guide only has typescript version for now. In typescript you use #Inject anotation with the service name for it like so:
export class MyComponent {
constructor(#Inject('myService') service:MyService) {
...
}
}
What is the syntax for injecting a service by name using ES5?

To complete the pixelbits' answer, you need also define my service within the providers list either:
At the application level
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(Cmp, [MyService]);
});
At the component level
var Cmp = ng.core.
Component({
selector: 'cmp',
providers: [ MyService ]
}).
(...)
Class({
constructor: [MyService, function(service) {
}]
});
It depends on what you want to do: share your service instance for all elements in the Angular2 application or per component.
This answers could give more details on dependency injection in Angular2 with ES5: Dependency Injection in Angular 2 with ES5.
Edit
In fact, there is a subtlety. IN the case of upgrade you need not to bootstrap using the ng.platform.browser.bootstrap function but the one from the upgrade object.
upgrade.bootstrap(document.body, ['heroApp']);
Where heroApp is the Angular1 module containing services and factories I want to use in the Angular2 application.
In fact, when you call the upgradeNg1Provider method on the upgrade, corresponding providers are registered within its associated injector. This means that you don't need to specify them at described above.
So you simply need to do that where you want to inject:
(...)
Class({
constructor: [ ng.core.Inject('customService'),
ng.core.Inject('customFactory'),
function(cService, cFactory) {
}]
});
Miško Hevery provides a great plunkr for this: http://plnkr.co/edit/yMjghOFhFWuY8G1fVIEg?p=preview.
Hope it helps you,
Thierry

Use the array notation for constructor DI of services:
.Class({
constructor: [MyService, function (service) {
...
}],

Related

call $emit event from angular to angularJS (publish-subscribe pattern)

I have a hybrid angular application tha uses both angularJS and angular 5.
I'm migrating a controller from angularJS to angular 5. This controller (let's call controller1) have the following code:
$rootScope.$emit('goToHomeEvent', 'Navigate to HomePage');
Then, I have another controller (let's call controller2) that subscribes the goToHomeEvent:
$rootScope.$on('goToHomeEvent', function (event, data) {
//Do some stuff
});
My goal is to only migrate the controller1 to angular 5, mantaining the code of controller2 has it is, maintaining the ability to subscribe the goToHomeEvent event.
Any suggestions?
I think that you could probably make usage of the localStorage for this as they dont't really communicate between each other.
I would suggest you to populate the localStorage on emit and watch it in the controller2. It's probably going to work although I don't know if it's the best approach for your problem.
The solution I came up is to create global functions and associate them to them to the window variable like this:
controller1:
$rootScope.$emit('goToHomeEvent', 'Navigate to HomePage');
window.goToHomeEvent(data);
controller2 (in angular 2+):
window["goToHomeEvent"] = fuction(data) {
//do stuff here
}
Our solution was a variation on the global. In the angularJS file at top level that calls .module('app').run(fn), we inject the $rootScope, $q, $injector, and $state and set them to a global var app (as opposed to directly onto the window object). So Angular2+ calls app.$rootscope.etc.
app.$q is effectively the browser's native/polyfilled Promise.resolve and related functions, so $q's replacement doesn't need to be injected into newer code.
app.$state is just for router concerns. Once you change over to Angular2's router, re-implement the parts of $state that you actually use in terms of the new router.
app.$injector is useful for a lot of oddball situations.
i've solved by global function without emit/broadcast:
AngularJS:
$window.bridgeFormAngular = function (params)
{
console.log(params);
}
Angular 7:
public sendToAngularJS(params: Object)
{
window["bridgeFormAngular"](params);
}
You can upgrade the $rootscope of angularjs and use it inside the Angular side of the hybrid.
You will have to follow the following steps:
1 - Register the '$rootScope' service as a provider inside Angular:
#NgModule({
providers: [{
provide: '$rootScope',
useFactory: ($injector: any) => $injector.get('$rootScope'),
deps: ['$injector']
}]
})
2 - Use the $rootScope inside your Angular components or Services via injection:
constructor(#Inject('$rootScope') private _rootScope: any) {}
constructor(#Inject('$rootScope') private _rootScope: any) {}

Why getting provider error when trying to inject resolved property in controller using ui-router?

I am unable to inject resolve property of ui-routing in controller.
It is giving
Error: $injector:unpr
Unknown Provider
When I'm using controller property in state definition object as following
.state('widget', {
url: '/widgets',
template: '<h1>{{name}}</h1>',
controller: function(widget, $scope) {
$scope.name = widget.name;
},
resolve: {
// standard resolve value promise definition
widget: function() {
return {
name: 'myWidget'
};
},
// resolve promise injects sibling promise
features: function(widget) {
return ['featureA', 'featureB'].map(function(feature) {
return widget.name+':'+feature;
});
}
}
});
Then it is working fine and I'm able to get the widget in controller and able to use in html.
Please see the fiddle for code.
http://jsfiddle.net/sunilmadaan07/ugsx6c1w/8/
Might be I'm making a silly mistake.
Before posting this question I have tried returning with simple object, promise object to the property.
Thanks In Advance.
You can not get resolved data in the directive with the code you did. Basically, you are trying to implement component based structure with an older version of angular 1.3.x.
You have two options to achieve this.
Create route controller then you can access resolve to the controller as local dependency then use that dependency as binding to the directive.
Here is example - http://plnkr.co/edit/TOPMLUXc7GhXTeYL0IFj?p=preview
Upgrade angular version to 1.5.x and use "ui-router-route-to-components": "^0.1.0"
Here working example of your code - http://jsfiddle.net/ugsx6c1w/25/
In order for the controller to be able to use resolvers, it should be route component (only in UI Router 1.x) or route controller.
widget and features are local dependencies, they aren't registered in the injector and aren't available anywhere in the application except route controller/component.
As explained here, resolvers can be passed to nested components in UI Router 0.3.x and injected directly to route components in 1.x.

Migration to Angular2: How to use injected AngularJS dependencies

How Am I supposed to use an AngularJS dependency during migration to Angular2 outside the constructor? I am using upgrade module and service is not yet upgraded.
So the answer was partially Making AngularJS Dependencies Injectable to Angular but it was not demonstrated how to make it available outside the constructor.
Here is an example for the $log angularJS service.
Create ajs-upgraded-providers.ts where we declare the provider:
/**
* $log upgraded provider
*/
export abstract class Log {
[key: string]: any;
}
export function logFactory(i: any) {
return i.get('$log');
}
export const logProvider = {
provide: Log,
useFactory: logFactory,
deps: ['$injector']
};
Import to app.module.ts the declared provider:
import { logProvider } from './ajs-upgraded-providers';
#NgModule({
imports: [
//imports
],
providers: [
//Providers & Services
//Upgraded angularJS providers
logProvider
]
})
example.service.ts How to use the angularJS service while migration takes place:
import { Log } from '../ajs-upgraded-providers'; //edit path accordingly
#Injectable()
export class ExampleService {
constructor(private $log: Log) {}
exampleFunction() {
this.$log.info("Info to be logged");
}
}
Making AngularJS Dependencies Injectable to Angular
When running a hybrid app, we may bump into situations where we need to have some AngularJS dependencies to be injected to Angular code. This may be because we have some business logic still in AngularJS services, or because we need some of AngularJS's built-in services like $location or $timeout.
In these situations, it is possible to upgrade an AngularJS provider to Angular. This makes it possible to then inject it somewhere in Angular code. For example, we might have a service called HeroesService in AngularJS:
import { Hero } from '../hero';
export class HeroesService {
get() {
return [
new Hero(1, 'Windstorm'),
new Hero(2, 'Spiderman')
];
}
}
We can upgrade the service using a Angular Factory provider that requests the service from the AngularJS $injector.
We recommend declaring the Factory Provider in a separate ajs-upgraded-providers.ts file so that they are all together, making it easier to reference them, create new ones and delete them once the upgrade is over.
It's also recommended to export the heroesServiceFactory function so that Ahead-of-Time compilation can pick it up.
— Angular Developer Guide - Upgrading (Making AngularJS Dependencies Injectable)

What is the equivalent of a factory in Angular2?

So I am used to using factories & services in Angular.
I am reading through the Angular2 docs and I don't see any equivalent of a factory. What is the equivalent for Angular2?
Factories, services, constants and values are all gone in Angular2. Angular2 is radically and fundamentally different from the classic Angular. In Angular2, the core concepts are
components
dependency injection
binding
The idea of services, factories, providers and constants has been criticized in Angular 1. It was difficult to choose between one. Removing them simplifies things a bit.
In the original Angular, you would define a service like so
app.service('BookService', ['$http', '$q', BookService]);
function BookService($http, $q){
var self = this;
var cachedBooks;
self.getBooks = function(){
if (cachedBooks) {
return $q.when(cachedBooks);
}
return $http.get('/books').then(function(response){
cachedBooks = response.data.books;
return cachedBooks;
})
}
}
Angular2 significantly leverages ES6 syntax to make the code more readable and easier to understand.
One new keyword in ES6 is class, which can be thought of as a service.
ES6 classes are a simple sugar over the prototype-based OO pattern. Having a single convenient declarative form makes class patterns easier to use, and encourages interoperability. Classes support prototype-based inheritance, super calls, instance and static methods and constructors.
Here's how that same code might look in Angular2
import {HttpService, Promise} from '../Angular/Angular2';
export class BookService{
$http, $q, cachedBooks;
constructor($http: HttpService, $q: Promise) {
this.$http = $http;
this.$q = $q
}
getBooks() {
if (this.cachedBooks) {
return this.$q.when(this.cachedBooks);
}
return this.$http.get('/books').then(function(data) {
this.cachedBooks = data.books;
return this.cachedBooks;
})
}
}
#Richard Hamilton's answer is appreciated and in addition to that there are few points to note.
For Factories,Service, and etc, in Angular2 we have service (or shared service). we have to make our service Injectable in order to use it.
NOTE: This code belongs to beta version and not RC.
import {Component, Injectable,Input,Output,EventEmitter} from 'angular2/core'
import {Router} from 'angular2/router';
import {Http} from 'angular2/http';
export interface ImyInterface {
show:boolean;
}
#Injectable() <---------------------------- Very Important
export class sharedService { <----------------- Service Name
showhide:ImyInterface={show:true};
constructor(http:Http;router:Router)
{
this.http=http;
}
change(){
this.showhide.show=!this.showhide.show;
}
}
If I want to use everywhere in my app, then I have to inject this service in bootstrap function like this,
bootstrap(App, [HTTP_PROVIDERS,sharedService <--------Name Injection
ROUTER_PROVIDERS,bind(APP_BASE_HREF).toValue(location.pathname)
]);
This way it creates single instance of your service. If you don't want to go with single instance, what you can do is - you can use Providers:[sharedService] metadata in you #component decorator.
Then, use it in your one of components like this,
export class TheContent {
constructor(private ss: sharedService) { <--------Injection dependency of your newly created service
console.log("content started");
}
showhide() {
this.ss.change(); <----- usage
}
}
Check working example here
I don't know what factories do exactly in Angular1 but in Angular2 there is useFactory:
{
provide: SomeClass,
useFactory: (dep1, dep2) => (x) => new SomeClassImpl(x, dep1, dep2),
deps: [Dep1, Dep2]
}
to provide your own instance construction logic if the default doesn't match your needs.
You can also inject a factory to create new instances yourself:
/* deprecated or removed depending on the Angular version you are using */
provide(SomeClass, {
useFactory: (dep1, dep2) => {
(x) => new SomeClassImpl(x, dep1, dep2),
},
deps: [Dep1, Dep2]
})
constructor(#Inject(SomeClass) someClassFactory: any) {
let newSomeClass = someClassFactory(1);
}
Argument x must have type assignment, otherwise angular doesn't know how to deal with it.
class SomeClassImpl {
constructor(x: number, dep1: Dep1, dep2: Dep2){}
}
If you need a new instance of a service in some component you need to provide it in that component like this:
#Component({
selector: 'hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
This will generate a new instance of the HereService as a factory does.

How to use typescript inheritance with angular dependency injection

I'm building an application with angular and recently switched to TypeScript. The core of my application consists of multiple actual classes that interact with eachother (so not just global services). The way I did this is as follows:
class Person {
constructor(private $http, public name: string) {
// ... use this.$http for some task
}
}
class PersonFactory {
static $inject = ['$http'];
constructor(private $http) {}
create(name: string) {
return new Person(this.$http, name);
}
}
angular.module('app').service('personFactory', PersonFactory);
So if some other class needs to create a Person object, it needs to get injected with PersonFactory and call its create method. This works ok, even though it requires some boilerplate.
My main problem arises when I want to subclass Person:
class Student extends Person {
constructor(private $http) {
super(this.$http, 'Some actual name')
}
}
I could follow the same pattern as above, but then I need to, again, create a StudentFactory that passes the required services like $http to the class constructor, which in turn passes it to the constructor of the super method. If I now had multiple such extending classes and would change the base class Person, I had to change every extending class and their factories. Is there a better way to approach this problem? Might it be better to just remove all dependencies on angular services in my core application and just use angular for the UI code?
Solution
As suggested in the answers, I added the $injector service to the window, like this:
module('app').run(['$injector', function($injector) {
window.$injector = $injector;
}]);
The service can now be used in any class to get other services like $http.
This didn't work for unit tests though, so I had to put the injector on the global scope again:
// MyClass.test.js
var $injector;
describe('My class', function() {
beforeEach(module('app'));
beforeEach(inject(function(_$injector_) {
$injector = _$injector_;
}));
...
});
I had to change every extending class and their factories. Is there a better way to approach this problem
Just inject $injector and then the base class can get what it wants using $injector.get().
or even more crazy ... put $injector (its a singleton) on the global scope / or some module ... and just use that in your classes to get angular specific stuff.

Resources