KARA: I think the clicker has disappeared
since I left. Hi everyone, I'm Kara Erickson. As Naomi mentioned, I'm a developer on the
Angular core team, and I'm hear to talk to you about Angular Forms. Here's a quick overview of what we are going
to be discussing, we will start with what is new in Angular Forms for version 5, and
then we will go into what we are calling advanced topics, so what do I mean by that? Basically, in previous talks, I've explored
more introductory concepts like the difference between Reactive, the basics of validation,
but today, I wanted to dive a little bit deeper. Once you start building forms that are a little
more complex than first-name, last-name like you see in demos, you have another set of
questions, right? You want to start to make your forms more
reusable. You want to split them up, make them dynamic. These are some of the questions I'm going
to try to address today. So, disclaimer: this talk assumes that you
have some Angular knowledge, like you have some familiarity with the dependency injection
system, you know what a viewChild is, et cetera. We don't do much of a review. If that is not you, that's totally fine. There's lots of content online, and you can
always circle back to this talk. Okay, so before we jump in, we will do a very,
very quick review on the differences between temp plate-driven and Reactive forms. We have the same simple form here, on the
left, it is template-driven and on the right, it is Reactive. So you're template form directive is called
ngForm and that is something that is implicitly created for you every time you add a form
tag if you import this module. It has its child directives which are ng models,
and the user will - so the form NgForms corresponding directive on the
Reactive side is the formGroup directive, and this has its children, form control names
and will name themselves with the formGroup directive. With Reactive forms, you have to instantiate
your own form tree. You have this form group that you instantiated
and passed into your formGroup directive. On the template-driven side, so, on
the template-driven side, it's directives like NgModel and NgForm that will instantiate
the same underlying models. Now let's jump into something more exciting
which is what it new in Angular 5. So the main thing that is new is something
called the updateOn option, designed to give you more feign-grain control over when your
value and validation pipeline will run. Every time the value changes, your values
will update, and your validation status will validate up the form tree. That's normally totally fine but in the case
that maybe you have service-side - server-side validation or heavy processing triggered on
value changes, that might be too often for you. So, in that case, we have some new options
that you can take advantage of. So I'm going to do a quick demo so you can
see what this looks like. We will start with a basic form control. Zoom. Is that too small? Of course, my zoom is not working. Let's see. Let's see if we can make this bigger. This is a new computer. Is that understood.? Okay. Is that good? Consider why is config changing? Let's continue with what we are doing. We are citing a basic form control so I can
show you what these new options look like. We're going to add a form control. We're going to start with a few validators. And then in here, let's maybe add the placeholder
as you can see. Then maybe we can display the form's value. Let's start with that. We need actually to look at this right here. Is that big enough? You can see we have a regular form control
here. This NgNew. It starts out with an empty value and a status
that is invalid because obviously, it is required, and there's nothing in it. So every time you type, a character, you can
see that the value is updating. Also, the validation status is updating. This is our default behaviour. Let's say your error messages may be only
show on blur. It doesn't make sense for you to be running
this on every key press if it is not meaningful because you're not showing the intermediate
steps. For this, you can add updateOn, blur, that's
it. And now, when I type in it, nothing happens. The values aren't updated, the status isn't
updated. As soon as you blur the input, everything
updates so it gives you control over one that happens. You're not always going to have forms that
have just one text field. Most will have a lot of controls, so, in that
case, it doesn't really make sense to have to apply each updateOn on individually. In that case, if I can make this quickly into
a form group, right, so, let's convert this into the ubiquitous first-name-last-name form. This can be the last. And then up here, let's just make this the
form group instead. Okay. And we're going to take the validators from
here. Okay. So let's get rid of this. And it now, if you want to make this form
by default, upunder blur, you can add the updateOn option as the second argument. Blur, at the form group level. So, once again, we have two inputs. If you type Nancy, you have nothing until
you blur. Similarly, if you tripe Drew. The same thing on the form level. You can also updateOn submit. If you're only showing in your validation
errors on submit, that's as often as you would like to run this pipe line. You can run updateOn submit, and then you
need a submit button. I will make it nice-ish, okay. We will add a button and call it "submit". Okay. All right, so, once again, when you type,
nothing happens. Also, when you blur, nothing happens because
we're not doing anything until Smith. Then when you hit it, it automatically updates. That's it for the demo. A really easy demo. Going back to our slides, just a quick review
of what we saw. For individual form controls and Reactive
forms, you will use the updateOn blur as the second argument, and for updateOn - what I
haven't shown you yet is the tablet-driven version which is also very similar. For an individual NgModel you add NgModel
options and updateOn blur, and NgForm options which again is the same object. So, that's what is new for Angular 5. Now we're going to move into advanced topics. The first thing that I want to talk about
is something called custom form control. This is something you may have heard of. A custom form control is essentially a directive
that those how to integrate with Angular Forms API. If you're familiar with Angular material,
you've probably seen these custom forms before. You can see the mat-slide-toggle et cetera. These are elements that don't exist in HTML,
like the slider, the slide toggle. Others are that do exist like check boxes
but they've been designed to a material design spec and we have added functionality and features
to make it more robust. Those are some examples you may have seen
for custom form controls. There are some other use cases. For instance, you might want to wrap the control
related elements to make a custom form control. Maybe you have a text field and you want to
wrap it with its label and the error messages that come with it. Another use case is parser which are can be
implemented as custom form controls. A custom form control can be used inside a
form just like any native form control element. You can see here on the top we have our text
fields and we have a select, because we are native, and then we have my control. It is used exactly the same way, right? You add the name, you add the NgModel. It's as if this is something that came shipped
with HTML. And the nice thing about form controls like
this is that they are form module-agnostic, so you can soften into a template-driven form,
put it into a Reactive form, and no matter which model you're using, you don't have to
change anything about your implementation, so it is super useful. To kind of explain how you might build one
of these, we're going to build one together on slides, so it is not a live demo. We're going to build something called "required
text". Imagine for a second you work at some company
and you have a lot of forms, and your forms have a lot of text fields, and all of your
text fields are exactly the same. You need them all to be filled out, your app
is very strict. They require validation, and they need an
error message - the same error message. It might get laborious to keep packaging the
same logic over and over and over. You decide that I'm going to build a custom
form control and that way I only have to write required text and everything will come with
it. It might look like this. You have a text field. You can type in it. It is a text field. It has some sort of built-in validation and
error-messaging. Perhaps you use it like this. The required text selector. Okay, we are going to implement three things
for this custom form control. One we're going to support everything a regular
text field does, otherwise it won't be very useful. Two, we're going to add built-in validation
for required, and we're going to have an error message that shows when the control is invalid
for any reason. First, we're going to support everything that
the field does. To build a custom form control, you need to
build a control value access sore. That's an interface that acts as a bridge
between a form-control element in the DOM and our virtual representation of that form-control
element - the form control model. These two things need to talk to each other
in order to get the values synced up on both sides. When you type into a text field, the value
changes, it also needs to change on the model side. If you set a value programmatically in a model,
you need that to be reflected visually in the view, right. So every one of our native form control elements
like select, text fields, or radio buttons, they have ControlValueAccessSore built in. You don't see these but that's how they integrate
with the API. If you want to build a new form control, you
need to implement the same interface to tell the form's API how to interact with your control. It is pretty simple. There is three mandatory methods that you
have to implement and one optional method. To explain what these are. There's write value, which is exactly what
it sounds like - you write the value to display in the view. There is register on change, which is a way
that you can save a callback, so that you can call it when value changes from the DOM
side, so, within your component. There's register on "touched" which says you
can save a callback so the form's API can properly toggle the touch property - usually
on blur. The optional one is to set stable state to
you can enable your form control to be enabled and disabled programmatically. So, let's pretend this is our component template,
which is on the top, and we will write our component class code on the bottom here. So far, we have a text field. It is straightforward. We are going to implement each one of these
methods in the interface. So we start with write value. How we would write a value to a text field? We might want to get a hold of it using a
view child. Then set its value property. That's straightforward. Register OnChange. So a call-back function. When the value changes from the DOM. So, first we have to save the function, and
it's our responsibility to know when to call it. In this case, on the input event. When a user types the model will also be updated. Register OnTouched. Then, on the blur event, we will call this
method in the form set touch to true. Lastly, set disabled state text fields, they
have native behaviour for this so we can define the text field. Once you've implemented this interface, you
need to provide this component as a value Access sore. The reason you need to do this is so that
it can see your access sore because NgModel will inject the access sore token, and, if
you've not provided it, it's not going to see everything that you just did. Now we support everything that a regular text
field does. Now we are going to implement some required
validation. So, if we go back to our top-level form here,
where we're using our required text fields, one thing that you can obviously do is add
a required attribute to this, and that would totally work. That would add required validation. However, that doesn't make your required text
field that useful, right? If you have the right required text and also
add the required validation. Instead, we want to build this into the component
so that it comes with the component, if you use it. So we're going to learn how to do that. So you might be tempted to do something like
this, which is just add the required attribute on your internal text fields. Which is understandable. But this actually won't work the way you want
it to. Here's why. So, here is our, or my very sophisticated
diagram to explain this. So, I guess we can all use our imaginations. This is a required text component, this is
the blue box. Underneath it, in its view, we have a text
field. We have a really simple component that's easy
to reason about, but let's say our custom form control is something completely different. Let's say it was maybe a toggle button or
it could be anything. Underneath it, we don't have an internal text
field. Maybe we've divs and spans. Maybe you click on something. We don't know what is underneath your component
view, right? It could be anything. In the case of this toggle button, you're
probably not going to have any form validation attributes because they don't work natively
with things like this. The form's API only knows what you integrate,
that is your public contract with it. This allows you not to make any assumptions
about how you privately implemented your custom form control. So we have the same API for toggle buttons
and for text fields like this, and instead of something like required, we just require
that you implement the validator in the interface. It is a really simple interface, one method
called "validate", and it takes the control that's being placed on top of your custom
form control, so like the NgModel, or the formControl name, whatever it is. You want to return a set of validation errors
if it is invalid, or null if it is valid. In our case, it is really simple. We can use the built-in validators' function
pass-through control and that will take care of our validation. Similarly, with value assessors, we need to
make this validator visible to the form's API so we can provide a multiuser provider
to use it as an NgValidator, and then NgModel, they can inject the validator, and add it
to their validation. So as a review, only two things you have to
do. Add a validate method, and provide it. Okay. Last but not least. We want to show an error message when the
control is invalid for any reason. Okay. So how do we know when this NgModel is invalid? The user could have added an arbitrary validation
on top of this control that we don't know about. It's best to get hold of the model underneath
to check to see whether it is valid or invalid. That's the form control. For NgModel you can export the directive into
a template variable like dir and the form instance is always available action the control
property, so you always have access to the centre line for controlling. Similarly, with Reactive forms, if you have
a you set-up like this, you can - a set-up like this, you can use the get method. An obvious answer might be to create an input
for your form control, and pass through the form control instance directly, and that works. That's totally fine. Again, it kind of removes the usefulness of
the component we're using. If you have to pass through the control every
time, it starts to be a little annoying. Instead, what we want is a way to access the
form control internally without requiring the user to pass it in. So, in this case, you can use dependency injection. Okay. You might be attempted to do this. If you know that you're using Reactive forms,
you might inject the control form directive. If you're using template-driven forms, you
might be tempted to inject the NgModel. Don't do that. As soon as you direct one directive or another,
you're tying yourself to the forms module, making the form of less reusable and much
less portable. In addition, there's a lot - well, not a lot
- but there are a few different directives, so especially with Reactive forms, you would
have to support both form control name and directive, and if the form's API were to expand
at any point, and we were to add new directives, at some point, your component wouldn't be
as useful. Instead, you can inject something called NgControl. What is NgControl? It's the super class for all of these bottom-level
individual FormControl directives, NgModel FormControl directives in the form control
name with the forms that you would use for form grooms. What is interesting about NgControl is that
each of these form control directives will provide itself as an NgControl. This is a pretty neat trick because it means
you can inject NgControl and you will just get the closest form control directive and
you don't actually have to know what that form control directive is in your code which
means you can support everything, which is pretty nice. So it would look something like this. So you can inject NgControl, and you want
to make sure to technical crate it with self, or you might run into situations where someone
wraps your form control with their own form control, you might actually reach up and get
a form control instance that's not actually the correct one. You want to make sure you limit this to the
element you're currently on. Okay, one thing that you might have noticed
is that NgControl as I mentioned earlier is injecting value access sores, and injecting
validators. How is it possible for us to inject NgControl
when NgControl isn't injecting us? You can't do that. It creates a circular dependency. For now, we are going to remove these providers
that we added since we've kind of passed that. And we can actually set this up a different
way. So we've inventured it, so now we're injecting
NgControl, so it is our job to make sure that the NgControl is set up correctly with the
right value access sore and the right validators. We can do this by setting value access sore
on the control directive, so control.access sore equals this. On the validator side, you can set the valuators
programmatically. You want to make sure you - in order to make
this kind of flexible, setting validators is separate from updating, so you can do some
cool stuff where you set things but don't necessarily update until later. Also looking at this, you might notice that
we are overwriting any validators that might already exist on this control. So if the user was to add something on top
like mid-length or something, it would be gone. We can make this a little bit more sophisticated. We can check if control.validator already
exists, so, if there is no validator, it would be null. If it does, we can compose that validator
with the validator that we want. If not, we can use the validator required
and set them our own way. So now we are access to the control - hooray! We can go ahead and add our error message
here which is this field is invalid, which is super helpful and useful. So we can just check NGF if the control is
valid or invalid. I'm not doing control.invalid, and there's
a specific reason I'm not doing that, and that is because if you add an async validator
on top of this, they have a pending status while they're processing, so while the control
is pending, it is not valid, but it is also not invalid because we don't know, right? It's in that in-between state. In this case, you want to make sure you say
"not valid" specifically, okay? So, error message that those when control
is valid is done. Now let's move on to our next section which
is nested form. What do I mean by nested form? Maybe you have a very large form, and, inside
each form is a bunch of related stuffs, so the ubiquitous example for this is an address. These are all very related stuffs. I apologise for the US-centric address! An address, yes, it has many fields, your
name, your street, your city, et cetera. You might not want to write this same group
of fields over and over and over. Instead of a custom form control, which is
one field, as we've seen so far, we want many controls. So there are a few ways to do this. The first way is to do something called a
composite ControlValueAccessSore and what I'm calling a sub form component. We will start with composite control. This is an example of how you might use an
address component that's built as a composite control value accessor. It looks like how you use a custom conform
control, right? You have an address component, put an NgModel
on top of it and that is fine. You're implementing the same interface, the
same thing, the form's API. All it knows is that there is a control called
the dress, and it receives an object and writes an object. That's all it needs to know. Internally, though, rather than having one
text field, or divs, or whatever, we have many text fields, and, again, it is something
that's a big benefit using a control value accessor. You can use it internally. It decent care, using the public contract. If you build the address component, we have
our skeleton here for what the template is going to look like, so we have our two text
fields, and I've greatly simplified this for screen real estates here. Imagine your address is the street and industry. We might decide internally to use a form group. Once again we are going to implement the same
interface we've seen before. So we will implement right value. In this case, since we're using a form group,
we can just call this form.set value and pass the value directly in, and that should work. One quick note here. You might be tempted to use patch value here,
and you absolutely can. You want to be careful of it because it can
introduce junk values into your components because we are setting it directly here. So, if you decide to do that, make sure that
you handle these cases. The next one is register OnChange. A form group will omit any time any of the
children form controls value changes so this is perfect for what we are doing here. In real life, you would probably subscribe,
save the subscription. Going back to this writeValue method, you
think we're listening to the event but setting the value. How is that going to work? It's not going to work great because every
time we set the value, and we do a model-to-view change, we're going to trigger a value change
event, and then the view to model logic will fire. It is wasteful and will mess up the dirtiness
logic in the form's API, so, instead, what we can do when we set the value programmatically,
we can set the event to false, so we can only omit a case where it matters to us. -- an omit event. The form group also has the way to deal with
this, call it enable or disable. Then lastly, we have OnTouched. We can implement this in any way we want. I want the form group to be touched any time,
any of the child controls in the address is touched, so, in this case, I will listen to
the event, and listen to the touched in these cases. Okay. That's it. We've built our address component, so to speak,
and we can use it now in template-driven forms and Reactive forms. We can put it anywhere, and it should just
work. It is very reusable. That's one of the benefits of implementing
this way. It is reasonable, portable. The con is you do have some boilerplate. You have to implement this interface, and,
for some people, that's to the great for their use case. Maybe you don't want to optimise for usability. Maybe you have a really simple project and
you're using one forms module and module forms only and you want to split out the long form
and you don't care. That's fine. In that case, you can trade ergonomics for
usability. The trade-off is that you will have to build
this differently for template-driven forms versus Reactive forms, and I will tell you
why. Let's imagine this is your top-level form
once again. We have these regular component tags - some
general sub form and some address sub form. Let's say underneath the hood in our address
component once again that we are starting from scratch, and our component template,
we put an NgModel group with some NgModels inside it. You might expect the NgModel group to work
with the NgForms on the top level up here. However, that won't work. You may or may not have teen is before, but
you will get a no provider for a control area. This is a quick fix, a one-liner, but I want
to explain what is happening here. So what is the Control Container? It is the super class for a bunch of the directives. It is a super class for our topables like
NgForm and also the super class like NgModelGroup. So the reason the NgModelGroup is looking
for a control container is it is looking for any of these directives. So NgModelGroup its job as a directive is
to look up the tree and find its parent form. Its parent could be another group because
these could be nested arbitrarily as deeply as you like, or it could be the parent form. But ultimately, it has to resolve to a parent
form, because the parent form is - especially in an NgForms case - the thing that does a
lot of the heavy-lifting. So, in this case, the top level is the NgModel
group. You can't find the NgForm that it needs. It's going to throw an error. You might say there's an NgForm - look at
it, it's right there. That's not quite going to work. If we imagine that NgModel group is injecting
this control container, it is going to start by looking at its parent here. Yes, looking at its parent which is the required
text component. It's not going to find it. Then formally, you think it's going to look
up at the form tag that is directly above that. But, it's not quite that simple, because NgModelGroup,
if this is a simplified example of what the constructor looks like in the source code,
it has a coast decorator. What does that mean. So, a host decorator, it's going to make sure
that the NgModelGroup doesn't look above its view for its control container dependency. Why we would do this? One reason is that registration across component
boundaries like this can be risky. It means third-party components would have
perhaps unexpected side effects on your app because it could kind of reach all the way
up and register itself with anything which is not great. Also, control depends on this level of encapsulation
where things are completely separate. For instance, if you're building something
like a slide toggle and internally, it was a checkbox, you wouldn't want that check-botch's
model to - you want it to be a slide toggle, right. It depends on having these barriers. What you can to kind of get around this and
say, "No, I mean it, I want to register this NgModelGroup" is use view providers. You can provide the control container that
it is missing, and you can use the existing NgForm that's going to be in that dependency
injection chain. So, why view providers than providers? Most people use providers. If you were to provide a control container
in providers, you would be providing it at this level, and the host decorator creates
this lovely dotted line which means that the NgModelGroup can't reach up and see it. You're still going to get the same - view
providers are on a different level. If you provide it at this level, the NgModelGroup
can find the form it's looking for. Once again, you provide control container
this way, and you're done. Okay, so pros and cons. As a pro, it is quicker to set up, despite
the long explanation, and the con is that you limit yourself to one forms module. If internally you're using an NgModelGroup,
it has to be used with an NgForm when being consumed otherwise it will never be found,
right? So let's move on into the Reactive form version
of this. Again, we have our top of the form, instead
of our NgForm, model, we're using a form group directive here, and we have our address component. And then, for all Reactive forms, we have
to instantiate the form group in the component class. Once again, we are starting from scratch,
so let's assume this is our address component template, and, at a top level, it has a form
group name, and we want to register it with the form group directive that's on the top
of it. Once again, we can use view providers with
the same trick so we can provide form group directive instead of NgForm, but we have one
more problem, and that is because Reactive forms register directives, their job is to
take existing form controls that you have created and match it to the DOM, right? In this case, we don't have those form controls. They're not being defined here, so, in our
address component, we can inject the form group directive, and we can add the control
when the address component is used. This way, obviously, the top-level form doesn't
have to add the address component and also add the controls, which would be confusing. So that is it for nest the forms. Our last topic of the day is form projection. So, what is that? So, we're going to explain this with two examples
or two use cases. The first is an error aggregator. So what is that? If you do a lot of online shopping, like some
of us do, you might be familiar with a shipping address form that gets very angry if you're
missing fields. If you can see at the top here, all of the
areas are aggregated at the very top. So obviously, you can do your error message
as you like, but this might be a common pattern you're using throughout your app, so you want
to build some sort of form that wraps this aggregation logic so you don't have to do
it over and over and over again. So, maybe your plan is to do something like
this. Internally, you have a form tag. Again, this is your component template and
component class. Internally, you have a form group and you
want to project the form contents - arbitrary form contents - from any form into in form
tag inside the component. So, perhaps you would use it like this. You would have a wrapped-form component, and
as its content children, you would have the nested form group name. So, if we were to continue with this, you
would have some sort of error logic here. I'm not going to go into it because that's
not the point of this, but the problem is this is not actually going to work. Why is it not going to work? So, if you go back to our sophisticated diagram,
we have our wrapped form component here, and, underneath it, we have our wrapped form view,
which is our form group. But it is actually a little bit nor complicated
than that. We also have content children, so we have
both our view children, like FormGroup, and our content children, like the form group
name we are trying to inject into it. If the form group is looking for its form
container, it will only look up. It can look for it in the wrapped form, it
can't look for it inside the wrapped forms view. It's not going to find it. How do we solve this problem? The first strategy is don't do this! Don't project forms. Don't worry, I will give you an alternative. A better way often is to relay the control
instead. Why? If you're projecting forms, similar with nest
the forms, you're - nest the forms. You're tying yourself to one forms module. If you have a forms group or NgForm, the only
thing you can project into it is the corresponding directives, right? So, again, you're limiting your reusability
and your portability, et cetera. Also, typically, there's a better design. There's probably a way for you to break this
up into pieces that are a little bit more understandable than something like this complicated
provider tree. Also doing so can be tricky to get right,
and I will show you that in a second, so you will believe me! Okay, better design. What is another way you can build this error
aggregator instead of putting a form tag and an error tag inside the component? Well, you could create an error-aggregator
component that only has the error aggregator logic and your user can wrap that error-aggregator
with any form tag or forms module they choose. In this case, it is really obvious what is
happening and you're relaying the form group into the error aggregator. Another huge benefit of designing it this
way, like I said, you can use it with both template-driven and Reactive forms, and it's
just going to work because you're just passing through the form group which is common to
both modules. In this case, we have an input that takes
the form, and most of the other logic isn't changed. Okay, so let's think of another use case. What if I had a form wizard? So you may have seen us in Google Forms or
other forms around the internet where you have a bunch of sub forms, and you can't move
on until you hit next, and you can't hit next until your form is valid. It takes you through the form process, but
each step must be valid before you can progress. So, if you were to build a step or components
where arbitrary forms could be nested inside it, your forms, or your form stepper would
actually need to know about the validity state of each sub form, right? So it's easier to see if you look at it. Let's imagine again we have our informal form
tag, and then in our application-level code, we might use it like this, we have a form-stepper,
and trying to content-project all of our arbitrary steps which can be any form at all, and then
each step contains part of the form, right? So that might be your initial design. Strategy one: don't project forms. That's one strategy. Relay the control instead. This is what we do with the Angular material
stepper. So there is a real use case out there. So, in this case, you could do something like
each step could be its own form. That's something you could do. If that is the case, then you can go ahead
and pass that control directly into the step, and that way you can figure out whether the
control is valid before you press the Next button. Again, this works with Reactive and with template-driven. Another strategy might be to instead have
each step be its own form, you could wrap all of your steps in one form and use the
magic of form arrays to make this work. This is a Reactive-only strategy, but you
could pass in the steps right here, and then each of your steps, the form group name, could
match the step index, and that could be the way you get it from the array as you go through
your steps. Which brings us to strategy two: if you really,
really need to project your form, you have a really specific use case, you can do it. Huge disclaimer, though: it is tricky to get
right, and it leads to bugs, so please be careful of this pattern. Okay. So, we are back again with our initial design. How do we make this work? So, again, we have a diagram. We have our form-stepper component at the
top, and we have our form-stepper view, which is our form tag internally. And then we have our FormStepper conpent which
is our NgModel group. So how do we provide to this NgModel group
in the content children the form that's in the FormStepper's view children? How do we do that? Somehow, we have to get that information to
the parent, so the FormStepper, because that's the only thing that the content children can
really see. We can do that with a viewChild query. We can grab the NgForm that is underneath
here. Then we can provide the control container
using a factory. So, we can get a hold of the form-stepper,
and then we can look at the form property to get that viewChild that we just created. That's blurry. This is from angular.io. How will it work in content is always processed
before viewChildren. Content happens. Won't the control container always be undefined? And you would be right: initially, it will
be undefined because of the order of things that happen. So, to make this work, we have to wrap our
step in an NgTemplate so we can choose when this is intansiated and when this is processed. Internally, instead of using an NgContent,
which you might be used to, we can use an NgTemplate outlet, and use a content child
query to grab the NgTemplate that we projected. So, you can see why that is a little bit complicated,
right? But it is possible, and you will see this
example and all the examples for the code today that we went through. Online, I will be tweeting it later, so, keep
an eye out. So, in conclusion, to review, because I know
that was a long talk, we have new validation timing options, as you've probably forgotten
about by now. Those are really exciting. Custom form controls are useful and powerful
and you can use them for a lot of things and they're not scary. Nested forms are possible, and what strategy
you use depends on your use case and the trade-offs. Don't use form projection if you can help
it. Thank you! [Applause]. [Music]. NAOMI: Thank you, Kara. Time to play musical chairs. If you're staying here, don't go anywhere
if you want to hear from Victor Savkin on state management patterns, followed by MobX
with Adam Klein. Head over to Pluto if you're interested in
the panel on advanced Angular topics. Please remember to post your questions for
the panel this afternoon. We will see you soon.