Angular: Nested template driven form

Alexey Zuev
Oct 14, 2017 · 3 min read

In this article i am going to show how we can build nested angular template drived form.

I am assuming you have already known what ngForm, ngModel, ngModelGroup directives are for.

So let’s start with simple form:

Stackblitz example

There is nothing new here for those how do it every day. We’ve written form containing firstName and lastName fields and we’re expecting the result like:

{
"firstName": "",
"lastName": ""
}

Now let’s imagine our client has changed requirements and we have to add address fields to our form:

Stackblitz example

and the value will look as follows:

{
"firstName": "",
"lastName": "",
"address": {
"zip": "",
"street": "",
"city": ""
}
}

As the app grows inevitably you will face the situation when you need to reuse address fields from the preceding template.

That’s time to refactor our code. For that we can create AddressComponent

address.component.ts

and use it in parent component:

parent.html

Stackblitz example

The problem

Unfortunately, as soon as we do it, we’ll get the error:

Template parse errors: No provider for ControlContainer (“[ERROR ->]<fieldset ngModelGroup=”address”>

Hmm… Angular complains about ControlContainer provider for ngModelGroup directive.


Let’s try to find solution

— Where does ControlContainercome from?

If we take a look at source code we can notice that NgForm and NgModelGroup directives provide it within their providers array.

Here is excerpt from ng_form.ts :

from ng_form.ts

This way angular can’t find ControlContainer provider that is declared on form element(NgForm directive lives there).

Now let’s open source code to see where angular requires it.

As we can see angular blocks search for ControlContainer provider by using Host decorator.

The documentation states that Host decorator:

Specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component.

Let’s get back to our AddressComponentand take look at its template:

In order to find provider angular will walk up from fieldset until it reaches address host element.

But our parent ngForm is located within parent template while NgModelGroup directive requires parent element within current template something like:

<some-element ngForm>   <some-element ngModelGroup>

The most straightforward solution would be passing parent FormGroup to child component. Such approach is typical for model driven form but we can’t pass FormGroup to ngForm directive.

Let’s get back to the error:

No provider… No provider… No provider…

Seems angular wants us to declare ControlContainerprovider. So let’s do it!

For those who know how angular DI works it should be easy. Host decorator gives us the opportunity to get a provider from viewProviders declared for host element:

addres.component.ts

That’s all.

Working example could be found on Stackblitz

Alexey Zuev

Written by

I’m a Frontend Architect at Waveaccess

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade