Angular Forms – Kara Erickson – AngularConnect 2017

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: AngularConnect
Views: 38,401
Rating: 4.9083967 out of 5
Keywords: angular, angularconnect
Id: CD_t3m2WMM8
Channel Id: undefined
Length: 46min 36sec (2796 seconds)
Published: Mon Dec 11 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.