Build reusable components in Knockout.JS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everybody today we're gonna have another installment in my series about knockout js the hope of these videos is that if you are find yourself in a situation where you need to quickly learn not cow js i'm hoping to have your back on that so today what we're going to be talking about is actually one of my preferred ways to work with knockout js and that is their component feature sometimes you'll be working on an app and you may code a piece of ui and you need that ui to appear in many different places throughout the app so rather than you know recoding it every single time we want to bundle it up into a nice reusable component and that way we can just sprinkle it wherever we want and if we change it once it'll change everywhere so today we're going to look at knockout's opinion on how to do that if you've used a more modern component based framework before like react or view this is going to feel right at home to you before we get started here just a quick reminder that i upload both javascript and gamedev tutorial videos to this youtube channel so if you're into that kind of thing please subscribe okay so here we are in codepen and this is the app that we're going to work on today i have really just a basic list of strings here in knockout we have an observable array it's going to iterate through our list of strings items and just print out each item in there this array is starting empty right now but if i just bring this part back in see that when we have items in the list we'll see them in the list over here and specifically we're going to work on this button and what this button is going to do is kind of emulate um fetching data like a ui that needs to when you press it it's going to go out and download items from our api or whatever really common use case in app development um and it's it it's not just a button though it's going to have a little bit of state to it right so we're going to ask this button when we click it we're going to go out make a fetch call to go get us some data that takes some time right so while that request is happening we want this button to kind of dim out to be in a loading state another common pattern happens all the time in app development and so typically what we could do is just add all of that like button state code to our view model container here that's this whole thing right here right alongside with the actual list that we're going to have maybe we could have like you know self.is button loading is true or you know it starts false and then it's true and we dim it and all that and that would work fine but the second we want a different kind of button just like this um in our app somewhere else we would probably have to recode all that logic and all of that button behavior so today what we're going to do is make a knockout component that we can use in many different places in our app without needing to recode it every time so i'm here in the html and see i've already got a button in here but this is what we're going to actually replace with our own custom button component um and to do that we're actually going to make up our own tag name uh and so i don't know i'll call this loading dash button and i'm just completely making this up right now what's going to happen is we're going to tell knockout hey we want to register a new tag type with this name and then that's going to allow us to attach some knockout functionality to it and so to do that we'll come down here to our javascript give us some more room and just right under here anywhere before apply bindings happens you can come in here and on the global knockout object you just say ko.components.register and then you give it the name of the button or sorry the name of the element that we want so in our case it's loading button that's the first argument and the second argument is going to be an object where you configure this so in our configuration here the first thing we're going to give it is a template and this is going to basically replace the html that we would normally write and it's going to be a string so i could come in here and just say button if you remember our button up here that we are kind of replacing uh has you know it's three lines it's got a span in it and so what you can do is just kind of come back in here to the javascript and cram this all in here on one line and that totally works but as this thing grows it can get kind of hairy to look at and so one kind of trick i like to do here if you want kind of multi-line templating first of all if you're uh lucky to be transpiling or you don't have to support ie 11 or any ie um you could just use back ticks here and that will allow you to make multiple lines and then you could come in here and you know have multiple lines and while this looks kind of ugly right now this would work fine again i said at the top of the tutorial series i'm going to try to keep this friendly for people that are still supporting ie11 and aren't transpiling because that's the exact situation my team was in so a trick we like to do if you want to write the template with multiple lines in it what you can do is actually make an array by the way the reason we have to do it this way is because ie11 doesn't support the backtick character as soon as it sees that it's going to crash and so can't use that but what we can do is make an array and then after we're done with the array just throw a dot join on here join um and we want to join it with an empty character which will then take any line that we put in this array say we open the button like this uh do another line to close the button we'll fix the formatting in a second um and then get our span going in here this way we can look at this let's see we'll call this get get items just kind of at a glance when you're coming back to look at this later this kind of looks um nice like the html up here did uh compared to just cramming it all in again it just depends how big this component gets typically for these to be really effective you want to keep them as small as possible anyway but just wanted to show you this is a trick we do for consistency i'll make those the same and then we'll get our class name on here so class oops class equals loading button and that will give us our styling and whoops don't forget your commas now we can see a second buttons appearing in our app up here because remember if we go up to the html and let's in fact let's even do this let's inspect the page and if i inspect this second element see that our html has this new type in it loading button it's right under our act you know our old button and knockout's going to come in and inject the template that we wrote right in here so it looks the same right now but the second button actually came from our javascript right here and so now i'm going to go in and get rid of that first button because we don't need it anymore okay so the next thing we're going to add to our configuration object is going to be our viewmodel function so a view model just like this this is essentially going to be the exact same thing that we do in kind of our you know normal knockout work so if i scroll back up to the top here all the previous videos we created a function that acts you know is our view model and then when we uh start the app we say new instance of a viewmodel and then we kick it off with that that's where we do all of our observables and all that uh you can do all that same stuff right within a knockout component itself and so you have your function defined here viewmodel um and in that it's going to take a thing called params we'll just pause on that for a second we'll get there in a minute but in here i do the same pattern it's like self is this so we always have a clean reference to this top scope here and then let's just say right now our component currently hard codes the text get items that's typically a no no you know if you're doing with multiple languages or you want this text to come from somewhere else like a cms or something and so what we can do is say you know turn that into an observable itself so we'll say self dot button text equals ko dot observable this is all the same stuff we've done before and what we're going to do is for this initial state of the observable we want to pass it in to our um to our component from the top and so to do that you come back to your html here it'll give us some room and we're going to say params equals this and then here we can just start doing like a comma separated list of all kinds of data that we want to pass into this component this is really similar to like props and react or something like that and so we'll say button text and we'll just say um get all items i don't know i just want to show something different than what we're already using and so that will now be available to us in this parameter right here and so i should be able to say params dot button tech can't type today button text and that is going to line up exactly with what we put in the html here and then right now we're not actually using it but in our span tag here in our template we come in and add our old friend databind this is the same you know kind of htmly knockout stuff we did before it's just happening in this component now and so we'll say data bind text is going to be button text and so this is going to refer to the observable within the component and now you see our app has reflected that instead of using that hard coded text even though it's still right here i can get rid of it instead of using that it's going to pipe in the value that we pass in as a param until you see that happening here so this is all working great so far but so our button's not really doing anything right now the idea behind abstracting this button into a reusable component is that we can give it some behavior to do because this button likely all the buttons on the site are probably not all going to do the same thing right we want to configure each button with its own behavior what happens when i click this particular button and so so far we've only been passing text in as params but you can also pass functions and so to do that what we're going to do is actually define a function on the parent and that parent let's see and that function is going to be the thing that we want typically the behavior of the button is probably going to be relevant relevant to the actual context that it's being used in right so we're using this button to fetch a list of items so it might make sense that like that fetch happens right here in our items list and so we'll say you know self.fetch items and that's going to be a function and so we'll just add some logging here so fetch items not we'll have to pop open the console to see it that's okay and then um right now let's let's go ahead and actually pass that into our component so that the idea is that even our button doesn't know anything about this parent function yet but we're going to pass it a reference to that and then when we click the button it's going to fire this that way it kind of decouples like if we use this button in a different area of our site or whatever we can just pass in a different thing that should happen when you click it so let's go to our button tag here and then right after button text i'm going to have a comma and i'm going to say action an action is going to be you know a reference to the function on our parent view model that we want to fire so we'll say fetch items that's what i call it fetch items and so what should happen now is that the button is receiving a param called action which is a reference to a parent you know a function or parent like i've already said and so come down here and we'll use it so in our params with some more room we'll say how about self dot on click and this is going to be a function and what we want to do is params.action whatever was passed in as the action we're just going to fire it like this as a function now to actually wire up this self.on click part we need to come up to our template and give knockout that instruction another data bind that when we click this button we want to fire uh our own on click so this on click is going to refer to the components function that function is going to fire whatever was passed into the component so a lot of stuff going on here but it can be very flexible now just to test this out if i kind of you know open my inspector what should happen is when we click the button we should see this fire which lives on the parent and i click it and i do see that so that's good okay closing out of this um so let's fill out our actual fetch now so our button is firing this but it's not doing anything it's just logging out and so like i said at the top what i want to do here is kind of mock uh an api call that would happen so to do that i'll just fire up a set timeout here set timeout if you don't know we'll basically do this function after this delay so after 400 milliseconds this inner like callback kind of function will happen which is a nice emulation for an ajax request that will take some time to do and that's where you know our button will eventually go into its loading state so after the short delay just to mock the network timing um i kind of like to make variables like this that mock out kind of stuff that looks like what we'd see from an api and so that might look like a list of items with i don't know i found some items nice so so there's our data um but we need to actually use the data and so a good way to do that or a way i like to do is to as an argument in this function here we want to give it a call back because the idea is like we can't put any code right here because this code um of course will execute before this inner code actually does in the timeout and so we want to kind of um give ourselves a hatch so when this asynchronous code is done we're going to whatever comes out of it we're going to give that to the callback and then fire the callback so in here i'll say callback and we'll just do our mock response shortcut here dot items and of course in a real project you wouldn't have to do any of this you'd probably be hitting an api and just being able to use the response from the api in the callback now let's actually implement this callback in our button because right now we're just blindly firing the action and you know when our action completes and does this callback we're not actually giving it a callback so it'll actually crash right there so what we're going to do is give it a callback function here and this function we'll just take data because remember this is a generic button it doesn't know that we're dealing with items we just happen to be using it in that context right now so we wouldn't want to call this like items or anything like that because that might not always be the case and then let's go ahead and set up our loading state and so what we want to do is just create a nice little flag that when this button is working on something it's going to have a is loading true kind of concept that'll allow us to like dim it out and maybe disable it so you can't keep spamming the button to have the action keep going and so to do that let's make an observable so is loading self that is is loading and that's going to be our same observable and we're going to start it as false but right on when we click the button here we're going to immediately set it to true and then whenever the button is done doing whatever it's doing this callback will fire and then we're safe to put this back to false so you know on click we're immediately going to set is loading to true after a certain amount of time this eventually will happen and then we'll you know put it back to false and so to implement some visuals for that right now it's not going to look like anything but i have prepared some class names for us ahead of time so just by putting this let's if i open the css for us for a second just by including this extra.loading class it's going to change the color and so in here we need just a little bit more of data binding magic to like dynamically include that class or not and so to do that in our data bind right after the last thing we have which is on click right now you can add the css as an object and then in here we'll just say in here you put a key value pair and so we'll say loading and loading the left side here the key is going to be the class name that this element gets the right side is going to be the test that's like should i get this class name or not and so that for us is just going to look like is loading and so this is a reference to our observable here when this is true the loading class will be present when it's not true it won't be present and so now when i click it um you see it happens very fast let me uh bump that up a little bit going back to up here if i put it loading for like two entire seconds um when i click the button you'll see that it goes gray and it because it's loading and then it comes back and now it's not loading anymore let's also remember that when this first you know in our parent function when we fire that callback we are passing in the items that we got from the server from the api response and so let's just make sure that's coming through this data right here if i include a console.log should be the array that we get so if i spin it open my console here click the button see that this array of text does end end up coming through and that came from the api and so now our button can actually give that data back to the parent it's kind of like the parents like hey when you click this button i want you to do this thing now the button's saying okay i did this thing here's what i got out of it and so let's add another thing to our button if i close out of the inspector here that's gonna fire when when our operation is complete because this button you know the context is it does something asynchronous and so let's go back to our html and pass in a another param i'm going to call this on done and what this is going to do is fire a different function on our parent view model and i don't have it in here yet so we can say self dot on new items or something like that and this is going to take in the new items that we got and what we'll do here is just pipe this directly into our observable array so we have an array going here we want to include all the new items we're getting into the array for now i'll just blanket set it i suppose you could have it like add in to the array or something like that but you know who cares for now so i'm going to come up here and grab myself.items and just pass the new items in and this is what we want to happen when the button is done doing its work and so that this function is what we're going to give as a param to the button we call that undone and it's going to look just like this undone if i get my string formatting right there we go and now to implement this down here in our javascript when our button is done doing the thing it's doing so it's doing the action and then it's you know setting its loading flag back to false like it's done doing anything let's actually params.undone fire that on done callback with whatever data we got so now when i click the button the button is going to fire the action that was requested and then when it's done doing that action it'll fire the done callback which will then pass the data that it received back to the parent and we should see it hydrate our parent view model so get all items and now we see our strings in the list so the great part about this is now we have this button that's totally abstracted into doing two things basically it's going to do something that takes some time and while it's doing that it's going to have a loading state after it's done with that it's going to do something whatever it got out of that thing that took time and so what that means for us is that we can come in here and basically uh sprinkle this all over our app so even i'll just duplicate it here a few times this is all of course doing the same thing right now you see we get four different buttons but if you imagine this in context of different places in your app you can change out the behavior of what the button does on click so give it a different asynchronous action to do and then when it's done with that action what should it do with whatever came out of that that way you never have to re-implement like the loading part again if you're working with designers and you want you know consistency across your app you only have to style it the one time and then finally if you update it once it'll update in all the instances across your entire app so i found it to be definitely a nice way of working okay so that's all for this video about components and knockout js really appreciate you watching to the end thanks so much if you learned something or you enjoyed this please i really appreciate it when you hit the like button and again feel free to subscribe for more videos just like this we also have a discord of people making indie games and javascript apps and all kinds of stuff so if you're interested in hanging out with us there the link is in the description below and finally uh if you have any requests or anything like that about more knockout related videos please let me know in the comments that's how i know what kind of videos to make so thanks a ton and i'll catch you next time later you
Info
Channel: Drew Conley
Views: 3,776
Rating: undefined out of 5
Keywords: knockout.js, javascript, tutorial, beginner, javascript tutorial, knockout.js tutorial
Id: oqthYvHXGo8
Channel Id: undefined
Length: 21min 4sec (1264 seconds)
Published: Sat Sep 26 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.