1

Suppose that I have the following class:

export class Teacher {
    constructor( public name: String, private age: number ){}
    ...
}

Teachers are created like this:

const firstGradeTeacher: Teacher = new Teacher("Hannah", 32);

I want to add a logger to the teacher now. The logger is Injectable(). My first line of thinking was to do this:

export class Teacher {
    constructor( public name: String, private age: number, private logger: Logger ){}
    ...
}

Unfortunatly, this means that now everyone that constructs a teacher needs to either:

  1. Have a logger or

  2. Know how to construct a logger

What I would reallly like to do is this:

export class Teacher {
    private logger: Logger = Injector.get(); // <-- Does this exist?
    constructor( public name: String, private age: number){}
    ...
}

Is there a way that I can do this?

sixtyfootersdude
  • 25,859
  • 43
  • 145
  • 213
  • Possible duplicate of [Storing injector instance for use in components](http://stackoverflow.com/questions/39409328/storing-injector-instance-for-use-in-components) – Estus Flask Sep 16 '16 at 20:11

2 Answers2

2

The access to root injector is possible with a hack.

However, this designates XY problem. The class is supposed to be either injectable or non-injectable. It isn't a good idea to mix these concepts, because there's no idiomatic way to do this in Angular.

A factory for non-injectable class is a good way to handle this.

@Injectable()
class TeacherFactory {
  constructor(private logger: Logger) {}

  public createInstance(...args) {
    const teacher = new Teacher(...args);

    // or pass it as extra argument if logger is used in Teacher constructor
    teacher.logger = this.logger;

    return teacher;
  }
}

@Component({
  ...
  providers: [TeacherFactory]
})
class SomeComponent {
  constructor(private teacherFactory: TeacherFactory){
    const firstGradeTeacher: Teacher = teacherFactory.createInstance("Hannah", 32);
  }
}

Or

// non-injectable class
class TeacherFactory {}

@Component({
  ...
  providers: [{ 
    provide: TeacherFactory,
    deps: [Logger],
    useFactory: (logger: Logger) => (...args) => {
      const teacher = new Teacher(...args);
      teacher.logger = logger;

      return teacher;
    }
  }]
})
class SomeComponent {
  constructor(private teacherFactory: TeacherFactory){
    const firstGradeTeacher: Teacher = teacherFactory("Hannah", 32);
  }
}
Community
  • 1
  • 1
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
-1

I think you are looking for this:

import { Optional } from '@angular/core';

export class Teacher {
   constructor(public name: String, private age: number, @Optional() private   logger: Logger) {
     if (this.logger) {
       this.logger.log(some_message);
    }
    ...
   }
 ...
}
Omar
  • 139
  • 1
  • 7
  • 1
    Sadly, this won't make `Logger` to be injected automagically when the class is instantiated directly like that, `new Teacher(...)`. – Estus Flask Sep 16 '16 at 21:06
  • I don't know what you want to do exactly. What I understood is that you want your Logger to be an optional dependency, for that you need to annotate the logger in the constructor with @Optional(). – Omar Sep 17 '16 at 00:43
  • The question explains how Teacher class is supposed to be used. The injector would throw on Teacher class from your example, because other constructor annotations (public name: String, private age: number) are not providers. – Estus Flask Sep 17 '16 at 00:58