Video details

Typed Forms in Angular

Angular
07.20.2022
English

Hi friends 👋!
Welcome to another new video all where we're talking about Typed Forms in Angular. Our very own Dylan Hunn shares the details! Are you already using Typed Forms in your applications? Let us know in the comments down below.
Join the conversation online: Angular Twitter → https://twitter.com/angular

Transcript

Hello, Angular community. My name is Dylan Hen, and I'm a software engineer on the Angular Framework team. Over the last eight months, we've been working on improving angular forms, and I'm incredibly excited to share this new feature with you. Today. Typed forms are part of angular 14 and are ready today for you to start migrating your application. Before we jump into the details, let's get some background on reactive forms. This might already be familiar to anyone who builds or maintains a forms application. Reactive forms are one way to build forms in angular. Compared with template driven forms, they provide a more explicit data model and state management using R, XJS, and observables. They are not always the right choice. Template driven forms are less verbose and better for some use cases. These new types we're going to see today are only for reactive forms. So that's where we'll focus. Let's start with some background on why we're making this change and how it benefits most applications that use reactive forms. Typed forms have long been the most upvoted feature request from the angular community. Additionally, this change unlocks a number of future improvements, such as better control, state change events, stricter template type checking, and much more. When designing type forms, we tried to follow some North Stars. In particular, we had four guiding principles. First, we want the types to be powerful. You should be able to confidently modify even the most complex forms, no matter how deeply nested you have form groups or form controls. Second, we don't want to completely replace forms. We want to have one unified ecosystem that we move forward together. Third, we want forms to be as safe as possible, no more surprising anys in your types when you access values or controls. And fourth, you should be able to gradually migrate at your own pace, even if you have a lot of existing forms code in your application. Now, let's consider an example. In general, every form corresponds to a schema, or the shape of the data you want to collect. This is an example schema representing a party. It has inner objects, in this case, the address field and inner arrays, in this case, the menu for the party. It's never necessary to explicitly declare your schema, but it's always useful to think about when you're working with a complex form. By contrast, we do explicitly declare the form model, and this is the corresponding form for hosting a party. It accepts inputs for the party's address for the menu, and for any other form controls you might want. Previously, interacting with complicated forms models like this one could be hazardous. For example, this code contains a subtle bug. The value of place is number, and calling a substring on it will crash at runtime. Now, with type forms, this bug will be caught at compilation time right in your editor. These new types permeate the API. You'll benefit from them when accessing form values, getting deeply nested controls, subscribing to observables, and so much more. At virtually any point, you interact with the forms API surface. This will give you much improved safety, preventing both simple typos and more complex bugs. The types also allow for robust auto completion right in your editor in the party. For example, when accessing the form's value, the idea suggests every known child of the form. This helps you navigate even the most complex forms with confidence. Although this is a big change, it's 100% backwards compatible with all of your existing forms code. When you update to angular 14, your forms will be automatically opted out of the new types. Then, when you're ready, you can turn them on one control at a time and incrementally migrate at your own pace. Now that you've got the basics, let's explore the types in action. So a moment ago, we saw a schema for building a party. Now let's have a look at some actual code corresponding to the form for this party. So let's jump into the template and we'll add a new button corresponding to hosting a party, and we will call the Party and SF Convenience Function. Now we'll jump into the corresponding component and we'll add the Convenience function for throwing a party. We will access this party from the enclosing component. This corresponds to the form group for all the parties data, and we will call Set value on this form group in order to give it a new value. However, notice that we've actually forgotten the house number. TypeScript will warn us that we are missing a key here because it expects, when we call Set Value, all of the keys in the group to be present. So let's go ahead and add it. And notice that as I type the new TypeScript, types also power autocompletion. However, I still have an error here. The type system doesn't just check for missing properties, it also just checks for all of the names and all of the types in the object. So here in particular, whereas I currently have a number key, I should have a house key. Another option would have been to use patch value instead. This still protects us against serious typing errors, but relaxes the constraint that requires us to have all of the keys present. In particular, we can just have the street key. Now, let's consider another case. Let's say that a neighbor made a noise complaint and we want to figure out which neighbor it was. So we can add a new method on our component here, and we can use the Get method. The Get method accepts a string and will automatically tokenize each field in the string, allowing us to access inner controls. And notice that the type is correctly computed. In this case, we actually have an error. Again, that's because we're trying to do arithmetic with a string key. And this error tells us we should be using the house key instead, which is numeric. Now that you've observed the new types up close, let's dive into the nitty gritty design details. So consider the form group we saw a moment ago for an address. What should be the type of address controls in this example? And on the other hand, what should be the type of address value? When working with type forms, it's important to know about the difference between the types of values versus the types of controls. As we saw earlier with the Party example, the value is the shape of your data. Here, for instance, the street field has type string. On the other hand, you can also access controls. Here. The street field is a form control which has a string inside of it. Type forms use control types heavily, although you rarely need to specify an explicit type, if you do, you should always use the control type. Another interesting topic is resetting controls. Let's consider the simplest possible form, just a single control. When we first consider this control, we see it contains a string, so we might expect the type to be form control of string. However, there's a tricky detail. When you're working with a form control, you can call reset at any time, and when that happens, the control's value will immediately reset to null. This means that you cannot always assume the value of a control is a string. In previous angular versions, this example would crash at Runtime because you cannot call a substring on a null value. Angular 14 now protects you from these kinds of errors because the type of the dog control in this example is string or null. Sometimes, though, that's not actually the behavior you want. In fact, many existing forms applications do not rely on the existing nullability of the controls. In angular 14, form controls have a brand new option called non nullable. Instead of resetting to null, the control will reset to its initial value. As you might expect, this also removes null from the controls type. In this example, we've gotten rid of the nulls entirely, and now calling substring is totally safe. A related area involves disabled controls and the effect that they have on a form's value. With angular forms, disabled controls are not included in the forms value. This is similar to how native HTML forms behave when their controls are disabled. However, this has an impact on the type of your form. When you call value, any key in the form group might have been disabled. As a result, the type knows that each key is optional, and when you use this value, the type system enforces you handle the possibility that each key might be undefined. In this example, when using cat value, all of the fields inside the cat form group are optional because they might have been disabled. That, however, is not the only case in which we might have optional keys in our form. Sometimes you want to be able to freely remove keys from the form. For example, here we call Remove Control on the name Gay. With typed forms, we can now explicitly specify which keys are optional versus which are required. This allows the type system to enforce that we safely handle controls that might be missing for form groups that are entirely dynamic where you want to add or remove many controls. We've also introduced a brand new companion type called Form Record. The typing improvements here are wide and deep, and there's so much more to be excited about. You can read about them in depth in the new Typed Forms guide, available today on angular IO and linked down below in the description. We are so excited about angular 14 and the future of typed forms. We hope that you're as excited as we are, both for this project and for the future of the forms package as a whole. Please like and subscribe and thank you for watching. See you next time.