13

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?

user2202911
  • 2,898
  • 5
  • 20
  • 28

4 Answers4

15

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
  • 25,478
  • 10
  • 60
  • 87
  • 20
    why would you say **services** are gone in Angular 2? It is still a big section as a general concept in the official tutorial: https://angular.io/docs/ts/latest/tutorial/toh-pt4.html – paradite Oct 09 '16 at 07:40
  • Can you show in the answer how to create a shared object? – zoonman Nov 27 '16 at 06:40
  • Following on from @zoonman, class factories are also needed to hide the DI dependencies of classes as demonstrated here: https://angular.io/docs/ts/latest/guide/dependency-injection.html#!#factory-provider – Herc Mar 27 '17 at 12:49
  • 1
    I don't think this answers the original question. Even if the 'factory' alias is not implemented in angular the pattern is still viable and significantly distinguished from a normal service pattern. – Raven May 03 '17 at 14:21
  • 1
    I think as es6 has a 'class' keyword, we can just use class to replace factory. A factory is a lot like a class – MING WU Dec 14 '20 at 01:35
15

@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

micronyks
  • 54,797
  • 15
  • 112
  • 146
9

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){}
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • should deps property be in the same scope as useFactory? – smirnov Sep 07 '16 at 15:07
  • As far as I remember I answered a similar question with a Plunker. Would have been a good idea here as well :-/. Thanks again for the fix. – Günter Zöchbauer Sep 07 '16 at 16:29
  • @GünterZöchbauer Looking at `let newSomeClass = someClassFactory(1);` ( middle snipped) - in the CTOR , the `someClassFactory: any` is already(!) invoked , returning already `new SomeClassImpl`. So why do you invoke it again in `let newSomeClass = someClassFactory(1);` ? Am I missing something ? – Royi Namir Apr 26 '17 at 09:37
  • Thanks for the hint. The update for the "new" syntax to register providers I forgot a `(x) =>`. The intention was to inject a factory function, not the result of the factory function. – Günter Zöchbauer Apr 26 '17 at 09:41
1

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.

Damsorian
  • 1,664
  • 2
  • 14
  • 10