I have subscribed to the Angular Forms course. No words, it's just amazing. I highly recommend it to everyone who wants to reach an advanced level in Angular Forms.
Very cool! I actually did the same thing recently, but did it slightly differently: a) I added the Validator to the field permanently but enabled and disabled them instead of added and remove them b) I transferred the valueChanges observable into a signal and used angular effects for switching. BTW: I really enjoy your channel! You made me a better angular developer! :)
Thank you Dmytro. Great content. I have a question about your course. Are you updating its content with the features of the latest versions of Angular? (17 and 18 for example)
I subscribed to this course also last year :-) great course absolutely :-) would be cool if you could update it to Angular 17/18 features :-) especially Signals :-)
Hi :) Thank you! Honestly, I don't remember that the control models have any mechanism of auto-unsubscription, so I would definitely unsubscribe explicitly. I personally follow the rule - Unsubscribe-Always ;)
I think using cross validation is better when control validation is related to other control values within the form, where we can get the year value from form.value.year and check it conditionally every time conditionalPassportRequired(control: AbstractControl) { const {year, passport} = form.value; if(year > x && !passport) { return {passportRequired: true} } return null }
Hi, Thanks for your suggestion. As always, in web development, things might be done in many different ways, and each solution has its own pros and cons. Let’s see if the cross-validation at the root FormGroup is indeed a better solution: The cross-validator on the root level of the form will invalidate the root FormGroup and not a particular control (passport). This will lead to multiple problems: 1. Inconsistent error checking. The error object returned by the validator will be added to the root FormGroup.errors. This means that in order to check the passport field against the ‘required’ error, you would need to do it in the root FormGroup like this.form.hasError('passportRequired’). To me, it is quite confusing and not obvious because the error object should “live” in the control it belongs to and not in the root FormGroup. 2. Inconsistent control validity state. From an Angular point of view, your passport control will remain valid. This means that it will get the ng-valid CSS class. In this case, you might encounter a situation where your field got a green border (indicating a valid state), but below will be displayed an error message that the field is required. This is quite confusing UX. 3. Scaling. At first glance, this solution wouldn’t scale well. In an actual application, you usually have an object/map like “validatorKey -> Error Message” to keep validation messages consistent across the whole application. Because you introduce a new error key “passportRequired”, you would need to add this key also to the error message map, and the message for this new key should most probably be the same as the message for the regular ‘required’. So if later you would need to change the error message for the “required” validator, you have to update it also for “passportRequired”. And here, we are talking only about the passwordRequired case, but in reality, you might have other dynamic validators, which will make error message management quite challenging. 4. Performance impact. Most probably, if the validator logic is lightweight and the form is small, you won’t see a real performance impact; however, it is definitely worth mentioning that the validator in the root FormGroup will be triggered every time when any FormControl value changes in the form. It makes no sense to do that because we are interested only in changes from the yearOfbirth control. Those are the first thoughts that came to my mind this late night :)
Hey but what if we assign the validator to the passport FormControl and the change validator code to access "control.parent.year" and return "required" error name :) ?
Can you please tell me how to configure angular form disable/enable functionality? I want to disable a form group in situation A and re-enable it in situation B while keeping the status of the children AND ignoring them in form value. From my perspective it's just either disabling and enabling zhe group itself, which still includes children's data in form data, or disabling and enabling the childs too, but overriding their status, if the were enabled/disabled on their own. To me it sounds like a big downside and i can't imagine why it's done this way.
If user selects 10 times an adult date, won't be the validator added 10 times? Or angluar sees that the object is already there, and won't add it many times?
Good question. This is what says the comment in the src code about that: “Adding a validator that already exists will have no effect” - github.com/angular/angular/blob/101edda0184b458d331163b7e04280c3b8b48bfc/packages/forms/src/model/abstract_model.ts#L788
Thanks for the question. Yes, updateValueAndValidity() will trigger the valueChanges and statusChanges events. Whether it is a desired behavior or not depends on your particular use case. Nevertheless, if you don't want those events to be emitted, you can call it like this: updateValueAndValidity({ emitEvent: false })
Is assigning that Validator reference to a local variable for minLength really the "official" way of doing it? I think it would be more cohesive to have some sort of option for it in the method call add/removeValidators. Slava Ukraini!
Once the yearOfBirth control is defined with an initial value, it makes sense to add a required validator to the passport control when it's defined, and not by using the startsWith operator. Слава Україні!
I'm confused by the assignment to this.skills$. I can't fully see what is being done here as the rest of the file is not shown, but why are you using tap here rather than map? If I'm not mistaken, tap will not return anything, so this.skills$ will never emit anything. I also can't see where the subscriptions to this.skills$ are.
Hi, thanks for the question. In the context of exactly this lesson, this.skills doesn't play any role. However, to answer your question, I use the tap operator because the code inside performs side effects (new FormControls are registered), and the tap operator is exactly the place where side effects have to happen. The map operator should be a pure function that simply transforms data from A -> B, but that was not the case there.
@@tarquin161234 well, the alternative would be to put logic from tap to the subscribe() callback but from the functional point of view it should be the same and it is mostly a matter of taste. I even saw the thread about this topic on Twitter and opinions were split pretty much equally :)
Ah, ok. Now I see what confuses you. Actually, the subscription to this.skills$ happens via the async pipe in the template. This will trigger an HTTP call inside the getSkills() method, and once data arrives, it goes to the tap operator where, based on returned data, I create corresponding FormControls and add them to the form (calling buildSkillControls()). Because buildSkillControls() performs side effects, I placed it in the tap operator because, in my case (since I subscribe via async pipe), the tap operator is the only place where I could handle side effects. I hope now it is clearer :) It might look strange because you don't know the context of the course and what was done before :)
Learn Everything About Angular Forms 🚀
bit.ly/advanced-angular-forms_yt
The angular forms course is gold quality. I subscribed last year and helped me a lot in my development
I am so happy to hear that! Thank you for sharing your experience!
P.s. I swear the comment wasn't a paid ad 😅
😂@@DecodedFrontend
I have subscribed to the Angular Forms course. No words, it's just amazing. I highly recommend it to everyone who wants to reach an advanced level in Angular Forms.
Very cool! I actually did the same thing recently, but did it slightly differently:
a) I added the Validator to the field permanently but enabled and disabled them instead of added and remove them
b) I transferred the valueChanges observable into a signal and used angular effects for switching.
BTW: I really enjoy your channel! You made me a better angular developer! :)
I wish I could like your Angular Form course million times. Thank you very much for your generusity in teaching complex issues in a very simple way.
Dmytro, could you please make a video about Angular Material theming for v18? The documentation is confusing.
Thank you Dmytro.
Great content.
I have a question about your course. Are you updating its content with the features of the latest versions of Angular? (17 and 18 for example)
I was time travelling here, nice video.
Haha, yes :)
Top quality content!
Any plans for making a course all about signals with Angular?
Thank you for the video Dmytro
I want to thank you for your gold videos you helped me a lot ❤❤❤❤❤❤❤❤
I'm so glad to hear that! Thank you for the comment :)
You are an awesome guy, love your videos ❤️
Useful video
I subscribed to this course also last year :-) great course absolutely :-) would be cool if you could update it to Angular 17/18 features :-) especially Signals :-)
Once the signals will be adopted by Angular Forms, I will definitely to the update :)
Hi Dmytro nice video. Question: do we also have to unsubscribe onDestroy when subscribing to a form control, or angular takes care of that? Thanks
Hi :) Thank you! Honestly, I don't remember that the control models have any mechanism of auto-unsubscription, so I would definitely unsubscribe explicitly. I personally follow the rule - Unsubscribe-Always ;)
I think using cross validation is better when control validation is related to other control values within the form, where we can get the year value from form.value.year and check it conditionally every time
conditionalPassportRequired(control: AbstractControl) {
const {year, passport} = form.value;
if(year > x && !passport) {
return {passportRequired: true}
}
return null
}
Hi,
Thanks for your suggestion. As always, in web development, things might be done in many different ways, and each solution has its own pros and cons. Let’s see if the cross-validation at the root FormGroup is indeed a better solution:
The cross-validator on the root level of the form will invalidate the root FormGroup and not a particular control (passport). This will lead to multiple problems:
1. Inconsistent error checking. The error object returned by the validator will be added to the root FormGroup.errors. This means that in order to check the passport field against the ‘required’ error, you would need to do it in the root FormGroup like this.form.hasError('passportRequired’). To me, it is quite confusing and not obvious because the error object should “live” in the control it belongs to and not in the root FormGroup.
2. Inconsistent control validity state. From an Angular point of view, your passport control will remain valid. This means that it will get the ng-valid CSS class. In this case, you might encounter a situation where your field got a green border (indicating a valid state), but below will be displayed an error message that the field is required. This is quite confusing UX.
3. Scaling. At first glance, this solution wouldn’t scale well. In an actual application, you usually have an object/map like “validatorKey -> Error Message” to keep validation messages consistent across the whole application. Because you introduce a new error key “passportRequired”, you would need to add this key also to the error message map, and the message for this new key should most probably be the same as the message for the regular ‘required’. So if later you would need to change the error message for the “required” validator, you have to update it also for “passportRequired”. And here, we are talking only about the passwordRequired case, but in reality, you might have other dynamic validators, which will make error message management quite challenging.
4. Performance impact. Most probably, if the validator logic is lightweight and the form is small, you won’t see a real performance impact; however, it is definitely worth mentioning that the validator in the root FormGroup will be triggered every time when any FormControl value changes in the form. It makes no sense to do that because we are interested only in changes from the yearOfbirth control.
Those are the first thoughts that came to my mind this late night :)
That's was great deceleration, thanks 👍
Hey but what if we assign the validator to the passport FormControl and the change validator code to access "control.parent.year" and return "required" error name :) ?
I think the problem for this is that when we change code control won't trigger the passport validator
hey, thanks for your wonderful forms course. How can i get the source code of this video to reference?
Helpfull video
Why not use new features like the new control-flow or the takeUntilDestroyed operator
Because at the moment when this video was recording, those features were not released :)
Can you please tell me how to configure angular form disable/enable functionality? I want to disable a form group in situation A and re-enable it in situation B while keeping the status of the children AND ignoring them in form value.
From my perspective it's just either disabling and enabling zhe group itself, which still includes children's data in form data, or disabling and enabling the childs too, but overriding their status, if the were enabled/disabled on their own.
To me it sounds like a big downside and i can't imagine why it's done this way.
If user selects 10 times an adult date, won't be the validator added 10 times?
Or angluar sees that the object is already there, and won't add it many times?
Good question. This is what says the comment in the src code about that: “Adding a validator that already exists will have no effect” - github.com/angular/angular/blob/101edda0184b458d331163b7e04280c3b8b48bfc/packages/forms/src/model/abstract_model.ts#L788
If we do updateValueAndValidity() isn’t it just call the valueChanges() again and again
Thanks for the question. Yes, updateValueAndValidity() will trigger the valueChanges and statusChanges events. Whether it is a desired behavior or not depends on your particular use case. Nevertheless, if you don't want those events to be emitted, you can call it like this: updateValueAndValidity({ emitEvent: false })
Why not to use a custom validator?
For some reason I had expected something better written than what I saw in the video. Like some util. But any case, thanks
cool 😎
Is assigning that Validator reference to a local variable for minLength really the "official" way of doing it? I think it would be more cohesive to have some sort of option for it in the method call add/removeValidators. Slava Ukraini!
Why not just create a validator for a form?
upload the course in udemy pls
Once the yearOfBirth control is defined with an initial value, it makes sense to add a required validator to the passport control when it's defined, and not by using the startsWith operator.
Слава Україні!
Героям слава 🇺🇦 Yes, it could be an option as well 👍
Use toSignal instead
I'm confused by the assignment to this.skills$. I can't fully see what is being done here as the rest of the file is not shown, but why are you using tap here rather than map? If I'm not mistaken, tap will not return anything, so this.skills$ will never emit anything. I also can't see where the subscriptions to this.skills$ are.
Hi,
thanks for the question. In the context of exactly this lesson, this.skills doesn't play any role. However, to answer your question, I use the tap operator because the code inside performs side effects (new FormControls are registered), and the tap operator is exactly the place where side effects have to happen. The map operator should be a pure function that simply transforms data from A -> B, but that was not the case there.
@@DecodedFrontend It's just weird seeing tap with no other operators in the pipe. I was curious to see it's uses.
Good video anyway
@@tarquin161234 well, the alternative would be to put logic from tap to the subscribe() callback but from the functional point of view it should be the same and it is mostly a matter of taste. I even saw the thread about this topic on Twitter and opinions were split pretty much equally :)
@@DecodedFrontend sure. But from my pov it's confusing because you can't see any subscriptions so looks like the tap will never execute
Ah, ok. Now I see what confuses you. Actually, the subscription to this.skills$ happens via the async pipe in the template. This will trigger an HTTP call inside the getSkills() method, and once data arrives, it goes to the tap operator where, based on returned data, I create corresponding FormControls and add them to the form (calling buildSkillControls()). Because buildSkillControls() performs side effects, I placed it in the tap operator because, in my case (since I subscribe via async pipe), the tap operator is the only place where I could handle side effects.
I hope now it is clearer :) It might look strange because you don't know the context of the course and what was done before :)