Possible to manual inject NgZone? - angular7

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...

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.

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.
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!
So I went and updated the code to this and it worked.
{ provide: NgZone, useValue: Zone.root },

Related

Service Dependencies in NestJS

I have many endpoints in my app:
/Route1
/Route2
...
/Route99
In a number of these routes, there is some common functionality such as getting specific data from one source such as a local file, or another resource such as a No SQL database or external HTTP endpoint. My problem is that these services need to have a service dependency themselves, and I am not sure that how I have currently done it is the best way to do it in NestJS.
Route1Service - Read a file of data, and return it. This uses the FileSystemService() to wrap all the error handling, different data types, path checking etc., of the NodeJS fs module. The Route1Service then returns this to the Route1Controller
#Injectable()
export class Route1Service {
private FS_:FileSystemService; // defined here instead of constructor, as I do not know how to set it in the constructor via NestJS, or if this is even the best way.
// constructor(private FS_: FileSystemService) { }
// Since I do not set it in the constructor
public DataServiceDI(FsService:FileSystemService):void {
this.FS_ = FsService;
}
public GetData(): string {
const Data:string = this.FS_.ReadLocalFile('a.txt');
return Data;
}
}
Route99Service might do the same thing, but with a different file (b.txt)
#Injectable()
export class Route99Service {
private FS_:FileSystemService;
public DataServiceDI(FsService:FileSystemService):void {
this.FS_ = FsService;
}
public GetData(): string {
const Data:string = this.FS_.ReadLocalFile('b.txt');
return Data;
}
}
This is a contrived example to illustrate my issue. Obviously a basic RouteService could be used, and pass the file name, but I am trying to illustrate the dependent service. I do not know how to define the module(s) to use this dependent service or if I should be doing it this way.
What I have been doing for my definition:
#Module({
controllers: [Route1Controller],
providers: [Route1Service, FileSystemService],
})
export class Route1Module {}
The controller than has the constructor with both Services:
#Controller('route1')
export class Route1Controller
constructor(
private Route1_: Route1Service,
private FsSystem_: FileSystemService
) { }
Now that my controller has the FsSystem service as a separate entity, I need to add a method on my Route1Service, DataServiceDI(), to allow me to pass the FileSystemService as a reference. Then my service can use this service to access the file system.
My question comes down to, is this the best practice for this sort of thing? Ultimately, in my code, these services (FileSystemService, NoSqlService) extend a common service type, so that all my services can have this DataServiceDI() in then (they extend a base service with this definition).
Is this the best approach for longer term maintainability? Is there an easier way to simply inject the proper service into my Route1Service so it is injected by NestJS, and I do not have to do the DI each time?
The current method works for me to be able to simply test the service, since I can easily mock the FileSystemServie, NoSqlService, etc., and then inject the mock.

NestJS: UseGuards Doesn't Use Dependency Injection - Can't Override Guard

