-
Notifications
You must be signed in to change notification settings - Fork 26.3k
Allow injecting ComponentRef of a component through DI #47398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
(linking #8277 here as an additional use-case) |
This feature request is now candidate for our backlog! In the next phase, the community has 60 days to upvote. If the request receives more than 20 upvotes, we'll move it to our consideration list. You can find more details about the feature request process in our documentation. |
Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends. Find more details about Angular's feature request process in our documentation. |
Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage. We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package. You can find more details about the feature request process in our documentation. |
I think that we should start with the use-case first, as the notation of a host directive setting an input of its host component is ... surprising. @Harpush could you please share real-life use case where you need to set component inputs from a directive? |
@pkozlowski-opensource sure. I had not a while ago a third party component with several inputs. We set all of them to the same values in most of the app. Now the idea I had is adding a directive that sets those inputs. The component is using ngOnChanges and not input get set so I got stuck. If I had a way to inject the component ref and call set input - the ngOnChanges would have been invoked. |
I agree, @Harpush. @pkozlowski-opensource, this would make a lot of third-party integration szenarios easier. It would not make the learning-process for Angular beginnners harder, but would support advanced use-cases if needed. As mentioned, IMHO it would fit well into the |
One real-life example, although not very nice is, that in one project I have a lot of components that have and id as input. Now I wanted to write a directive that can set this id. But currently I would have to inject all the components into this directive to be able to set the input. With the In general this allows inversion of dependency. It would allow directives to interact with a component without knowing the interface. This has certain downsides, as for example type safety is not ensured, but on the other hand that is always the case if you have such loose coupling. |
@pkozlowski-opensource any news concerning this? |
We should also need a DirectiveRef, this can also be used to interact with HostDirectives. |
Since components cannot be composited very well (e.g. an element can have a single component only), there is currently no way to create a specialized component from a base. For example: There is a button component with an attribute selector ( Now, we'd like to have a specialized button component which extends its style and/or sets sensible input properties. Since these components cannot be composited, we have to:
The problem with the first approach is that in this case, we have to forward everything that a button can do. And that's not even enough. What if there is a directive (e.g. The problem with the second approach is that directives cannot contain styles (see #17766). Also, with the new signal-based inputs, we cannot even update the properties from the typescript.
|
I have this exact use case while trying to refactor a component to signal-based inputs. |
Reading through https://netbasal.com/angulars-model-function-explored-a-comprehensive-overview-4481d023c822 I saw the same problem described here trying to be solved with the new model. Why is this issue still with needs clarification? |
I would add that inheritance VS composition is a subject of discussion and with Angular I usually go with composition. But that seems a bit overkill: directives look pretty well suited for adding a behavior or setting inputs to components since you can cumulate several ones like traits in PHP or Rust, without having to build 4 - 5 wrapper components and without having to face a diamond inheritance issue. |
I have the same use case, I have an addon directive that meant to be applied on a specific component and override its input values. currently I cannot replace |
This is really needed when you have component with input() signal. You cannot change input value and you don't have componentRef<> available to use setInput(). So it is forcing you to change to model<>() which is basically antipattern. |
Typical usecase is, when you want to write reusable directives to set inputs of components, several component libraries use that. Example usecases:
This is not possible without ComponentRef.setInputs() or model() (which is antipattern to change all inputs to models() because of external usage) Before input() signals. Everyone was able to set inputs via these steps:
Now, we have no way to do that. |
The only way that i found at this moment is by use a computed that create a new signal with the signal input value internal to the directive, and the expose a method from directive to modify that derived signal. So every time the input of directive change, reset the derived signal value. But this is a simple workaround, and can't be a good pattern, especially if we need to do this mechanism for a lot of inputs of directive. Example:
|
Just wanted to reiterate what @montella1507 is saying - this is something that was possible to do without signal inputs before (set the input and call change detection) and is no longer supported with signal inputs. It's a pattern we're using as well and it's stopping us from fully going to signals. Tagging @JeanMeche since you closed #54987 where the conclusion was to use |
In the context of unit tests you should indeed use |
@JeanMeche that seems more like a workaround than a proper solution to this problem. Also if we create a directive for a component or directive that comes from a package, it won't be possible to alter that input if they dont already have a |
@JeanMeche sorry to say, but this is the same terrible design decision, like the allowSignalWrites was. Forcing the developers to "guess the future" - to implement something somehow, that does not make sense in a scope of a particular piece of software, because there is mistake in a framework. Let me explain in examle with allowSignalWrites.
And... because of design decision, it does not work, because... developer is writing inside signals, but HE DID NOT WANT TO, and he did not know at all.. he was just setting value with patchValue()... The reality, that there is Signal() inside, is hidden.. should be hidden, should be implementation detail.. But it is not. The point is, CVA-component creator, was doing 100% legit things, just using signals... setting them not inside of a effect... With allowSignalWrites and this one.. we were (are) heading to ERA where every developer is forced to now ENTIRE callstack and implementation details... even to the future. The reason it was bad design was the usage of "ambient context pattern" - that somehing relies on something else from "outter space". And now.. You suggest to do the same... |
Hi Team, We have several directives that simplify the the usage of complex components by setting inputs and their host component. We are unable to migrate @input to InputSignal because it is not possible to set them from directives. In some cases, we can go to the host component an change the input to a model ... we have too many properties to even consider using linkedSignal pairings. In addition, even if we did not migrate any of the components to InputSignals, you have library Authors such PrimeNG who are talking about modernised components using signals coming up in future versions. This means either they will be stuck building the new versions or we will break because the inputs will become InputSignals and we cannot set them. Is there anyway to fast track a solution to this, injecting ComponentRef, providing a footgun method on InputSignal, allowing casting to writable signal etc? Kind Regards, |
@thaoula, could you please prepare a simple Stackblitz PoC that demonstrates your current design? I am curious how it looks like and what you struggle with. Let's see whether there is a proper way to refactor this to the modern Angular APIs. |
Hi @mikezks , Many, thanks for your reply. I have create the simplest example to demonstrate what we are doing. In this simple example, I have a basic host component (ComplicatedLookup) and two directives (JobLookup, ContactLookup) that set properties using different data. https://stackblitz.com/edit/stackblitz-starters-jxjmiswb?file=src%2Fmain.ts In real life the host component is something similar to NGSELECT with many inputs. We have reasons to utilise this strategy in our business application and we find it extremely powerful. I do not want to get into a discussion about the merits of the strategy. It works currently and is very effective. The core issue is that we have this object, InputSignal that allows setting from outside (magically via the template binding) but not setting inside the component. I guess, this ensures unidirectional flow. In our case, we have a directive that is technically outside the component and would like to set the inputs on its host and it cannot. We cannot get a ComponentRef and we cannot cast it to something because API to do so is not public. We are just asking for a way either via ComponentRef (not strongly typeed) or some other mechanism to emulate the template binding technique. |
you mean more react-like antipattern design based APIs :-) As @thaoula . This pattern is usually used as "attached behavior". For the instance.. The most used libraries like Kendo are using this pattern. We are using this too for "Datasources". Usually directives to set datasource for "dumb components", like dropdowns, comboboxes, or even the grids. They usually nned to set inputs like items, state, to react to events etc. |
Is this subject somehow alive? Also I don't want to emit value from input to pass it through HTML, that's not the point here.
To address this I would either need to access SetInput from ComponentRef or be able to update the value of InputSignal somehow. |
Maybe a function called in ctor similar to the new bindings: @Directive()
class Dir {
private comp = inject(Comp, {host: true});
test = input.required<string>();
constructor() {
bindInput(comp.test, this.test);
}
} Pretty sure it will be helpful for the new signal forms too :) |
Which @angular/* package(s) are relevant/related to the feature request?
core
Description
As of angular 14 we have the new super useful
setInput
onComponentRef
for dynamic components. It finally allows fully CD aware dynamic inputs changing.Currently if I want to extend a component with a directive and set inputs from it I need to do one of the two options:
ngOnChanges
won't fireProposed solution
Allowing to inject
ComponentRef
of the host component instead of the actual instance. This way we could just usesetInput
to update inputs and have a full CD aware binding support not through the template.Alternatives considered
ngOnChanges
was not used...ComponentRef
but as any other way to usesetInput
from DI injected componentsThe text was updated successfully, but these errors were encountered: