Charles Max_Wood:
Hey, welcome to another episode of Adventures in Angular. This week on our panel, we have Lucas Paganini.
Lucas_Paganini:
Hey Chuck, good to be back again.
Charles Max_Wood:
Yeah, good to have you back. I'm Charles Max Wood from Top End Devs. And yeah, let's dive in and talk about custom form components. Now, you were kind of explaining to me that this will save people a ton of time. And I can imagine how that works. But do you want to just kind of give people I guess the value proposition of using custom form components in Angular and yeah, effectively why they would want to do it?
Lucas_Paganini:
OK, OK. So what I'm going to do is I'm going to just disclose a normal scenario that Angular developers have. And then I'm going to show the problem with that, because it's not exactly a matter of why people should use custom farm components. It's a matter of people just generally have to create them. Imagine you're creating an application. At some point, you're gonna have forms, you're gonna have inputs, you're gonna have ways that the users give information, and somehow they click on a button, whatever happens, you send this information to your backend and you do something with this information. So when we're building web applications, we're always collecting information somehow. The question is, how can you create those components? those custom components that will be used in your forms in a way that integrates with the ecosystem that Angular had already created. So you're
Charles Max_Wood:
Mm-hmm.
Lucas_Paganini:
gonna create a date picker, or let's say that you're creating a system that you need to give rating to users, so you have like five stars, two stars, four stars. So you want to create a custom component that you just click on the amount of stars and you register as a number. So all of these are things that you're gonna have to create yourself. Like a native HTML doesn't provide you those solutions from the get-go. So you're gonna have to create them yourself. Of course, you can use third-party libraries, you can use Angular material, all the other libraries that provide you with components that you can use on your forms. But probably you're gonna have to create your own components eventually, especially if
Charles Max_Wood:
Mm-hmm.
Lucas_Paganini:
you're just coding a design that somebody else created. So the question at this point is, how can you do this process of creating new form components in the most frictionless way possible? The way that you don't have to type as much code, the way that you can reuse as much of what already exists in the framework as possible. This is what we're gonna talk about here today.
Charles Max_Wood:
That makes sense. I have a quick question on this and that is, let's say you're doing the date picker. Like I've seen date pickers that they kind of have a singular value, even though you are kind of, you could break it up into values for, you know, month, day, year, or even a month, day, year, hour, minute, you know, whatever time zone, AM, PM. When you create these, do you typically just have it all have one value, like one date value, or do you kind of internally store? all that in different values and then if you send it to the back end, do you send it as one value or multiple values? And I'm assuming you can do both it either way, right? Because it's software, but anyway.
Lucas_Paganini:
That's a great question. Let me just check to see if I understood this properly. So I believe the question you have is, if we're creating a component, a form component that is very complex, such as a day picker, then this component many times has more properties than just the actual value. So for example, you also need to inform if you're using... AM-PM or a 24-hour format. You also need to inform the time zone. So when you are dealing with components that they have more state than just the primary value, you also have some secondary options to customize the component. In this scenario, how do you store it? Is that your question?
Charles Max_Wood:
Yeah, basically, but it sounds like what you've, the way you described the question answered my question in the sense that you have a primary value and then you derive the secondary values from the primary value.
Lucas_Paganini:
Exactly, this is what I generally do. I have a primary value that is flexible enough that I can in it aggregate those other informations, such as what time zone
Charles Max_Wood:
Mm-hmm.
Lucas_Paganini:
was used, the format, and then I can derive the secondary information from that, or I can just get rid of the secondary information and the secondary information can be something that is just... in the front end is not sent to the back end, I can just send the primary information. And then that means that I'm gonna lose something when I get this data back, because for example, you may send a date that is in a time zone, but if you just send the UTC date, you don't send the time zone that was used. When you get this back from the back end, you don't know the time zone that was used. So you
Charles Max_Wood:
Mm-hmm.
Lucas_Paganini:
have the correct date, but you don't know which time zone the user wanted to display that, but that's okay, because it's still the correct. date globally speaking. So you have to choose on when do you want to keep that information or not. So I've worked in a system that they wanted to keep this information. Like for every single input, it wasn't a global option. So many times, if we're talking about the specific case of defining the time zone of your dates, in many cases, this is just a single global option that you set on your application. So you just say, globally speaking, What is the time zone that should be applied for all dates that appear in the system? So many systems are like that. So in this sense, you do not have to store this information on an input per input basis. Why? Because this is already a global information that is probably like a user preference. So when you send data to the backend, you don't need to send the time zone because this is already saved in the user preferences. You can just say, you can just send the UTC date. because this is the only value that is important to you. But it really depends on your scenario because I've worked in an application where you did have a global option to set the time zone of the entire system. But every time that you would set a date input, you could also change the time zone for that particular input. So it was a data analytics platform, so it made sense to them that people could change the time zone based on the dashboard that they were using. So we wanted, when we were saving the value from that date input, we wanted to save not just the actual international date, but also the time zone that was used. So that when users reload the page, the time zone that they selected would still be there. So it really depends on your scenario. I would say everything that you want to keep after you reload the page, it's a primary value and should be stored in the primary value. Everything that if you reload the page and it goes back to the default values and you're okay with that, this is secondary information and it can be just a local storage value. It doesn't have to be sent to the back end, it doesn't have to be the primary value, it can just be a property in your component.
Charles Max_Wood:
Awesome. All right, so now that I've taken us off on our first tangent, let's get into how to build these components. Because I think everybody's familiar with building a component, right? Some part of the UI that does something, and you can set up events on it and things like that. What makes it different from the form component, or the form control component, or whatever? I guess you're subclassing something, or something like that, right? So what makes it different, and how do you start setting that up?
Lucas_Paganini:
So what happens is people tend to apply the same concepts that they have for normal components to components that we'll be using forms. So you just said people can create components, they set up inputs, they set up outputs that are events that are going to be outputted from the component. So people think, oh. If I know how to create an input and an output, then I can create a form component. A form component is nothing but a component that inputs a value and outputs events when something happens. So for example, the value has changed. Let me output a change event. You can apply this module, this model, and this is what most developers do. But they realize later on that They are repeating a lot of logic in a lot of places, and they are not getting their component to integrate with the solutions that Angular already provides to handle forms. So let's get back one sec and talk about what are exactly the APIs that Angular provides to interact with forms. There are two. It's the forms module and the reactive forms module. So... Angular provides those two natively in the framework for us to interact with forms. And Angular already provides implementations that adapt the native HTML form elements to the Angular Forms API. So for example, you're using a regular native input element, just an HTML input element or an HTML select element. Angular already integrates those elements with the forms module and the reactive forms module. So you can, and this is what Angular shows to you in their docs, you can easily just add form control or a form control name or even an ngModule if you want to do two-way data binding. So you can add those directives. to those native HTML inputs, and it just works. When you put those directives in them, they automatically bind to the form controls that you created in your component. And from those form controls, you can control the value of those inputs. So if you're using template-driven syntax, that means that you're using the forms module. In that... scenario, you are probably going to rely on two-way data binding using ngModel. So instead of explicitly declaring the form controls in your Angular component, you are just going to declare properties. So imagine that you have a form that collects a name and an email. Name and email. So you're gonna, if you're using the template-driven syntax, you're gonna have a component that has two string properties. One of them is called name, is a string. The other is called email, it's a string. Then in your component template, you're gonna have an input for the email, an input for the name, and you're gonna use the ng-model directive using the banana syntax, which does two-way
Charles Max_Wood:
Mm-hmm.
Lucas_Paganini:
data binding, and you're gonna put
Charles Max_Wood:
Right.
Lucas_Paganini:
the property of your component. So every time that the input changes, the properties in your component are gonna change too. If you're using the reactive forms module, you're gonna do this in a more explicit way. So what does it mean to be explicit? It means that you are going to get rid of the magic that connects the properties from your components and you're gonna connect things more manually. So in your component, you're gonna instantiate name control and this is not gonna be a string. It's gonna be a form control with the type string. So it's a form control that controls an input. that emits strings. You're gonna also have an email control property, which contains a form control typed as string. Then you're gonna go in your template and you're not gonna use ngModel. You're gonna use form control, the form control directive, or you're gonna use the form control name directive in case you're using a form group. So all of that here, all these complications that we were talking about here was to connect the state of your component to the native HTML inputs using the APIs that Angular already provides us to control forms. Now, if you try to create your component using regular inputs and outputs, you won't be able to use the same API. So you can't just create a component and put the ngModel... banana syntax directive on it. It's not going to work, not out of the box. You can't just create a component and put the form control directive on it. It's not going to work. Angular doesn't know how to connect your component to a form control. And most developers, they don't even know that this is possible, because quite frankly, the Angular docs don't make it very clear. I myself was taking a look at the current Angular docs, which is already in version 15 before starting this conversation. And I couldn't find a doc guide in the Angular docs that would explain how you can create components that work with form controls, that can connect to a form control. And then you can just use ngModel, form control, form control name, all those properties that you're already used to control native HTML inputs. How can you do that to... through custom form components. So this is the challenge here. If you don't go this route, what do you lose? So this is the big question. Okay, Lucas, like, I don't know what you're talking about. I've never used that shit, but my application is working. Like I'm using inputs and
Charles Max_Wood:
Right?
Lucas_Paganini:
outputs. Everything's fine. Like, why are you pushing me towards that? What is the need? Okay. Let's say you create a date picker, a custom date picker, and you're just doing that in the most naive way possible, which is creating that as if it was a normal component. You're gonna create an input, which is probably gonna be called value, which contains the current value of your date picker. But you're gonna pass this value from the parent component to it. but you also need to track changes to it. So the parent component doesn't just want to pass a value, they want to know when the value changes. So now you need to let the parent component know when the value changes. Okay, so how can you do that? Well, you can just create an output that is like on change. So the parent component can listen to when your date picker component changes and they can get the new current value. Cool, okay. Oh, but there is another problem. you have a logic that you only show the error messages or the validation messages after users have already touched the input. So you don't show the error messages when the input is untouched. So now you also need to keep track of if the input is pristine or if it's already dirty. And you need to emit an event when that happens. Okay, that's fair, not too many things. But... Now, suddenly there's yet another thing, which is after you submit the form, you need to reset the state of your form component. So you need to make it pristine again, you need to make it get back to the initial value, and you need to do that maybe without emitting some of the events, because you're just re-initiating that. You don't want this to be like a new change, you're just resetting it. Now, your component starts to get a lot of logic that wasn't the purpose of the DatePicker component. Like, this is the purpose of a general form component. The DatePicker should only be responsible for creating a UI in which you can pick a date. Like, all those other things, you should be able to isolate that logic in a single place and reuse it.
Charles Max_Wood:
Mm-hmm.
Lucas_Paganini:
And Angular has already done it for you. This already works for native HTML inputs. So why not reuse it for your custom form components? So it's more of a quality of life improvement than if you don't do it this way, you can't create custom form components
Charles Max_Wood:
right.
Lucas_Paganini:
in Angular. You definitely can. You're just gonna repeat yourself a lot. The code is gonna be... more complicated than necessary. And you're probably gonna have some performance issues too because if you do it the regular way, you're probably gonna trigger change detection way too much, way more than necessary. So Angular is gonna keep re-rendering your component every time the value of the component changes, which is a very common issue that people have when they're creating custom form components. So if you just... do it the other way that we're gonna talk about here today, you get rid of all those issues.
Charles Max_Wood:
Makes sense to me. Do you have any questions to brought so far?
Subra_Mishra:
Yes, so about that one, sorry, first of all, sorry for the late join and about the custom component component. What I remember, what I gone through my my past career is it's always helps on. If you have some custom needs, like you told you, if you if you have only two fields like a email, email ID or name, then it's I don't think it's always good to go for a custom component. if you are in hurry or if you have already created a custom form and that you can use directly with some events and all, then it should go for it or in that case template syntax or template or normal reactive forms would also be help. And also embedding a custom form with observable and NGRX and make them Like you are telling about the make like enabling the on push change detection and those things will will help a lot to the application rendering process.
Lucas_Paganini:
Yep. That's a good point. So if you don't need to create a custom form component, you can just create a component for the entire form instead of a component for an individual input, then that's much easier because you can just bind directly to the native
Subra_Mishra:
Yeah.
Lucas_Paganini:
HTML inputs. Angular has already done the work for you to create those bindings. You can already bind to them and you can just... use CSS to style them the way you want. So totally.
Subra_Mishra:
hmm yeah one more thing also like i said i think i it might i might be wrong uh in some terms i saw like a lot of uh projects uh in terms of making smaller components uh they go and divided each uh input as also make a separate component try to use that so According to me, if it is done, it can be done in a native HTML way, forcing it to do it as Angular might be a overburden in the future. Like suppose your project is becoming big, now you have two, three teams are planning to work on the same project. Then if you are creating a component for each and every everything. That might be a overburden like a over engineering thing on top of What native HTML or native CSS can provide by default? So just to add on to your point Like according to the requirement we should like act upon either we need the custom Component or it's like my firm by default the HTML can can handle it pretty efficiently Without going through the angular All chain detection and everything need to be gone through that. So that's one point from my side.
Lucas_Paganini:
I tend to avoid that, I get to your point. I think that indeed, every time that you can just use the native solution without making things more complicated by making it the Angular way, the framework way, or whatever other framework you're using, it's usually better to just go with the native solution. In the case of forms, I am more opinionated in
Subra_Mishra:
Mm-hmm.
Lucas_Paganini:
the sense that... I always prefer to use the Angular way of handling forms and I always use reactive forms. Like I never use the forms module using the template driven syntax because
Subra_Mishra:
Yeah.
Lucas_Paganini:
one common characteristic that I always aim for in every single application that I or my team is working on is we always want it to be reactive. So we want that. when a single thing changes in a particular place, every other places that depend on that can answer accordingly and like in real time. So without needing to reload the page or anything like that. And using the Reactive Forms module just makes this so much easier because the Reactive
Subra_Mishra:
Because
Lucas_Paganini:
Forms module already wraps the form inputs into observables.
Subra_Mishra:
the next question, how
Lucas_Paganini:
And if I already
Subra_Mishra:
do you
Lucas_Paganini:
have
Subra_Mishra:
already
Lucas_Paganini:
observables, I
Subra_Mishra:
walk?
Lucas_Paganini:
already
Subra_Mishra:
You said you walk,
Lucas_Paganini:
have...
Subra_Mishra:
but once you put the feet
Lucas_Paganini:
the primitive that
Subra_Mishra:
into
Lucas_Paganini:
I
Subra_Mishra:
the subsurface,
Lucas_Paganini:
need
Subra_Mishra:
you
Lucas_Paganini:
to
Subra_Mishra:
already
Lucas_Paganini:
do reactivity. So
Subra_Mishra:
have
Lucas_Paganini:
I
Subra_Mishra:
the
Lucas_Paganini:
can just listen to those changes or pipe my observables and pipe them through operators to make the changes at the time that I want. So if I go with the native HTML solution, if I bypass Angular, I lose this wrap of observables
Subra_Mishra:
observing the
Lucas_Paganini:
on top of
Subra_Mishra:
problem
Lucas_Paganini:
the forms,
Subra_Mishra:
of the
Lucas_Paganini:
which
Subra_Mishra:
virus, which
Lucas_Paganini:
to me is...
Subra_Mishra:
is
Lucas_Paganini:
important
Subra_Mishra:
important
Lucas_Paganini:
when I'm working
Subra_Mishra:
in the
Lucas_Paganini:
in
Subra_Mishra:
population
Lucas_Paganini:
my projects,
Subra_Mishra:
of
Lucas_Paganini:
but
Subra_Mishra:
the virus.
Lucas_Paganini:
it really
Subra_Mishra:
But do
Lucas_Paganini:
depends
Subra_Mishra:
you really depend
Lucas_Paganini:
on what you're
Subra_Mishra:
on what
Lucas_Paganini:
working
Subra_Mishra:
you're working
Lucas_Paganini:
on. If you're
Subra_Mishra:
at? Because
Lucas_Paganini:
just
Subra_Mishra:
you're not
Lucas_Paganini:
working
Subra_Mishra:
going to have
Lucas_Paganini:
on
Subra_Mishra:
a
Lucas_Paganini:
something
Subra_Mishra:
lot of
Lucas_Paganini:
really
Subra_Mishra:
people who are
Lucas_Paganini:
small,
Subra_Mishra:
going to be affected
Lucas_Paganini:
real
Subra_Mishra:
by
Lucas_Paganini:
quick,
Subra_Mishra:
the virus. You're
Lucas_Paganini:
it's
Subra_Mishra:
not
Lucas_Paganini:
not
Subra_Mishra:
going to
Lucas_Paganini:
going
Subra_Mishra:
have
Lucas_Paganini:
to
Subra_Mishra:
a lot of people
Lucas_Paganini:
touch too
Subra_Mishra:
who are
Lucas_Paganini:
many
Subra_Mishra:
going
Lucas_Paganini:
places,
Subra_Mishra:
to be affected by
Lucas_Paganini:
it doesn't
Subra_Mishra:
the virus.
Lucas_Paganini:
have
Subra_Mishra:
And how
Lucas_Paganini:
value
Subra_Mishra:
does that
Lucas_Paganini:
from
Subra_Mishra:
depend on
Lucas_Paganini:
having
Subra_Mishra:
how
Lucas_Paganini:
this
Subra_Mishra:
you
Lucas_Paganini:
reactivity
Subra_Mishra:
respect
Lucas_Paganini:
that I'm mentioning
Subra_Mishra:
the development
Lucas_Paganini:
here, then
Subra_Mishra:
you're doing?
Lucas_Paganini:
for sure. If you're in this scenario, you might even question if you want to use Angular or not, because if it's such a simple solution, you could even go without the framework. I
Charles Max_Wood:
Mm-hmm.
Lucas_Paganini:
think that people jump to frameworks too soon,
Charles Max_Wood:
hahahaha
Lucas_Paganini:
many times.
Subra_Mishra:
Now, like suppose for example of in your application you have a login page which you have username and password and in the sign up page, sign up page has a lot of things. Suppose you need address which might be the fields are related. So in the sign up page you can use reactive form because you are some fields are related and you are doing a lot of validations in that like it should be this much. Suppose for a scenario like your age should be above 13 and your address would be in this range So in that scenario react using reactive form make sense but in the same application for a login form if you are using a username and password and If you don't have a very complex pattern matching in the password, then I think normal form normal form Makes sense here for only for two fields and a submit button means login button. So in the same application also, it's what I meant to say is it's always depends on the how big your form is, how complex your form is and decide upon that like which type of form or component you want to use.
Lucas_Paganini:
and also in how many places you need to use that logic. Because for example, for a password input,
Subra_Mishra:
Yeah.
Lucas_Paganini:
it might be extremely simple, it might not be too many pattern matchings for it,
Subra_Mishra:
Mm-hmm.
Lucas_Paganini:
but what if you want to reuse this password input in many other places of your application? So if you don't isolate this in a component, even though it is simple, it's so hard for you to keep track. of all places and if you make a change to the logic to validate your password, you might forget to change in one place.
Subra_Mishra:
Mm-hmm.
Lucas_Paganini:
So definitely you should always consider too in how many places you need to use the same logic. But yeah, I totally agree with you. These are all things that you need to consider because what we're about to say here, even if you don't do it the way that we're gonna talk about here today, even if you're just... creating custom form components the normal component way using inputs and outputs. Even if you go that route, you have to ask yourself,
Subra_Mishra:
normal way.
Lucas_Paganini:
do I really need a component for this? Is it
Subra_Mishra:
Yes.
Lucas_Paganini:
even going to be reused more than once? There are components that sometimes we create that they are simply not used anywhere else. Like literally nowhere else. This happens a lot when we're creating... regular websites, for example. So you may see like a section of a page and you think I'm gonna make this a component. It's not a problem if you want to make this a component, but is it gonna help? Like this section first, are you gonna reuse it anywhere else? No, okay, so then it becomes optional. You need to ask yourself. If I isolate this section in a component, will it make it easier for me to reason about? Does it have enough logic that it makes sense to break it down in a component? Or should I just make a single component for the entire page and put all the sections in a single component? Because you can also do that. It's not wrong. I think developers have lost the meaning of right and wrong. It used to be... If it works and if it solves the problem, it is right. If it doesn't solve the problem, it is wrong. But nowadays, things got so blurry because it's like, it solves the problem, but it's not using the framework conventions or the library that my coworker told me to use or whatever. If it works, dude, and it complies to the quality standards of the project that you're working on, then it is right.
Subra_Mishra:
Yeah, I think a lot of time people tends to make a single page multiple component might be depends on the code size they have because that also makes sense to change just to simplify the code and not to have a like a thousand or five 500 line of code in a in HTML file rather to divide that two different component and the law divide their logics. Mostly, I think it makes sense if you can make a dummy component. What I meant by dummy components is that with all the components which are not do any logic change, they will be just there for rendering the things and they will pass the data to my service or to a base component which will do all the logic for them and pass them back the That makes sense of reusability point of view and why people should make different components and that plug them to their website to make it faster.
Lucas_Paganini:
OK, should we get into exactly how to integrate your custom form components with the Angular Forms API now?
Subra_Mishra:
Yep.
Charles Max_Wood:
Sounds good.
Lucas_Paganini:
All right, let's do it. So what is the magic? What is this thing that we've been making you curious up until now? So how do you do this? There is an interface exported from Angular Forms. So you can just import this from Angular slash forms. The name of this interface is Control Value Assessor. This interface has four methods. Three of them are required, one of them is optional. The three required methods are write value, register on change, register on touched, and then the optional method is set disabled state. Everything that I'm saying here is based on a content that I created two years ago. So I'm going to put the link to that in the show notes too, if you want to check out the article that I wrote on that and the video that I did for YouTube, that might help. There's also a repository with an example of creating a date picker. Actually, it was a date input, not a date picker. It's an input that generates a date value, but it's not a date picker. It's just a date input. But... is that you have this interface with four methods and you need to make your component implement this interface. So this is step one. The step two is gonna be how to connect form controls to your component. But let's focus on step one. Before you can tell Angular to connect form controls to your component, you have to make sure that your component implements this interface called control value assessor. So again, We have four methods. Now let's talk about what each of those methods is responsible for doing. So first, the method called writeValue. This method should receive a single value. So it receives only one argument. And this is gonna receive the current value. So imagine that you have connected the form control to your component. If you have a form control, you can initialize the form control with a value. You can also call setValue in the form control, which changes the value of the form control. And somehow the form control needs to inform your component that the value has changed. So how is that done? Every time that... Imagine that the form control is already connected to your component. I know that we haven't done that already, we're gonna do that later, but imagine that it's already connected. How can the form control... notify your component that the value has changed. Every time the value changes, the form control is gonna call the right value method in your component with the new value. So imagine you have a form control that contains a string. The string right now is ABC. Let's say that you just instantiated the form control and the initial value is ABC. As soon as the form control is connected to your input, to your component, it's gonna call the right value method in your component, passing the value ABC. If you call set value in your form control and you set the value to 123, it is going to call the right value method in your component, passing the string 123. So every time the value of the form control changes, it notifies your component by calling the method WriteValue. This method returns void. It doesn't care about what you're returning. The only important thing here is that when this method is called, you're always receiving the latest value from the form control. So this is the first method. The second one is RegisterOnChange. So WriteValue is the way that your component is notified that a change happened. But how can we do it the opposite way? How can your component notify that the value has changed? So you do this with registerOnChange. RegisterOnChange receives an argument that is a callback function. You can save this callback function and call it every time that your value changes. So register on change receives a callback function. This function is passed from the form control to your component. This function takes a single argument, which is the current, the new value,
Subra_Mishra:
the new values
Lucas_Paganini:
and it returns
Subra_Mishra:
and it will
Lucas_Paganini:
void.
Subra_Mishra:
turn around
Lucas_Paganini:
It doesn't
Subra_Mishra:
until
Lucas_Paganini:
matter
Subra_Mishra:
you
Lucas_Paganini:
what
Subra_Mishra:
have
Lucas_Paganini:
it returns.
Subra_Mishra:
the value. So
Lucas_Paganini:
So it has
Subra_Mishra:
what we
Lucas_Paganini:
the
Subra_Mishra:
have
Lucas_Paganini:
same
Subra_Mishra:
to
Lucas_Paganini:
signature
Subra_Mishra:
do
Lucas_Paganini:
of
Subra_Mishra:
is
Lucas_Paganini:
write value.
Subra_Mishra:
write the value.
Lucas_Paganini:
What you're
Subra_Mishra:
What
Lucas_Paganini:
going to
Subra_Mishra:
you're
Lucas_Paganini:
do
Subra_Mishra:
gonna
Lucas_Paganini:
is
Subra_Mishra:
do is
Lucas_Paganini:
you're going to
Subra_Mishra:
write
Lucas_Paganini:
write your
Subra_Mishra:
your
Lucas_Paganini:
register
Subra_Mishra:
red
Lucas_Paganini:
on
Subra_Mishra:
structure
Lucas_Paganini:
change method in a way
Subra_Mishra:
in
Lucas_Paganini:
that
Subra_Mishra:
a way that
Lucas_Paganini:
when you
Subra_Mishra:
when
Lucas_Paganini:
receive
Subra_Mishra:
you receive
Lucas_Paganini:
the function
Subra_Mishra:
the function
Lucas_Paganini:
that is passed
Subra_Mishra:
that it
Lucas_Paganini:
to
Subra_Mishra:
passed
Lucas_Paganini:
it,
Subra_Mishra:
through,
Lucas_Paganini:
you
Subra_Mishra:
you
Lucas_Paganini:
save
Subra_Mishra:
save
Lucas_Paganini:
this function
Subra_Mishra:
this function
Lucas_Paganini:
in a property.
Subra_Mishra:
and you pop
Lucas_Paganini:
So
Subra_Mishra:
it
Lucas_Paganini:
you can
Subra_Mishra:
in. So you
Lucas_Paganini:
save
Subra_Mishra:
can
Lucas_Paganini:
this
Subra_Mishra:
save this
Lucas_Paganini:
in a private
Subra_Mishra:
and
Lucas_Paganini:
property
Subra_Mishra:
write it up and
Lucas_Paganini:
called
Subra_Mishra:
you can have
Lucas_Paganini:
onChange,
Subra_Mishra:
a change in the value.
Lucas_Paganini:
for example. So imagine this scenario. The form control calls a method in your component. The method in your component is called register on change. What does the form control pass when it calls this method? It passes a callback function. Okay, you get this callback function when the method is called in your component. You save this callback function in a private property of your component. Let's say that you save this in a property called onChange. Now you have this callback function saved. Every time that you want to emit externally to the form control and to the parent component, whoever is listening, Every time you want to emit to them that the value has changed, you call this onChange property that you just saved with the callback function, you call this callback function with the new current value. So, this is how you emit the value from inside your component to everything that is listening to it externally. I know that this is a very dense subject, it's so hard to explain that in
Charles Max_Wood:
Yeah.
Lucas_Paganini:
audio format. It's very complex. But okay, we're going well. We already talked about the two most important methods. They are WriteValue. WriteValue is how your component is notified when changes happen externally. And we talked about RegisterOnChange, which is how your component notifies... to the external word that the value has changed. Now, the third one is register on touched. It's the same thing as register on change, but the purpose here is you receive a callback function, but this function takes no argument. It's a function that takes no argument, and you call this function to notify that the input was touched. So let's say that somebody focused on the input, typed a letter and went away. Then you call this function to say that input was touched. And what this is going to do is the form control is going to be listening to that. And as soon as you call this function is the form control is going to mark your input as touched or dirty. And the last one is setDisabledState. This one is optional, but it is made to notify your component that the form control changed the status of the disabled state. So the form control has a way to... The form control keeps the state of whether that input is disabled or not. and it needs to notify your component if it should be currently disabled or not, because you might want to change the style of it. Maybe if it's disabled, you want to make it more transparent and make it read-only. If it's enabled, then you want to make it more contrastful and make it available so people can actually type and make changes to it. So you need to somehow be aware of... when the form control changes the disabled state. You do that with this method called setDisabledState. So if... If it was disabled and now it's enabled, the form control is gonna call this method with false. So this method receives a single argument, which is a boolean indicating if it is disabled or not. So if it was disabled and now it became enabled, it's gonna be called with false. If it was enabled and it's now disabled, it's called with true. That was a lot.
Charles Max_Wood:
Hehehehehe
Lucas_Paganini:
Where ya at? Before we move to how to connect the form control to your component, do you guys have any questions on this interface and those four methods?
Subra_Mishra:
I'll just like to for the viewer comfort just about the making a true false you can think like a flag and We'll just turn on and turn off the flag. That's to Make it a little easier
Lucas_Paganini:
Okay, let's get more complicated then. So the last step is to tell Angular that your component is ready to be connected to form controls. You do that with a token that Angular provides called ngValueAccessor, all caps lock. So you import this token from... Angular Forms from the Angular Forms module. So you're going to type import ng-value-accessor from at Angular slash forms. And here we get back to a little bit of complication, too, because now you would technically need to know more about the dependency injection system and how what I'm about to say, how does that even work in practice. I'm just going to give you the answer. and then we can talk about how this works. So in your component decorator, so when you put at component, you have to define a lot of properties. So the properties that we are used to defining are the selector, the template, and the styles. Okay, these are the most common properties. The thing is, your component can also have providers. So a lot of developers don't... know that this is possible, a lot of them think that you can only provide things on ng-modules, but actually you can add providers to components too. So in your component decorator, you can add a property called providers, and this is going to take an array of everything that your company wants to, that your component wants to provide, similar to an ng-module. So just like an ng-module, has providers, and everything that is inside the ngModule has access to those things that were provided. A component can also have providers, and everything that is inside that component will have access to those things that were provided in the component. So you're going to add this property called Providers in your component decorator. This property takes an array, and you're going to give it a single element in this array. is going to be the ngValueAssessor token that we just imported. But you're not just going to provide this token directly. Why? Because this token is already provided by Angular. What you want to say, actually, is that your component can be provided to the ngValueAssessor token. So what you're going to do is, in this array of providers, you're going to create a new object with three properties. The first property is called provide. This property tells Angular what is the token that you're referring to. So, for example, we are very used to treating classes as things that can be injected. But this is actually just a synthetic sugar. When you provide a class, what Angular is doing is creating an object in the dependency injection system and say, provide that class, use this value, multi is false. So, multi is false means that for this particular token, only this value is provided. If multi was true, that would mean that you can, for a single token, provide multiple values. So, what we're going to do here is we're going to use the more explicit... longer syntax. We're not going to use the synthetic sugar that is just providers and putting a single class and letting Angular take care of expanding that into an entire object. We're going to create the entire object ourselves. So the provide property is going to be the ngValueAssessorToken. adheres to the control value assessor implementation. In other words, every single input element that Angular knows how to connect to form controls is also provided in the ng-value assessor token. So if you set multi to false, you're going to override everything that came before. You don't want that.
Charles Max_Wood:
Oh yeah.
Lucas_Paganini:
You only want to append your component to everything else that Angular can also control. So you don't want to delete and override what came before, you just want to add one more thing, which is your component. So we're going to set Muti to true. And the last thing they're going to do is you're going to add a property called useExisting. And in this property, you're going to pass your class. which in the case of a date input component would be date input component. So what this is saying is, I want to provide my component in the ng-value-accessor token, and I want to treat it as a multi-provider. I don't want to overwrite everything that came before. I know that this is an array, and I want to add another element to the array, which is gonna be my. What this tells Angular is that... your component can be connected to the form control. So just to remove the magic completely, how is that internally being done? Because this can still feel like magic. This is done because the form control is a directive. So, and the directive can inject things. So if you look at the source code for the form control directive, in the constructor, injects the ng-value assessor. And what is provided in the ng-value assessor? Your component. So imagine you create your component. It's a date picker. You add the form control directive to your component. When the directive is instantiated, the directive is going to inject your component in it. How will the directive inject your component? Using the ng-value assessor token. This is why you need to provide your component in the ngValueAssessor token. If you don't do this, the farm control directive will not be able to inject your component, and thus, it won't be able to call all the methods in your component. You can implement the controlValueAssessor class as much as you want. If you don't provide this token, then the directive won't be able to access the instance of your component, which means that it won't be able to call the controlValueAssessor methods of your component. Make sense?
Subra_Mishra:
would like to add something here just to simplify for the listener so why we need the token here it's whenever we add a provider in our other component decorator so how angular works it's the first the first level of component so it's the hierarchy of the priority of that so first our all component will check the do we do we have any provider in our provider components provider array and that's by default each instance will be created for that component if you're not giving multi or not passing the token then it will go and check with the module so is there anything provided in the module then the upper level is the which is common like at the root level so if you're not doing anything so uh so suppose uh for example you have already instantiate a service in the root level, which means it is provided in the root level, but still you can use that service and use it as your provided component in a component level, but the data will not be shared across. So what will happen is when the component will be created, a new instance of that service will be created for this component. So what Lucas is seeing here. each if you if you provide a same token and make that as a multi so for this component now it can be that whichever things you have added to the form and in this component you are adding something extra so that will be scalable and so now you from form can work together not as a uh so so not on on on every instance of your component there will not be a extra instance of your service will be created. like did I solve like made it more complex or I just made it simple.
Lucas_Paganini:
Yep. And if somehow you were able to listen up until now, and you're still understanding what we're talking about here, then you're going to notice that there's still one issue happening. So if you implement everything that we said here, you're going to notice that there's still one little bit detail that we removed from the equation, and we're going to talk about it now, which is... When you try to provide the ng-value-assessor token and use your class in it, so you're setting the providers in your component, you're providing the ng-value-assessor token, and you're using your component in this token, you're gonna notice an issue, which is you're trying to provide your component, but your component wasn't even declared yet. So it's not gonna work because the declaration of your component happens below. In the component decorator, your component doesn't exist yet. You haven't defined it yet. So, how can you provide something that wasn't defined yet? What you're going to do here, you're going to use a very nifty trick, which is the forward ref function. So, Angular exports a utility function called forward ref. And what this does is it is used in cases such as this one. Every time that you need to provide something to Angular, but this thing wasn't defined yet, what you need to do is you need to make it lazy. What does it mean to make it lazy? Instead of defining the actual thing, you give it a function that when the function is called, it gives back that thing. And the idea is when this function gets called, the thing that doesn't exist yet, will already exist. So this is fine. So you're making it lazy. So you're gonna call this function from Angular called forward ref, and you're gonna pass to it a function that takes no arguments and returns your component. That way, everything's going to work, because now you're not defining, you're not trying to pass your component before defining it. You're passing a function that returns your component. And when this function, gets called, then your component will already exist. So this is going to work properly. So now you have all the pieces to make your custom form components in Angular and make them work with the existing APIs that Angular provides for forms. This was very complex. It was probably way too much for a podcast episode, In truth, if you take a look at the article that we're putting in the show notes, you will see that this is not very complex to do. It's just very complex to explain in audio format. But if you just take a look at the actual code of what we were talking about, you'll see that this is pretty straightforward to implement. So just recapping everything, there are two things that you need to do to connect your custom form components. with the Angular Forms API. The first is you need to implement an interface called control value assessor. The second is you need to provide your component using the ngValueAssessor token. That's all. It's just that explaining that in audio format is a little bit tricky, but these are the only two things that you need to do. If you take a look at the code that we're putting in the show notes. you'll be able to see an actual example of how to do that. And then you can simply copy and paste that and adapt to your needs in your application. And you'll be able to use form controls, ng-model and form control name to control your custom form component.
Charles Max_Wood:
Cool. Well yeah, I hope people go check it out. We'll definitely put that link in the show notes. Yeah, let's go ahead and do some self-promotion, some picks, and then we'll wrap it up. Subrata, do you have something that you wanna talk about that you've been working on lately?
Subra_Mishra:
Go ahead do some drumming questions and then we'll wrap it up. If you have something you want to talk about, please drop me a link. old ways or suppose someone is not using webpack 5 how they will going to apply micro frontend and the next thing which is coming is with the webpack 5 and model for duration how you going to achieve micro frontend and make make application faster with multiple teams so i'll do that so check check that and as well as i would like to start a video podcast on my channel so whoever is listening like if you are a might be a college student also and if you have want to share something as well as if you are working in in some profession and you want to share share your pain or something then you can contact me I would like I will be very happy to do a video with you So that's all. So first we'll do cell promotion, then we'll do picks, right?
Charles Max_Wood:
Yeah, so we'll do pics in a minute.
Subra_Mishra:
No. No.
Charles Max_Wood:
All right, Lucas, do you have some pics for us?
Lucas_Paganini:
Um,
Charles Max_Wood:
or
Lucas_Paganini:
I do.
Charles Max_Wood:
sorry, some self-promotion stuff, sorry.
Lucas_Paganini:
Oh yeah. I do, it's the same one from the last time. So I'm still very, very focused on my web animations course. Again, still not publicly available. I mean, I don't know how far in the future you are. Maybe it's already published, but right now it's still just for alpha users and we have a waiting list. So if you're interested, in knowing more about web animations and creating a solid mental model of how web animations work. I'm not just gonna craft some animations for you, we're not just gonna do some exercises together. I'm gonna give you the mental model around how the APIs for web animations work and how you can use them to create any animations that you want or that you need. So you don't have to be... limited by the exercises that we're going to do together. I'm going to give you the tool so that you can create any animations that you want to do. So if you're interested in that, you can join the waiting list for this course on lucaspaganin.com slash web animations. If you do that, when the course gets launched, you will have a major discount on the price that everyone else is going to get if they get to the course after it was already launched. So if you're interested in the subject, be sure to join the waiting list on lucaspaganini.com slash web animations.
Charles Max_Wood:
Awesome. I think my self-promo this week is going to go toward the Angular Remote Conference. If you'd like to speak, the call for proposals is open. If you would like to buy a ticket, you can still do that. It's going to be in April. And I'm really looking forward to it. Going to start pretty heavily inviting folks that I know that you all know to come and speak. So yeah. Really looking forward to that. If you go to, I think I own the domain angularremoteconf.com, so just go to angularremoteconf.com. If you can't find it there, then just go to topendevs.com slash conferences and scroll down a little bit. It's right there. And yeah, pretty excited about that. And then the other thing I'm going to promote real quick is I'm putting together videos on how to... set goals for and plan for this next year. And I do it a little different from the way other people do it and it's really focused on achieving your dreams and getting where you wanna go as opposed to my goal is to lose weight. Well, no, we're gonna nail it down a little bit better than that and actually make a plan for how to get there. So, which to me is what goal setting really is. It's not just, I want this, it's I want this and here's what I'm gonna do to get it. So if you want to jump in and get those videos, we're doing those this week for the membership. Membership also includes the book club and a bunch of other stuff. So, yeah, keep an eye out for that stuff. And yeah, we'll see you at Angular Remote Conference and hopefully we'll see you on some of the training calls that we're doing every week. All right, let's do picks. Subrata, you have some picks?
Subra_Mishra:
Yeah, so I was going through the old one like about the new you don't know JS yet. So I think the third object in class is draft is stable now, but it's still in GitHub. If someone wants to go and read, so it's a rewrite on on the first you don't know JS. So it's a solid read like you a lot of. nitty-gritty JavaScript things will be clear here. Definitely you will find something which you don't know. And that's why the book sends also. You don't know JS yet. So that will be my pick for the second edition and the draft for object and classes.
Charles Max_Wood:
Awesome. Lucas, do you have some pics?
Lucas_Paganini:
Okay, so my pick is gonna be this monster that I have here on my desk. So it's this two-liter gigantic thermal water bottle from Under Armour.
Charles Max_Wood:
Hahaha,
Lucas_Paganini:
If you
Charles Max_Wood:
nice.
Lucas_Paganini:
think that this is too much, you're probably right. This is the kind of water bottle that is so huge... that you're gonna be embarrassed to use it in front of a camera because you're gonna just destroy the focus of everyone else in the meeting because they're gonna be like, why are you drinking water from a barrel? It's huge. But it is perfect if you're lazy to get water, just like me. I am the kind of person that if the water by my side is empty, if the water bottle is empty, gonna take so long to go downstairs and get more water that this thing has solved my problems. I leave a cup of water nearby and I have this huge monster that I fill in in the beginning of the day. And my goal for the day is to empty this bottle. So, always have a cup near you because you don't want to drink directly in this huge thing. It's gonna be hard. But if you use it, as a goal of I want to consume two liters of water per day, I want to keep my water cold through the day, then definitely check this out. And it's pretty unexpensive if you're in the United States. If you're anywhere else, I'm not sure. To me, it was a bit too expensive for a water bottle. But do check out Under Armour thermal bottle of two liters.
Charles Max_Wood:
Awesome. So I just,
Subra_Mishra:
So I just,
Charles Max_Wood:
you just made me
Subra_Mishra:
you just
Charles Max_Wood:
think.
Subra_Mishra:
make it.
Charles Max_Wood:
So in the U.S. we don't do liters, we do gallons. This is a half gallon, which is a little bit more than two liters. So I have one on my desk
Subra_Mishra:
I have one on my
Charles Max_Wood:
and
Subra_Mishra:
desk.
Charles Max_Wood:
funny enough,
Subra_Mishra:
So
Charles Max_Wood:
I bought it
Subra_Mishra:
I'm going
Charles Max_Wood:
last week. So I'm gonna
Subra_Mishra:
to
Charles Max_Wood:
explain a little bit about
Subra_Mishra:
explain
Charles Max_Wood:
that in
Subra_Mishra:
a
Charles Max_Wood:
a
Subra_Mishra:
little
Charles Max_Wood:
minute
Subra_Mishra:
bit
Charles Max_Wood:
with my
Subra_Mishra:
about
Charles Max_Wood:
other picks, but
Subra_Mishra:
that
Charles Max_Wood:
first I have to do a board game pick because
Subra_Mishra:
in
Charles Max_Wood:
I have to do a board game pick.
Subra_Mishra:
a minute.
Charles Max_Wood:
So my wife bought me
Subra_Mishra:
So
Charles Max_Wood:
this
Subra_Mishra:
my
Charles Max_Wood:
game
Subra_Mishra:
wife
Charles Max_Wood:
for
Subra_Mishra:
bought
Charles Max_Wood:
Christmas.
Subra_Mishra:
me this game for Christmas.
Charles Max_Wood:
We have not won
Subra_Mishra:
I think
Charles Max_Wood:
it yet.
Subra_Mishra:
I'm not
Charles Max_Wood:
We have
Subra_Mishra:
the
Charles Max_Wood:
played
Subra_Mishra:
one
Charles Max_Wood:
it
Subra_Mishra:
to
Charles Max_Wood:
probably
Subra_Mishra:
play this game for Halloween.
Charles Max_Wood:
six, seven, eight times. So we're enjoying it. It's a lot of fun. Just for whatever reason, we're having trouble figuring out how to win it. In fact, I was actually looking on Board Game Geek because Board Game Geek, I always give you the Board Game Geek wait for the game, which in this case is 2.59, right? So it's relatively. It's complicated enough to be fun without being so complicated that you have to spend an hour figuring out how to play it. Yeah, we picked it up pretty fast. But they also have forums on BoardGameGeek. And I have to admit, I've been tempted to build a competitor to BoardGameGeek because there are some things I wish they did. And some things that I think are a waste that they do. But anyway, that's beside the point. what people are doing. with this particular game to see if there are other strategies for winning it. Yeah, anyway, so effectively what it is, if you've played like Forbidden Desert or Forbidden Island, this is the same kind of thing, except with Forbidden Desert and Forbidden Island, you have the entire island or desert laid out, right? And then the board changes as you play, right? The island sinks or the storm moves or whatever, right? With Forbidden Sky, you start out basically a really small strip of a platform that you've landed on and you Explore and scout the platform and you build out the game board as you go so Anyway, we've we've been enjoying it. It's it's a lot of fun It's a cooperative game like the other forbidden games are and Yeah, so I'm gonna pick it Because it's fun even though I can't win it As far as the other picks go, so I think I've talked about this before, but one of the goals I set a few years ago was to complete an Ironman triathlon by the end of this year, by the end of 2023. And then I got caught up in other stuff and I didn't do it. I ran a marathon into 2019, but I kind of let things go the last few months just because I had so many things going on. And so... What I did is I decided that I'm going to get back into the triathlon game training. And so I got a gym membership, right? So I can go swim in the pool. Um, I got, I bought myself a new bike. I got that off of classified ads. So I'm not going to pick like, Hey, this is the, the awesomest brand of bike. Cause I literally went and got a used bike that's light and works well for me. That's the right size. That was the issue I ran into. If you're going to get into cycling. Don't just go buy a bike off of Amazon. I did that and I just it didn't work. The the bike was not Fitted for me. It wasn't the right size go to a bike shop have them tell you what you need And then you can go get a used bike on classified ads Because I mean it's one thing to have a bike that I can just ride around the neighborhood with my kids It's a different thing when it's a bike that I'm gonna sit on and ride for you know in the case of an Ironman 114 miles So, you know, it has to work a different way. Basically in that it has to be light, it has to be fast, it has to, you know, help kind of put you in the right position to, you know, to get the most power out of your ride. So anyway, so that's kind of what I'm looking at there. But yeah, so I'm doing that. The picks that I have related to triathlon training are, I signed up for Tridot.com and I think I might have talked about this last week, I don't remember. But they provide you with workouts that you do every day. And they also have an awesome Facebook group, so if you have any questions about anything you can ask there. And you get people who have done a zillion triathlons giving you answers. So it's awesome. When I signed up, they had a... I can't remember what... the deal was, but it was basically like a getting started deal where they'd give you two months free of training and then you'd have to pay after that. Then they came back and they sweetened the deal by saying, hey, we'll spread the two months of free training out over six months and we'll add on an extra month so you get six months for the price of three months if you pay half price for the next six months. That's what I did. I signed up for that. And I'm liking it. I'm really liking it. They have videos that explain how all of it works. Every workout has a video that, hey, here's the technique, here's the drill, here's the training, here's the deal. And it's great. So I'm gonna pick that. And then yeah, in addition to that, when I did the marathon, the last day of, or the day of the marathon was the last day of a challenge I did called 75 Hard. In 75 Hard, you drink a gallon of water every day. Hence. half gallon water bottle, right? And so if I need to go through two of these in the day between when I wake up and when I go to bed. And so that's why I picked that up. But the 75 heart challenge is awesome. Now it's not easy, but it's awesome. And so effectively
Subra_Mishra:
So,
Charles Max_Wood:
you have
Subra_Mishra:
effectively
Charles Max_Wood:
a handful
Subra_Mishra:
you
Charles Max_Wood:
of
Subra_Mishra:
have
Charles Max_Wood:
things you have
Subra_Mishra:
to
Charles Max_Wood:
to
Subra_Mishra:
handle
Charles Max_Wood:
do every day.
Subra_Mishra:
the subject
Charles Max_Wood:
You have to read 10
Subra_Mishra:
every
Charles Max_Wood:
pages
Subra_Mishra:
day.
Charles Max_Wood:
every
Subra_Mishra:
And
Charles Max_Wood:
day.
Subra_Mishra:
you have to move
Charles Max_Wood:
You have to drink a gallon
Subra_Mishra:
it
Charles Max_Wood:
of water. You
Subra_Mishra:
all
Charles Max_Wood:
have
Subra_Mishra:
in
Charles Max_Wood:
to stick
Subra_Mishra:
a
Charles Max_Wood:
to
Subra_Mishra:
waterway,
Charles Max_Wood:
your diet.
Subra_Mishra:
just to
Charles Max_Wood:
You have
Subra_Mishra:
keep
Charles Max_Wood:
to work
Subra_Mishra:
it dry
Charles Max_Wood:
out
Subra_Mishra:
and have
Charles Max_Wood:
twice
Subra_Mishra:
it work
Charles Max_Wood:
a day and one
Subra_Mishra:
out
Charles Max_Wood:
of them
Subra_Mishra:
twice
Charles Max_Wood:
has to
Subra_Mishra:
a
Charles Max_Wood:
be
Subra_Mishra:
day
Charles Max_Wood:
outside,
Subra_Mishra:
and one a month
Charles Max_Wood:
which...
Subra_Mishra:
a day, which
Charles Max_Wood:
sucks
Subra_Mishra:
sucks, right? Because
Charles Max_Wood:
here right
Subra_Mishra:
yesterday
Charles Max_Wood:
now
Subra_Mishra:
I
Charles Max_Wood:
because
Subra_Mishra:
went for a run, and it was like 30
Charles Max_Wood:
like
Subra_Mishra:
days,
Charles Max_Wood:
yesterday I went for
Subra_Mishra:
and
Charles Max_Wood:
a run
Subra_Mishra:
it was really, really,
Charles Max_Wood:
and it was
Subra_Mishra:
really, really,
Charles Max_Wood:
like
Subra_Mishra:
really,
Charles Max_Wood:
32
Subra_Mishra:
really, really,
Charles Max_Wood:
degrees,
Subra_Mishra:
really, really,
Charles Max_Wood:
which
Subra_Mishra:
really, really,
Charles Max_Wood:
is
Subra_Mishra:
really,
Charles Max_Wood:
freezing.
Subra_Mishra:
really, really, really, really,
Charles Max_Wood:
Literally
Subra_Mishra:
really,
Charles Max_Wood:
freezing.
Subra_Mishra:
really,
Charles Max_Wood:
It's zero
Subra_Mishra:
really,
Charles Max_Wood:
degrees Celsius. So, um, and I went and ran in it anyway, cause I had to do a workout outside. So that, that's rough. Um, and then you have to take a progress picture, right? And so, um, I went and did that yesterday and I am hurting today because I haven't. I wasn't doing it on a regular basis, so I'm a little bit sore. But it is so worth it to just get back into shape, get moving. And so I'm really looking forward to it. As far as the Ironman races go, they have them all over the world. Just go to ironman.com and you can look them up. And the races that I'm looking at doing, I'm looking at doing the half Ironman in June in Boulder, Colorado, and then the full Ironman in Maryland in September, October. But yeah, so anyway, so yeah, I'm just going to shout out about all that stuff because it's awesome and I'm enjoying it. And yeah, I guess the last pick I have is we've been moving all of the podcast stuff over from sort of the cobbled together custom setup that I had. The website's going to stay the same. It's going to stay on the code that I wrote. But the... hosting is moving to a system called Red Circle. And Red Circle will host your media and stuff like that. But the reason that we're moving over to them is because in exchange for hosting the files, they wanted us to join their ad platform. And so the ad platform will help us find ads, but it also gives us some features that I've wanted for the shows. So. you know, all the old ads were going back and we're working on replacing those with basically bookmarks in the files where the ads go. And so if you went back and got an old episode, you would get a current ad. Right. And so if I'm, if I'm working on another remote conference or I write another book or something like that, then you're going to get an ad for stuff that is current. And so, and that's what I wanted to be able to do with it. So anyway, um, So I'm going to pick Red Circle. And yeah, I think that's it.
Subra_Mishra:
I think that's it. I think that's
Charles Max_Wood:
Let's go
Subra_Mishra:
it.
Charles Max_Wood:
ahead
Subra_Mishra:
Okay,
Charles Max_Wood:
and wrap it up.
Subra_Mishra:
wrap it up.
Charles Max_Wood:
All right, till next time, folks. Max out.
Subra_Mishra:
Bye-bye.
Lucas_Paganini:
Bye bye.