Current behavior
The documentation states here that:
...we passed the RolesGuard type (instead of an instance), leaving responsibility for instantiation to the framework and enabling dependency injection.
So I'm expecting to "override" a guard with another, via the module's providers.
This doesn't work as expected.
Interestingly enough,
injecting the same service via the controller's constructor does yield the correct service, though.
Input Code
Simple: https://github.com/dima-gusyatiner/nestjs-guards-override/tree/only-app-module
With Another module, using exports: https://github.com/dima-gusyatiner/nestjs-guards-override/tree/master
Controller:
#Controller()
#UseGuards(AuthGuard)
Module:
#Module({
controllers: [AppController],
providers: [
{
provide: AuthGuard,
useClass: AuthOverrideGuard,
}
],
})
Expected behavior
I would expect AuthGuard to never even be constructed.
Instead, both classes are constructed, and AuthGuard is used as the guard.
Environment
- Nest version: 8.0.6
- Node version: 16.8.0
- Platform: Linux
Bug Report
I've also opened a ticket for this:
https://github.com/nestjs/nest/issues/8011
Question
Am I doing something wrong here, or is this a bug?
Anybody can suggest a workaround?
I am not sure I understand it that well either but from what I saw:
With UseGuards, the passed in class is not resolved as provider. Only when you declare the guard as a dependency. Just like in AppController, is it looked up in the provider scope.
Sure the guard's dependencies are resolved as providers within the calling module's scope but it itself is not. Just regular instantiation with dependency injection.
I think that is why both guards are still created, just that AuthOverrideGuard, is registered as a provider.
That is why you get the following output:
[Nest] 77074 - 05/09/2021, 05:01:39 LOG [NestFactory] Starting Nest application...
Construct AuthOverrideGuard
Construct AuthGuard
AuthGuard
AppController AuthOverrideGuard {}
This is why this.guard is correctly resolved as AuthOverrideGuard from the provider scope
To summarize, in my understanding, the docs are inaccurate.
The documentation states here that:
...we passed the RolesGuard type (instead of an instance), leaving responsibility for instantiation to the framework and enabling dependency injection.
This is inaccurate, since RolesGuard won't be instantiated with proper dependency injection. Instead, it will be instantiated using this exact class only.
To achieve proper dependency injection, one should use another class, injected via the guard's constructor. For example:
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { Observable } from 'rxjs';
import { AuthService } from '../services';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(
private readonly auth: AuthService,
) {}
canActivate(context: ExecutionContext) {
return this.auth.canActivate(context);
}
}
You can never provide another AuthGuard, it won't work. But you can provide another AuthService, which this guard uses.
#Module({
controllers: [AppController],
providers: [
{
provide: AuthService,
useClass: AuthServiceOverride,
}
],
})

Where should I setup DI container in Yii2?

I have an interface. And a class. The classimplements the interface and extends BaseObject. It's something like business logic service. I a'm going to inject it in a controlller via constructor. My target is use dependency from the interface.
I'm looking on the documentation and I don't understand, where should I write this code. Is it a parth of main config? Or it's some kind of new one config? Is it a separete file? If yse, how Yii2 will understand that it's DI configuration?
You can set up DI container in your app config. For example in web.php config:
$config = [
// ...
'container' => [
'definitions' => [
\my\namespace\MyInterface::class => \my\namespace\MyClass::class,
// ... other definitions
],
],
// ... other configs
];
Another good place to set up DI might be in bootstrap method of component. For example in Module class.
class MyModule extends \yii\base\Module implements \yii\base\BootstrapInterface
{
public function bootstrap($app)
{
Yii::$container->set(
\my\namespace\MyInterface::class,
\my\namespace\MyClass::class
);
}
}
In this case you have to add the module to app's bootstrap property.

ZF2, dependencies which I don't know at start

In my controller, via service, I get from DB a list of the names of widgets (eg. chart, calendar, etc). Every widget implements WidgetInterface and may need other services as its own dependencies. The list of widgets can be different for each user, so I don't know which widgets / dependencies I will need in my controller. Generally, I put dependencies via DI, using factories, but in this case I don't know dependencies at the time of controller initialization.
I want to avoid using service locator directly in controller. How can I manage that issue? Should I get a list of the names of widgets in controller factory? And depending on widgets list get all dependencies and put them to controller?
Thanks, Tom
Solution
I solved my issue in a way that suggested Kwido and Sven Buis, it means, I built my own Plugin Manager.
Advantages: I do not need use service locator directly in controller and I have clear and extensible way to get different kinds of widgets.
Thank you.
Create your own Manager, like some sort of ServiceManager, for your widgets.
class WidgetManager extends AbstractPluginManager
Take a look at: Samsonik tutorial - pluginManager. So this way you can inject the WidgetManager and only retrieve the widgets from this manager as your function: validatePlugin, checks whether or not the fetched instance is using the WidgetInterface. Keep in mind that you can still call the parent ServiceManager.
Or keep it simple and build a plugin for your controller that maps your widget names to the service. This plugin can then use the serviceLocator/Manager to retrieve your widget(s), whether they're created by factories or invokableFactories. So you dont inject all the widget directly but only fetch them when they're requested. Something realy simplistic:
protected $map = [
// Widget name within the plugin => Name or class to call from the serviceManager
'Charts' => Widget\Charts::class,
];
public function load($name)
{
if (array_key_exists($name, $this->map)) {
return $this->getServiceManager()->get($this->map[$name]);
}
return null;
}
Injecting all the Widgets might be bad for your performance so you might consider something else, as when the list of your widgets grow so will the time to handle your request.
Hope this helped you and pushed you in some direction.
This indeed is a interesting question. You could consider using Plugins for the widgets, which can be loaded on the fly.
Depency injection is a good practise, but sometimes, with dynamic content, impossible to implement.
Another way to do this, is to make your own widget-manager. This manager then can load the specific widgets you need. The widget-manager can be injected into the controller.
Edit:
As you can see above, same idea from #kwido.
I would use a separate service and inject that into the controller.
interface UserWidgetServiceInterface
{
public function __construct(array $widgets);
public function getWidget($name);
}
The controller factory
class MyControllerFactory
{
public function __invoke(ControllerManager $controllerManager, $name, $requestedName)
{
$serviceLocator = $controllerManager->getServiceLocator();
$userWidgetService = $serviceLocator->get('UserWidgetService');
return new MyController($userWidgetService);
}
}
Then the logic to load the widgets would be moved to the UserWidgetServiceFactory.
public function UserWidgetServiceFactory
{
public function __invoke(ServiceManager $serviceLocator, $name, $requestedName)
{
$userId = 123; // Load from somewhere e.g session, auth service.
$widgetNames = $this->getWidgetNames($serviceLocator, $userId);
$widgets = $this->loadWidgets($serviceManager, $widgetNames);
return new UserWidgetService($widgets);
}
public function getWidgetNames(ServiceManager $sm, $userId)
{
return ['foo','bar'];
}
public function loadWidgets(serviceManager $sm, array $widgets)
{
$w = [];
foreach($widgets as $widgetName) {
$w[$widgetName] = $sm->get($widgetName);
}
return $w;
}
}
The call to loadWidgets() would eager load all the widgets; should you wish to optimise this you could register your widgets as LazyServices

Provide new instances of model class for dependency injection in Angular 2

I have a class that serves as a model for some data I get from a server. This data starts as an unwieldy xml object where text nodes have attributes so the json format I convert it into does not have simple string values. Instead I have:
#Injectable()
export class FooString {
_attr: string;
value: string;
isReadOnly(): boolean {
return this._attr && this._attr === 'ReadOnly';
}
isHidden(): boolean {
return this._attr && this._attr === 'Hid';
}
}
Then my model is like:
#Injectable()
export class Payment {
constructor(
public FooId: FooString,
public FooStat: FooString,
public FooName: FooString ) { }
}
Everything ends up with the same instance of FooString. How do I get discrete instances for each of them?
I have tried a factory, but it still only creates a single instance:
export let fooStringProvider = provide(FooString, {
useFactory: (): FooString => {
console.log('in foostring factory');
return new FooString();
}
});
new FooString();
new Payment();
;-)
Why using DI when they don't have dependencies and you don't want to maintain single instances per provider. Therefore, just use new.
When to use DI
There are a few criterias when using DI instead of new the right thing:
If you want Angular to maintain and share instances
If you want to work with an interface or base class but then you want to configure from the outside what implementation should actually be used at runtime - like the MockBackend for Http during testing.
If you class has dependencies to instances and/or values provided by DI
If you want to be able to easily test classes in isolation (https://en.wikipedia.org/wiki/Inversion_of_control)
probably others ...
If there are good arguments to use DI, but you also want new instances then you can just provide a factory.
This answer https://stackoverflow.com/a/36046754/217408 contains a concrete example how to do that.
Using DI is usually a good idea. There are IMHO no strong arguments against using DI. Only when none of the above arguments apply and providing factories is too cumbersome, use new Xxx() instead.

Resources