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?

1

2 Answers 2

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);
  }
}
Sign up to request clarification or add additional context in comments.

Comments

-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);
    }
    ...
   }
 ...
}

3 Comments

Sadly, this won't make Logger to be injected automagically when the class is instantiated directly like that, new Teacher(...).
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().
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.