angular: formControlName could not be used with component transclusion
[x] bug report => search github for a similar issue or PR before submitting
Version: 2.4.1
A component with selector parent-component has this:
<form [formGroup]="myFormGroup">
<ng-content></ng-content>
</div>
And it is inserted in another template with:
<parent-component>
<input formControlName="myControl">
</parent-component>
Angular says the control is used without a form.
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 55
- Comments: 46 (9 by maintainers)
I have switched to React long long time ago.
@asfernandes This unfortunate behavior is due to the fact that the
form-wizard
content is processed as part of the app, not as part of theform-wizard
template. ThusformControlName
directive will not findFormGroup
(or ControlContainer to be more precise) to register.What I’m usually doing in such case is using
template+formControl
instead ofng-content
. Here is an example - https://plnkr.co/edit/lVqEIYcY9zFJCeoYdvwL?p=previewThis is the best solution I’ve come so far.
Here is a solution:
Just add it to a div as well
I came up with this other approach of duplicating the formGroup directive on the container component and on the form and it seems to work ok: https://plnkr.co/edit/77wcSk0dnDKuJ2IyBgZd?p=preview Do you see any problem with this?
Good for you if you like it. I am the author of: https://www.wearefrontend.com Remember some devs are doing open source in both side react and angular to (HELP) the community (YOU)… Think about it before giving an answer like this one (some respect to this people)… your answer has nothing good with the context we are in (trying to solve the issue (YOU) flagged).
viewProviders have solved my problem with wrapper component for inputs in reactive forms. Example:
usage:
<app-input name="lastName">Last name</app-input>
“name” is used as formControlName in wrapper component
+1 We are wrapping Angular Material components in our own components to reduce boilerplate and this issue forces us to use the hack specified by gschuager.
There’s nothing insulting in saying that someone is using a different framework now. The issue was opened 3 years ago and you tagged him directly with the solution. He was kind enough to reply and tell you that he won’t be able to check it out because he uses React now. Then for no reason you decided to call him out in a patronizing tone.
What did you expect him to do? Open 3 years old code and decide to rewrite the whole the app to Angular?
As others have mentioned, the problem here is that child form directives like
formControlName
are looking to register with a parent form, and here there is no parent form above it in the component tree that can be injected. TheFormGroupDirective
instance lives inside the component’s view, which at best is a sibling.It’s possible to make this work with a mix of viewProviders and a template-outlet, but it’s hacky and we should have an easier way to implement components like this. This needs a design doc.
For everyone having this problem:
Had the same difficulty as @artem-v-shamsutdinov while attempting to wrap angular material components into my own app components.
Solved this by adding a @Input prop at my input component to carry the current formGroup instance, and adding it to my input container tag. In my case, this container was
<mat-form-field>
, but i think it would also work with<div>
. Just like this:login.component.html:
cv-classic-input.component.html:
cv-classic-component.ts:
Hope this helped. Credits to: https://medium.com/@joshblf/using-child-components-in-angular-forms-d44e60036664
@lazarljubenovic His answer seems to say “i switched to react so it’s not my problem anymore”. I don’t like this kind of mindset so i just said what i think about it. It’s just my opinion you can agree or disagree with my answer knowing that i took the time to try to answer him (providing an online example) when i am currently working in a project with “react”…
note: what lead me here -> in our spare time (with my friends), we are comparing angular forms with react ones and we are trying to solve issues in angular forms listed in https://github.com/angular/angular/issues/31963
@undo76 if possible can you share a stackblitz (online) with your case/solution?
That I flagged in “Jan 3, 2017”. I do open source development as well, and my comment was not insulting.
@adam-marshall
I found a workaround using a factory to make it work with formGroupNames and FormGroupDirectives in Angular 7. It feels hacky, but it is the best solution I have found so far.
Yes, that is just shorter naming convention for my directives. In the component i have
@Input() name: string;
and in the template... <input matInput formControlName="{{name}}..."
Component attaches itself to the parent formGroup and it works as expected. Validation, blur, dirty, submit, valueChange… whole nine yards
I dig into the angular source code in
FormGroupName
andFormControlName
.FormControlName
tries to injectFormGroupName
, and can’t find it, because it is its parent only by transclusion(ng-content
), not a “real” parent. The reason it is not working depends in the way angular proceed transclusion, angular ‘lost’ actually my parent. I have here a simple plnkr that demonstrates how I lost my parent component. what I want to show there that I have issue with transclusion not just with forms.found a related issue with good menu example: https://github.com/angular/angular/issues/5126
@lazarljubenovic I believe you tagged the wrong Renan 😃
@steve-todorov - at the moment the Angular team are all hands to the pumps on getting the new Ivy rendering engine and associated parts working. I would not expect this issue to be addressed until that is complete, at the earliest. @kara might have more info.
Does anyone have a new solution / workaround for this?
When duplicating the
formGroup
directive, thenchangeDetection: ChangeDetectionStrategy.OnPush
will break some functionality of the component.Here’s a small Stack Blitz example: https://stackblitz.com/edit/angular-material2-issue-zupfgz?file=app/app.component.ts
If you click the “submit”, then the errors for the first input will be shown, but the second input doesn’t get triggered
I’m not sure where you got the extra group from.
@renanmontebelo You don’t need a custom input at all, you can simply inject it. See here for details: https://stackoverflow.com/a/46025197/2131286
Also the issue is about projection, not sub-components.
@nasreddineskandrani, you are right. My answer was directed to @adam-marshall question about nested named groups.
On the other hand, I don’t think mine is a nice solution. It is not obvious at all why I need to do so for such a simple wrapper.
What I am trying to do is to create a wrapper for inputs with labels, errors, styles. In order to make it work in nested groups with FormGroupName I have to propagate the current ControlContainer in a factory.
Demo: https://stackblitz.com/edit/angular-kklcs7
I can manage to get
viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]
working such that[formGroup]
is on the parent component, and[formControlName]
is on the child, and all works as expected.However my big problem is when
[formGroupName]
is introduced for nested FormGroups and FormArrays… I can’t figure out a way of splitting[formGroupName]
out using the viewProviders or any other means.I’ve tried using
ng-content
,ng-template
, loading components withComponentFactoryResolver
and even nested components which implementControlValueAccessor
, but it seems you need that one, concrete HTML element with[formControlName]
on, at the same depth as the[formGroupName]
.The closest I’ve got so far is to have the label, input and validation messages of the control as a component which implements
ControlValueAccessor
(a lot of boilerplate when the control itself is very simple), and then replicate the outer structure offormGroupName
for each type of control… not ideal as you can see below:Any ideas?
Any update?
@kara can you elaberate on how to make it work with viewProviders and a template-outlet?
@gschuager Indeed this is valid solution too. It will work as the
FormGroup
is the same instance 😉. However I don’t like it/use it as it is too much noise, but still this is just a personal preference.I was hoping that the angular team will address this behavior somehow as it has another side effect. You can check this issue here. Unfortunately it was closed and no answer since then 😞.