0

I'm trying to use Injector to inject my component/service but one of them requires NgZone as its dependency. From https://angular.io/api/core/Injector

export MyComponent{
  constructor() {
    const injector = Injector.create({
      providers: [
        { provide: NgZone, deps: [ ] },
        { provide: MyService, deps: [ NgZone ] }
      ]
    });

    this.myService = injector.get(MyService);
  }
}

Then in child class:

export MyOtherComponent extends MyComponent {
  constructor() {
    super();
  }

  public helloWorld() {
     this.myService.stuff();
  }
}

But I'm getting the following error:

ERROR Error: StaticInjectorError[MyService -> NgZone]:
NullInjectorError: No provider for NgZone! at NullInjector.get (core.js:8896)

I tried with a dummy service that don't have anything in the constructor, and it worked.

  • Is there a way to provide NgZone manually through the deps like that?
  • Is there another way to get the "global" NgZone object (there should only be 1 instance of NgZone running right?)

MyService is also a downgraded service and is being used in both AngularJS and Angular7, not sure if that changes anything.

Edit: Reason I'm trying to do this, is because MyComponent is a component base class that will get extends upon and have many child class extending on that. If I could do it like this by manually injecting it internally, then I don't need to pass all those dependencies from the children. Imagine I have 6-7 dependencies and 30+ childrens, and lets say I need some new dependencies, I'd have to update every single one of them...

codenamezero
  • 2,724
  • 29
  • 64
  • Please show more code around this thing. You are creating an Injector with no parent so it only has what you gave it. Also why would you do it like that, why not inject your service in a constructor? What is the purpose of creating injector manually? – waterplea Nov 22 '19 at 07:35
  • I've added my reason in my edit. The code is really what you see there, I just want to know how to manually injecting anything that requires those angular related classes such as `NgZone`. – codenamezero Nov 22 '19 at 14:27

2 Answers2

1

You could inject injector — it would be a single dependency. And all your children could then get what they need from this injector. Yes, you would need to provide that injector through your inheritance chain of super() calls, but at least it would be just one thing.

There's also this:

https://github.com/angular/angular/issues/16566#issuecomment-338188342

This comment states it is possible to use DI in abstract classes if you decorate them. As for NgZone instance — yes, I believe there must be only one and I also tried to get a hold of it once but couldn't come up with an elegant solution.

waterplea
  • 3,462
  • 5
  • 31
  • 47
  • I already knew that, that's not what I'm asking for. I am exploring the possibiilties here. – codenamezero Nov 22 '19 at 14:53
  • Injectors are hierarchical, you cannot get something that isn't there. If you ONLY need NgZone — there are nasty hacks like this: https://stackoverflow.com/questions/47619350/access-angular-ngzone-instance-from-window-object You can also store it in window yourself through some hand written patching – waterplea Nov 22 '19 at 14:57
0

Guess I was brain dead last night. This morning after digging deeper, I think I've found a way to grab the global Zone and it seems to work (it triggered the change detection).

Since @waterplea also have the assumption that there should only be 1 instance of the NgZone, I decided to just look around in console and what do you know.

enter image description here

Then I tried to just pass the global Zone to it like this:

    { provide: NgZone, useValue: Zone },

And it gave me the error that this.ngZone.run is undefined. OK... digging deeper, oh hey, there is a root object in Zone and hey, look, a run function!

enter image description here

So I went and updated the code to this and it worked.

    { provide: NgZone, useValue: Zone.root },
codenamezero
  • 2,724
  • 29
  • 64
  • That's not NgZone, that's Zone. And it doesn't even seem to equal (`===`) to `ngZone._outer`, although it looks pretty much the same (also named ``). If it works for you, that's cool, but I wouldn't recommend relying on this in production. If you really need that, it would be better to store ngZone in a static property of some singleton service that gets initialized inside `APP_BOOTSTRAP_LISTENER`. – waterplea Nov 22 '19 at 16:29