 It is 2018 and it's the first supercharged of the new year. Hi, I'm Surma. And I'm David East. Oh, just made it. All right, we're ready to go. Let's go. Did you hear that new intro? It's pretty good. You didn't hear it because we don't hear it. I didn't hear it. Good acting, though. I almost blew it to you. Welcome, everyone. Supercharged, live stream, live coding. It's back. And we have a guest from the Americas. I flew him in so he can talk to us a little bit about what is it that you do? As you put it, I work on the firebases. Firebases. And so you have the honor of basically breaking a long tradition of doing just vanilla, only local. Everything is on here. But for the first time, we're going to use firebases. And I'm OK with that. It's not my first time using a firebase. No, no, but on the scope of this show, that's pretty cool. We teased about it a little bit in our little videos that we put out. And apparently, lots of people saw it because we have a lot of viewers right now. Hi to everyone. Very glad to have you. That was a humble brag. Maybe we should. What is it we are building? So we talked about a real-time comment section. But surely there's more to it than just that. So what is it that you have planned? So we're going to do some custom elements. Not like the full web components model, because we're not going to use Shadow DOM. We're just going to use custom elements to have a nice organization to our code. And we're going to plug in Firebase authentication to sign in with Google. And then once you are signed in, we can then start adding comments. We'll get your profile picture and your name and then whatever comment you put in. And it'll be persisted with Cloud Firestore. So we'll also have some really nice offline, flaky network resiliency. Nailed it. It's going to be pretty good. All right, I'm looking forward to that. To everyone who is here for the first time, this is what the show is about. We're going to live code. In this case, you are going to live code. I'm going to monitor the chat that we have on the live stream. And you can ask questions. I'm going to try to either answer them myself or forward them to you and try to confuse you, make you stumble. So if you have any questions, didn't understand something, feel free to ask. I'll try to weave them in. But without further ado, I would say, let's get coding. All right, let's do this. So what are we starting with? I hope nothing. Well, I did the liberty of downloading the new modules because like, you know. We didn't want to waste time with NPN install, right? That's a good call, I would say. Yes. But we can peruse the package.json to see what's going on here. And so effectively, well, we'll ignore the scripts for now. So we'll get into that later. But we're going to use Express for our little server framework. We used Express before here. So hopefully, most people are familiar with that one. That's good. Use Firebase. I see. Then Firebase Functions to do that serverless sort of. We're using Firebase Functions. Cloud Functions for Firebase. Hashtag branding. When you say the four, that's when the hand comes out. Oh, for Firebase. OK. That's how you know. I'll learn the ways. And then as dev dependencies, we have Babel in the transform ES2015 modules. So you're in the Babel camp, not in the Babel camp. Oh, I didn't know that was a thing. It's a thing. I think Babel is technically correct. You should ask everyone if it's Babel or Babel. So if you want to use Babel, you will have to do the umlaut. Babel or Babel? Yes. But this is fairly simple just to transform any ES. So we're actually going to use ES2015 modules in the browser, but not on node. We're going to use CommonJS. So it's just going to be a really quick transform. So that's the only thing you're using. You're just using it to write modules and for the server side, we're transpiling them to CommonJS. It's not even going to down level any conslet class. Oh, cool. OK. And then CPX, just because it's an old trusted tool of mine to copy and paste things. So what does it do? Because not everybody knows what it knows. I already have a question. What is CPX? What is CPX? It is basically being able to copy with like, you know, splats and specific like .js, .html. So it's like a recursive copy, but you can filter by extension or by patterns in the name. You can do recursive, you could not recursive, you could do patterns. It's just a fancy. It's a more powerful copy. Yeah, more DevX copy, Dev Experience. OK. And then Firebase Tools, which is the CLI for Firebase for doing deployments, local emulation, all that fun stuff. So I'm going to start by creating my source folder. I'm like a source folder kind of person where it's like, write your stuff and then be able to just like totally clear out any of the built directories. So I'm going to start with, I'm going to have a client. So this is going to be all static assets, anything we're going to serve over Firebase hosting CDN. And Wax, you're going to serve everything over Firebase hosting CDN. But it's not going to get generated. I see. OK. Well, we'll get talking about the CDN stuff in a bit. We're going to have a server. So this is going to be all of our generate our HTML for our server rendering. And then the last bit, we're going to have our components. And this is where all of our custom elements are. And they're not in the client or server because they're going to be built into both. Because we're going to need to render it on the server. And we're also going to have them run on the client. So we'll see. OK. That sounds interesting having to run them both. So we're going to get started real quick the nice little index JS. And so we're going to require from Firebase functions. People have now started arguing about GIF versus GIF. Good. Which camp are you in? Oh, I am the GIF. Get out. Wait, no, sorry, GIF. GIF. Good. I couldn't remember which one I will. I was trying to say it. And then I said it. I'm the G-H-I-F GIF. Yes, I agree. I think GIF doesn't mesh with me. But it's fine, it's fine. So we're importing Firebase functions because we're going to use Firebase functions? Yes. And we're also using Express. And we're going to need to eventually require Firebase. And I'm going to actually require Firebase as Firebase slash app because that only is going to pull back one bit of it to know. So it's not as big of a deal, but it's still nice to shave off bytes where you can, wherever you can. And then going to do requireFirebase slash Firestore, which this is required to do because Firestore is not loaded by default into a, like if I just said, requireFirebase, this would bring in a bunch of Firebase stuff, but not Firestore. So you need to do this sort of like. OK, that's good to know. Little side effect require. That's fine. All right. So now what I can do is I can create a router. Is it a router or a router? We can just keep this going all day. Router or router people, leave a comment. In America, we say router. But a friend of mine who is in London, he made a very strong argument for why it was router. And basically, he read the definition of router and it was what we called router and read the definition of router and it was not what we call router. There you go. It's router, settled. It is router, but I'm going to say router. All right. So I have my router, let's say router.getMyHomeRoute. So we're going to build a comment section, right? Yes. So you're going to have one endpoint that serves, I guess, the HTML? It's only going to have one endpoint for the HTML, that's it, because everything else, we're going to be able to do on the client side with Firestore. Gotcha. OK, so I think actually before, we're going to do server-side rendering at some point. So that's probably going to go into here. Yeah, look, see, look, we're going to do a nice little busting out the marquee. Marquee still works. Blink sadly doesn't. I know. It makes me sad. I don't know if that is sad. Well, the custom element. Bring it back. Yeah, we could bring it back. So we want to set up the Firebase, Cloud Functions for Firebase. So to do that, you export out a function. So we're going to do exports.supercharged. You actually can call this whatever you want. Just calling mine that. And say functions.https, because there is plenty of things you can trigger off of. Look at auto-completion. Nice. Yeah, that's good. And then onRequest, and onRequest is actually just like this kind of callback right here. So it's a request response, like express style handler. Oh, cool. And what we can do is actually pass back the full router. So the cool thing here is if you have any existing Express apps and you want to throw them up on the Cloud Functions, just one line of code. So what we're expecting to see now is we're going to start the local Firebase Functions. But not. See, we're still on source. So we need to build. Our build things. Yes, so we're going to build. So actually, I'm going to set up Firebase. So to do that, not Yarnlock, that's a scary file. I'm going to make what is it, Firebase.json. And so I could do this through the Firebase CLI, but this is just going to be a lot faster. You're elite enough to actually do it by hand. I don't think anyone's referred to me that way. All right, so I'm going to create a hosting configuration. And I'm going to say my public folder. So what I want to serve my assets from is called public. You're so creative. It is. It's very creative. And so now, but also one of the things I need to do is since I have these, I need them to serve from functions. So effectively, you have this public folder, and you have a functions folder. So public is like straight on the CDN, like serve from orging does not go out to Cloud Functions. Cloud Functions, if you have something with Cloud Functions, it actually, by itself, has nothing to do with Firebase Hosting. But we can write some config to link them up. And so we can do that through a rewrite. So we can say, whenever Firebase Hosting sees this URL, we're going to call out to this Cloud Function and store its response. All right, so I just got a question. If this is going to be available on GitHub later on. Yes, of course. Like every our live streams, we're going to put the code on GitHub later on so you can play out with it, run it locally, see if you get the same results. Hopefully you will. And I'll put the link in the description once it's there on the video on demand because you can re-watch this video multiple times they are on. All right. Or send it to your friends, maybe. Who knows? I should probably explain what I'm doing. So I'm doing a rewrite where basically you're saying, whenever you see anything in this case, and in this case, it's OK to do that because only have one file we're getting out. So I could just say, like, slash, but for superstition, because that's how I've practiced it, I'm going to do the star-star. Just rewrite everything. Yeah, I'm cool with that. We're just doing a whole new topic. So star-star just means match everything even if it's in a sub-folder. Because I think one star would not match a sub-folder, but whatever. Anything. I'll take anything. The cool thing, too, is that if you have a static file, that always takes precedence over the Cloud Function. So you don't have to worry about skipping over static files. I see. So if I have an index.html, it will not serve this route of mine. It'll prefer the index.html. That is cool. That's really good. All right, so you can see we have everything's called supercharged. So our rewrites are good. We just actually need to set up Firebase, the Firebase Functions SDK. And so to do that, I'm going to tap into my node modules because I've installed locally. I'm going to say init functions. Oh, before I do that, I almost messed up. I want to create a Firebase RC file, which, by the way, isn't it so cool? You just make a file and it's got the icon. The little logo? Hashtag branding. Yeah. And it just appeared one day. We didn't do it. Someone just like, hey, put it in there. Right, you didn't do it. And so Firebase RC is your configuration for your project. So you can use aliases for projects to say, deploy to my staging, production. That's cool. I just set this up from scratch because I know the name of the project. And so ours is going to be a default. And the default is going to be called supercharged comments. Nice. All right. Also hashtag branding. Everything's on brand today. So now I'm going to initialize the functions SDK. This is my favorite part right here, where it's like Firebase. This nice little emoji art. You see it? Yeah, it's good. That's legit. All right, so it says, OK, I see you have Firebase RC. You're good. You can use JavaScript or TypeScript because there's actually out-of-the-box TypeScript support, if you like that. I do love TypeScript, but I'm sticking to JavaScript here. Today we're going to do the untyped route. Route. Ask them in the comments. And so you can even set up ESLinting, which I'm going to say no and probably regret. And right here is it's written our functions package.json and our functions index.js. And so this functions package.json is just has Firebase Admin, which allows you to do all this admin administrative type operations and then functions as well so we can do the serving. And this right here, your functions have to have their own node modules. So I'm going to CD into the functions and I'm going to just kind of like yarn install it. It's going to take probably like 20 seconds or so. So while that's happening, we can kind of go through and look at our scripts. Oh, the secret scripts. The secret scripts. Because we are skipping over anything like gulp or grunt all these buttons. We're going to write some simple node commands. I like doing that when it's like up to a certain level. Yes, it doesn't scale. But for like small projects like this one, it's totally. I had to and and one more time in here. I probably, that would be my limit. But what I like is, is I can say, OK, I have this big build command where I can say build server, which all that says is copy everything in the server folder to functions, client, everything in the client folder to public. Components says copy everything from the source components to public components, babel components, run our babel configuration to function slash components. And this is what we'll. Because those need to be in common JS format for the server side. Exactly. So for the front ends, we just stay on the module. We don't change our code at all really. We legit one-to-one take it from source to our public folder. But for the server side, we need the common JS version. We just need some module transform, that's all. And then we have this nice little serve. Well, this was what we're going to be using. So Firebase serve dash dash only functions hosting will emulate a hosting and functions environment. And it'll even read the rewrite for us. And so we just run it all in one port. So now we have that. Shall we go ahead and try? Will it actually work? Did you write all these configuration files by hand? So in a normal case, you could just use Firebase. You need to end it, right? And it would just do it for you. Yes. But you did this, or you did this, I guess, to explain what is actually happening under the hood and what it means, which is good. So you learned something, hopefully. So we're going to run this in awesome. There are all the commands that we took as a build script are now to have it run. Here is our nice little send. So now I'm going to say a yarn serve. It seems to be working. We'll see. Now's the moment where we switch to the browser, isn't it? Yeah, it is. Well, so it's going to prepare to emulate functions, and then it's going to serve on localhost 5,000. So this is all static. Now the functions, as you can see, it's on 5,001. But because we have the rewrite, it'll know to put them together. So now we have a localhost 5,000, and look at that. And the marquee works. I saw a question. And we're done. Ship it. That's our comment section, ladies and gentlemen. Thank you for watching. I saw someone asking, what is marquee? It's been a running gag in the 90s, I think. That was on the web. It does this. It makes some content circle around from left to the right. And it's been removed. Marquee is a very complex tag. You can even use properties, control, scroll speed, left to right. Back and forth. Don't use it. I built an entire website when I was like 15, where everything marquee'd in. You wanted to marquee everything because, yeah, great times with animated GIFs. So now that we have our serving running with our server-side rendering, what I want to do is I want to just build kind of a static version. And so in server, I'm going to do index.html. And I'm going to actually read from this file and serve it. And so I'll end up doing some replacements and stuff. So I'm going to do a little HTML snippet. Look how fast it can type. That's pretty good. I don't need an HTTP-equipped meta tags. I'm going to drop that. And then in title, call it super-charged comments. Nice. Yeah, always use your title. And I'm going to drop a style tag in, because we are going to be inlining some styles. I agree. I approve. Well, we only have one page, so. Yeah, that's fine. That helps with the first one. We don't need to fire off an additional request just to get the styles if it's a single-page thing. So since I'm not using Shadow DOM, I'm using the cheap version of Shadow DOM, which is called append a prefix before your classes. So this is basically Shadow DOM before Shadow DOM. You're scoping your styles by prepending SC. Yeah, super-charged. Yeah, super-charged. Yeah. So we're going to do a container, because we're going to put everything in the middle. So what we're going to build here is you're building the UI for a comment. So it's probably going to consist of a form where you can, if you're logged in, write a comment and submit it, and it will appear, and probably the list of the existing comments as you would expect from a comment section. So just so you know what David is working towards while writing this, because right now it's just Divsoup. But we're going to build it up. We're going to make sense from this Divsoup. But I like making the Divsoup first, and then refactoring it into custom elements. So what I'm doing right here is we have our, I have another container inside of that, so we're going to have sort of like an outer container, inner container, and then we're going to have this SC login, which will become a custom element. So I like to ID things. I feel like it's a little old school to do that, but sometimes I do that. It gives me a nice little confidence when I go to get something. I know where my elements are. Yeah. There's no shame in that. Some people prefer the get element by ID over the query selector, and that's, you know, perfectly fine, doesn't make a difference. I just kind of stopped using get element by ID, but I don't know, maybe today, we'll see. I've only used query selector. Shout out to get element by ID. The original, I don't know, the original dot. Starting with an H3, because why not? Yeah. Who needs H1 and H2? I don't want it to be that big. H3 looks like nice in the middle. Yeah, H3, it's unoffensive. Neutral. And so we're going to create a comment form, because we've got to be able to set a comment. So I'm going to call this a C comment form. And sort of the mindset I'm using here is that all this class right here will actually be the element name when I refactor it. And so what is this? So we're going to have a text area. Oh, I do not need that much. That's a little, it's also a little extreme. All right, so we have a text area, and then we're also going to have a button. So it's going to have a class of a C region. So for everybody who maybe joined a little late, welcome. This is super charged live coding. It's back. We're doing it. This is David Eat, East. David Eat, sorry. David Eat from the Firebase. And we are building a real time comment section where you can leave comments as a user and they appear in real time and also appear in real time for other users. And it uses web components and does server rendering and its resistance against flaky networks. Resilient. It's not resistant to it. We have like eight takes a day. We're trying to say that word. Word, it's not good. But so we have a lot of buzzwords to cover today. And I'm really excited about that. And we're now just building the form where users can hopefully later on leave the comment. I mean, we're not going to host this publicly. So you're not like we can all go onto this and start commenting, but the technology will work. So you can later on grab it from the repository, run it locally and play around with, you know, how airplane mode interacts with this thing and all these little things. We'll get there. For now, we're building the, the now that we have done the form, I think. Yes. The text area and the button. That's all you need for a comment, I think. This is all just to get it on the page and filed. And then I'm going to start moving the pieces into custom elements. So this will obviously be the part where all the comments from the other people are listed where you can apply to or laugh about how weird some comments are that sometimes get left on these comment sections on websites. All right. So thing this is all of our div stuff. And so what I want to do at this point is write some, actually I want to serve this, you know, beautiful looking page. So I'm going to open up a new little bash thread. I don't need that there. And I'm going to require the file system and what I want to do is I want to pull in this index.html file. Yeah. So now that's the thing where you said where we somehow link up the static content with our functions code. Yes. And I am reading at sync, which, you know, luckily you're only doing it once and not per request. Yes. So I'm going to, I'm going to let you have it. That is where, that is actually how I like rationalize this. I was like, well, it happens on startup. So how bad could it be? Somewhere, somewhere, there's some node expert who's just like, oh, no, what is he doing? All right. So now we have, oh, yes, I need to come back as UTF 8 or 7. It's not the default. No, it comes back as a buffer. Oh, right, you want a string. Yeah. I want a string. That is a good call. Yeah. And so now I can just send this back and we're eventually going to be doing some manipulation to this for the server side rendering, but I want to make sure it just serves up. So now I'm going to yarn build the server. People say you misspelled sc-autorname. That is good. I, you said, yeah, you used the German spelling. Yeah. Well, there's no styles yet, so. Doesn't matter. But thanks for catching that. I'm glad that a lot of people are paying attention and catching these typos. All right, we got error. So what I like to do is go here and says unknown encoding. So I probably spelled the encoding wrong as well. Probably had a dash, doesn't it? Oh, I didn't see that. That's very hard to spot. Yeah. I feel like now I really want to start as an encoding called Uft8 and just confuse people. It does everything backwards. It's like in a good old days when you would have. Did that copy over? I don't like reading it in the browser, because it's not formatted. Unknown type encoding, Uft8. I spelled Uft8 again. Oh, I know what I did. Oh, you're editing. I know. I usually have a git repository initialized and these are all grayed out, because they're getting hard. So he was editing the thing, the target of the copy operation, not the source. I'm glad I did that early and not later, or not after I wrote a ton of code, because it will just destroy everything. All right, now we refresh and it's beautiful. That chip it. We are done. That's the comment section. All right, so now I'm going to write the styles. This is the part that I've been like, mostly like, oh man. I love this bit, because I started to do it on practically any project. The first thing I do is a star sector, which matches any element, but at the low specificities of any other styles will override it and setting box sizing to border box. Because that means that the width that you define on the element will include the padding. So you can change the padding, basically indent the text or move it instead without making the element bigger or smaller. By default, changing the padding will make the element bigger and that can be really confusing. So I do recommend setting box sizing to border box. It's a really neat trick. Yeah, I learned that one from Mr. Paul Irish back in 2000 and whatever is, when he published the blog post, it was like one of those mind blowing moments. All right, I'm going to drop on this. There we go. So we're going to do the SC container. And so we're going to give it like a max width of like 840 pixels. That sounds right. That's a good number. Why not? And we have to make sure to margin zero auto. That's basically centering it. Bring it in. Because like the comments are going to have that much text. So I really want them like pushed into the wall. Yeah, you don't want text lines to go all the way from the left of the screen all the way to the right side. And oh yes, I want to add padding on the side. So when it is less than 840 pixels, you get 20 pixels on the side and it doesn't just like stick to the side of the screen. Paddings, they're a good aesthetic tool to use. They can be more airy. And then in the SC comments section, I'm going to constrain it even a little more. Say, all right, we'll only drop it in, so the inner container. We also want to margin 20 pixels, 20 pixels auto it so it has some top space. Some spacing between comments. One question we got, can you explain again why we used UTF-8? Well, Node returns everything as a buffer by default, which- It's not a buffer, it's not a string. So it's really hard to work with that. And since we want to do some manipulation because server-side rendering is going to come up next, we want to work on strings. And so we have to tell UTF-8, we have to tell Node, you'll get a buffer, but decode it for me using this text encoding, which in our case is UTF-8. So this is how Node knows how to turn this buffer into a string and gives us a string, which is much more convenient to handle or think about. See, it's already- All right, we're working, we're cooking. All right, so now bring it back down. And I want to, I will do the button now. So S-E-B-T-N. So I'm going to give that a border of a pixel. Interesting question, somebody asked, why is this star border sizing thing not a default style? Well, the thing is we can't just change that. We would break the designs of a lot of websites if we suddenly change the default styles of a browser. That's one of the veins of the web that once a thing has shipped, you're pretty much guaranteed to always have it there and no way to actually change it. Unless you think about a good way how old science remain functional and new sites can get the new behavior. And to be fair, it's like what, 18, 20 characters that you add to your style sheet. It's not that big a deal and definitely worth breaking a lot of existing sites over. And the second question regarding this was, does this work with ShadowDom? I mean, it does work with ShadowDom, but the style up there won't affect the elements inside of ShadowDom. That's the exact purpose of ShadowDom to give you proper isolation. So there's no way for outside styles to style something that's inside of ShadowDom and vice versa. Styles from within the ShadowDom won't leak outwards. But to not twist our brains in a knot, we're not using ShadowDom today. Just to keep things simple, at some point you probably should, but for now, that's a nice button. It's small, but I'm going with it. Yeah, it's good. I mean, you can zoom in. Yeah, exactly. Right? Command, control plus, whatever you have. Yeah. All right, so we have our button. Now I want to do the form. So the form is actually going to be kind of overloaded because I'm going to use it for the form in the comment, sort of like this container. Yeah. So I see comment form and I see comment. And then also what I've been doing too is I know I'm going to turn these into custom elements, so I'm just already specifying it. I see what you did there. So I mean, you could probably class them since I'm not using ShadowDom. Yeah, but. But this is effectively, if I was using ShadowDom, I could turn this into a host. Yeah, that would be like the default style for that element. But let's keep it simple for today. We're just going to use one style block at the sort of document. This is legit more about how can you make server-side render happening with Firebase? How do we consume our own index HTML and render into it? All these little questions you usually end up having when you build server-side rendering for the first time. That's what this episode is supposed to be about. And if you have questions about how and when to use ShadowDom, there's actually some good guidance on developers.google.com slash web, where we have loads of articles. So if you're interested or have questions about that, go there, read up. And if you still have questions, I mean, you can send us questions on Twitter or leave them in the comments later. Your choice, really? All right, border one pixel solid. All right, at least frame around it. Nine, and I want to use seven. All right, and I'm fairly certain I'm missing some styles, but I can always learn that by seeing how it looks. So at some point, you would probably also define a watch task for this. It would just auto-build whenever you change a file. But we're not doing that today, either. Keep it simple. That's how we roll. Margin, bottom. I don't want to implement a live reload server. That's what I like if I'm using webpack dev server or something like. Something that does it for me, I will take it. But if someone's like, hey, you want to build that, I'm like, no, I can run my command. All right, so now we have. Could we use WebAssembly? Not today. WebAssembly is super interesting. I just started working with it. But it's usually more relevant when you do really hard number crunching and have to have a really tight budget in terms of execution time. So for gaming applications and stuff. So for this, WebAssembly would probably be overkill. Yes, you could. So we're not going to touch on that today. Let's see here. So now I'm doing the text area. And I want that to like kind of full out, blend in. So it kind of feels like it's part of the cupid form. So I'm going to pack around color, FDE it. So it's the same as the comment. I'm going to give it a height of 150 pixels. So it's kind of constrained. So I don't have to implement autogrow because we don't have another hour. Yeah, that's quite resize none. Yeah, resize none. So there's no little drag. I did not know that's a thing. Oh, yeah. Today I learned resize none. And we're done. Cool. All right. And I want to give it an outline of none and a border. Just make sure nothing creeps in there. And that should. Look at that. Oh, there is one. I like the aesthetic you already have. Oh, yeah. The original one was good. But I like to give a good, large font size to these. I've updated it, but it just feels better. So now we only have one more thing to style. And that's the SC Author. So SC Author, right now they're kind of like on top of each other. Yeah. Actually, well, you can't see the, there's going to be like a little circle. And I want the circle and then the name. So I'm going to use Flexbox for that. Oh, touching into the modern layouting algorithm. Yes. Flex and Grid. If you don't know about Flex and Grid, it makes your life on the web a lot easier. And especially Flex, no, Flex and Grid actually have been shipped to all major browsers at the same time. So we actually can use them in all major browsers right now, which is really cool. It is very cool. And so now, not a circle, but a circle avatar. And so this is going to be the little circle profile picture. And it's going to constrain the height to 32 pixels, as well as the width. And then we're going to do a background color of, OK, I missed that one. So we're even going to do little avatars. Yeah. That's nice. With Google sign-in, you get the little avatar picture. Oh. Given the comments, a bit more identity. That's pretty neat. And I like, one of my favorite CSS tricks for making circles is that you can set the height and the width. And then you can set just like the most egregious border radius. And it will just turn into a circle. You don't actually have to know the real number. It could be like 50 pixels, maybe 500 pixels. It just turns into a circle. It's nice. Somebody just said resize is not supported by Edge and iOS Safari. That's probably good to know. That is good to know. We haven't called it out, and we probably should. This is not production ready code. As always, it's about the concept. So yeah, this is probably not something you should just take from the repository and ship it to production and call it a product. Because we're not going to even do testing. That's not going to be no tests. And you should have tests for these kind of things. So yeah, watch it, call us out on these things. But we're going to take some shortcuts for the sake of brevity and make it easier for you to follow what we're doing or what you are doing. Why do you think even we are trying to take credit for your work? Is that nice? Just like always. And then we're making a little utility function that I'm going to use later before I forget. All right. So it's built. And I do a refresh. And look at that. I can read that. Somebody just asked, is this really live? Yes. This is really live. Yes. This is live. Good job undermining my authority. Last time we had. This is not prerecorded. I feel like I should do something that really proves it's live. It is. It is. We have people here that do the cameras and stuff. All right. All right. All right, where we at? So we have our CSS done. Oh, nice. I see you present yourself as a black circle. Yeah. Well, it's going to be the placeholder for when. So if someone doesn't have an image, you usually get that broken image thing, and then the alt shows up. Somebody just asked, and I find it interesting, is there any resources for bundling or minifying Firebase server-side code? You usually don't need to bundle or minify server-side code. Well, you can webpack your server code up, and what's kind of nice is that then, if you're doing any type of deployment on the server, you don't have to do run through an NPM install on the server on a Docker boot. Oh, that makes sense. It's just that big bundle. So webpack works really well for that. That's a decreased startup time as well. Yes. And you also can hot module swap your server code without a read-a-boy. But definitely bundling is not as important on the back end as it is on the front end, because you don't have to worry about round time trips or slow network connectivity. It's all on one machine. So when you get to bundling and chunking and all these things on the front end first, it's more important. They're much more important. All right. So styles are done. I'm going to go through. I'm going to create this SC login. So that's going to be in the components folder. So it's called sclogin.js. And what I'm going to do here is start using ES 2015 modules and classes and stuff. Create a class called login button, extend HTML element. And then what I'm going to do here is actually a little trick that Mr. Serma taught me, is I like the static template. Oh, yeah. I do that a lot. Yeah. It's really nice, because now I can return, you know, do like a little literal inside here. And it's just a nice way to associate a template with an element. It's just a static property in there so you can. And I just, now I'm just going through. And I'm copying in the inner HTML of all these divs that I wrote. And what's kind of nice is, so now I have this, but I would prefer to have syntax highlighting. So if I write HTML because of the lit HTML VS code extension. Oh, that knows that this is now HTML and does like syntax highlighting. Yes. That's neat. But I'm not using lit HTML. I mean, you could here, but we don't. We could. And if I was continuing to refactor this, I would probably move. Did I say this is a production ready code? I would move to lit HTML. So here's a little trick. So I'm going to say const HTML is string.raw, which is the, however, it actually tells you it's intended for use as a tag function of a tag template string. And it does the default thing that you also get. You have no tag. So this basically makes HTML behave if it wasn't there, which is exactly what we want in this case. Which is cool. Before I do connected callback, I'm going to create my. Save foobar if this is live. Foobar. There you go. We now have established that this is live. That was not asked from someone to say beforehand. We did not. We did not do that. Let's get back to your HTML element. So we're doing, so we're turning the thing that you wrote as a little diff thing. We're turning it now into a custom element just to encapsulate all the logic that comes with logging in into one element that we can just reuse potentially in other projects later on. Yes. So it's legit just a button, basically. All right. While we are server-side rendering, this right here is not going to be server-side rendered because it's the login button. It doesn't bother me that if that's not in the document for server rendering. I want the comment content to be in the document. Like this can be added on. You have to have JavaScript to log in. So it doesn't really matter that the login button isn't server rendered. True. So templates, I hope most people know this, the template tag is a way to pre-power to HTML that you can then instantiate a lot quicker than using .InnerHTML because that will go to the parser and then nodes will get created. Well, in this case, it's already in a tree form that consists of nodes and it just does a deep copy. You get a copy and you can just use that, which makes instantiation of the same template multiple times much faster than it would be with InnerHTML. Correct. So I created this element. We're going to come back and actually make it do something. But at first, I'm going to create this index.js, which is the entry point of my client-side codes. So what I need to do is I need to import from components slash sclogin.js, which is very important. Yeah, so that you're now importing your own elements into your main JavaScript file. And then I'm going to use the custom elements API to define sclogin. That's the standard way of how you define a class as a custom element. Yes. So now it will be available as sc-login between angle brackets. We just made an HTML element. So what I can do here is I can actually delete this and just say sclogin slash sclogin. All right. And so at this point, I just need to add. Yeah, David, why not use Vim? Well, I've gotten to Vim once. And then I couldn't get out of it. So I just took my computer and just threw it away, bought a new computer. I have to admit, I have Vim mode enabled in my VS code. The best of both worlds. I use Vim all through school. And then when I discovered what IDEs were, I just never went back. Yeah, I can see that. You thought you did C-sharp in your previous life. If you've ever done C-sharp, there are some amazing IDEs. And you probably never want to go back. Yeah, yeah. This is quite true. All right, so I'm going to say script type is module, which is also very important. Because that way your import and your x-force. You're down with all the new stuff. Love it. Well, it's because when you fail pretty hard by not putting type equals module, you remember. Then import becomes a syntax error, and you're like, what? Yep. That's actually, I think that's one of the more confusing error messages that import, the import word just becomes a syntax error. Yeah. And you're like, whoa, wait. I thought my browser has import support. What's happening? Well, it's a different parsing engine apparently, or something on those lines. So type module enables the parser to understand the import word. Without it, it just doesn't. So we have SC login, but it's not appending to the DOM. So, oh. Well, that will explain it. I know why, because I only built on the server, and I need to start doing a full build now. David, pay attention. All right, all right. So now we're building, now do a refresh, and now we have our login button. We have a login button. As you can see, if we view page source, it's not the actual button itself is a pending client site, which is fine, because we're going to make sure our. Well, as you said, for login, we need JavaScript anyway. So there's no point of trying to make the button work without JavaScript if then the click doesn't work afterwards. Speaking of making it login, that's what we're going to build right now. So we're, I just did a nice little script that all does is grab the Firestore and Firebase app and Firebase Auth SDKs, and these little nifty double underscore. This is like a reserve directory for Firebase hosting. And so. So you don't actually to bundle those files yourself, but you can just like, oh, that's already on the CDN. I'm pretty sure, because this is Firebase hosting. Well, it's served on your, under your project. And then once someone hits it, then it's on the CDN. Yeah, yeah. Sure, but. But the cool thing here is this init. And so you usually have to say like, Firebase.initializeApp with your credentials. This one does it for you. We just, we generate it. Oh, because it knows which project it is, so it already. Correct. Oh, that's smart. I like that. And it shall also make a comment that we've deferred all these scripts because modules are deferred as well. Apparently 4.10 of the SDKs out. Yeah, well, I haven't tested this on 4.10. Deal with it. Feel free to later on grab the project, fix it for 4.10. Well, hopefully it should just work. It should just work. And then give us a PR. I would appreciate that. So we're not going to risk it. We are tight on time. So we're going to get, Firebase is on the window right now. We're going to work on making it more like, you know, ES module, like compatible, but right now we're just going to grab it from the window. So we're going to say Firebase.auth as a function. And then what we want to do is we want to say, OK, when someone gets this btn login. So I wanted this.querySelector, hashtag btnLogin. And this btnLogin, we're going to add an event listener on its click. OK. And so when someone logs in, when someone clicks this login, we want to log them in with Google. Absurd. It is completely absurd. Quick question I'm going to answer in between. Why defer on the scripts? So we have the script in the head right now. And if you don't put any attributes on a script tag, those script tags will block. That means nothing will appear on screen until the script has been both downloaded and executed. But we want to be on screen. The page is already there. And we don't need those scripts to execute beforehand. So we're just putting defer on it, which means just do it later. Do it when we're done rendering the script. And modules are deferred by default. So we know these will execute in order as we declared it just after the HTML has been completely parsed. Which is pretty essential when you're server-side rendering, because you're kind of saying, I have all this content on the page. You can defer all this JavaScript that will eventually create content or allow you to log in. So the user actually gets something immediately. So you can kind of get away with having larger library sizes if they are not involved in the rendering of your first initial rendering. So if you're looking into server-side rendering, make sure to either defer all your JavaScript or use modules. Yes. Very important. Otherwise, you will just undo all the hard work you just put into server-side rendering. Server-side rendering is good for performance when you can defer rendering scripts. If you can't defer the rendering script, then. There's no point. Yeah, exactly. All right. So what we just did right here is when someone logs in, we create a Google Auth provider. And we just call sign-in with redirect, which means when the. Very descriptive name. Yeah, it is. Very descriptive. And so when you sign in with redirect, it will redirect you to another page. Then from there, it comes back. So what a lot of people do is they want to return this as a promise and say, oh, after I'm done with my redirect. That's about how it works. The page is gone. Page is gone, so you can't do that. So there's actually a couple of ways of handling this. But my preferred method is to use the on-auth state changed method. And this will actually fire off if your user is not logged in. The user will be null. But when the user does log in, your user will be populated with its data. So what I'm going to do is I'm going to kind of abstract all the off stuff into this custom element, because that's one of the nice things about custom elements. And I'm going to fire a custom event. So a new custom event of, call it on-auth. And the detail, which is a property for you to store custom data in. It's sort of like your grab bag. We're going to have the detail be the user. And then we'll say this.dispatchEvent. And then the page will reload when it comes back. And then we'll dispatch an on-auth state changed event. Yes, that we can listen to. And then you go back into OK. So nothing is on it, like no login has been happening. You click the button. You redirect it to Google. Your page is not alive anymore. User clicks which account do I want to log in with, maybe type in a password if necessary. Comes back. Same thing happens. You're not logged in. But then the Firebase Auth module realizes, actually, we just did the login thing. Exactly. And then fires an on-auth state changed event. And we can react to it. And we store the user as a property, because then we can actually access that from the AC login element later. We want to show as under what name you're about to post a comment, right? Exactly. So we have this defined. It's another way of defined. We're going to grab it from the DOM. So document.query selector. And that's going to be sc-login. And now that we have it, we'll add the event listener of the custom event we just did on-auth. And what's cool about this is now what we can say is, well, if I'm logged in, I don't need to see a login button. So what I'll say is, if e.detail. And so if we have any detail at all, it's not null. What we'll do is we'll take sc-login. And we will go to the class list. And we will add sc-hidden. Because if we're logged in, I don't need to see a login button. Could you just add the hidden attribute? Set hidden to true? Well, that would keep it in line. This will give it display none. So we'll hidden. Hidden. No, hidden keeps it. Because the hidden is visibility none, right? Is it? I thought it was. I don't know. With someone in the comments. Yeah, go check it out. We'll set an element to hidden equals true. Will it actually move it properly? Or will it still be an invisible rectangle? Yeah, I'm very interested. Because I would like to not do this if I can. All right. So now what we'll do is we'll do a yarn build. And once it has gone through, great. That was a dictionary lookup for some reason. And now let's see. Well, we have, let's make sure, let me check something first, all right. So it does have sc-hidden. Because I have done this in the past where, let's check out the styles. Every time I made it to say, I said display hidden. And I really mean display none. Display, because I've already logged in. I'm actually logged in on this browser because of the test runs I've done. So it's going to pick up that I'm an existing user. Hard basis, that's smart, huh? Yeah, it's amazing. So what I need to do is not display hidden. I do want it to, man, after that conversation of what it does, and I don't even write the right syntax. All right. So now let's build that. Apparently display hidden does not, it didn't mean visibility hidden, right? Oh, that's what I was thinking. I meant the attribute hidden to true. Does it display none? Yeah. That's much better than what I'm doing. I thought that did a visibility hidden. No, it actually removes it from screen readers and whatnot. It's quite nice. It isn't very nice. And every custom element has it by default. Well, I've already written that code, so it's great. We're going to take it. It's fine. It works. And that's what's most important. So the login button is gone because you're logged in. Because I am logged in. It's SE hidden, supercharged hidden, hashtag branding. So now we're going to do our first bit of server-side rendering with the SE comment form. So we're going to create a new element, SE comment form.js. And I'm going to do some copy and pasting. That's what real developers do, real lazy people. Will this video be available later on? Yes, it will be available as a video on demand on the Chrome developers YouTube channel, which you're on right now because you're watching this. So while you're at it, subscribe. All right. It's good stuff here. So now we have our comment form, and I'm going to just go in. I'm going to take the insides of this, this inner HTML of this div. I'm just going to paste it in here. So now I have a nice little template. And for the server, I want to not just render the inner HTML, I kind of want to render the outer HTML tag included. So what I'm going to do is create another static method called component. And that is very similar, but the difference is I'm actually going to include the tag name. The own tag name of, you could even reuse the other function inside so you don't have to, you're way ahead of me, aren't you? You also taught me this one. Well, parts of it, I would assume. So this right here, we'll just call that and put it in the comment form so we have our full component and we also have the ability to just say the template. So it's a little bit of flexibility. So now what we're going to do with this one is we want to server render it. But we're not going to be able to server render this as is because while this is going to be in common JS, no doesn't know anything about the HTML element. Right, it doesn't exist because Node doesn't know about the DOM or anything so there's no HTML element base class. So if it sees this, the browser will be just happy, Node will just error out and we won't be able to do anything. So we're going to do a nice little trick that you also taught me. I did teach him that. I'm actually quite a part of this one because for server rendering, you need these template bits because you want to render this template on the server side. And so ideally, you just want to reuse the class you just wrote on the server side. But you can't because the HTML element doesn't exist. So teach me, David, how do you fix that? We're going to say, are you Node? And then Node's just going to be like, yeah. And we're going to be like, use this HTML element class I made. And then Node's just going to be like, OK, sure. And then what we also say like, are you the browser? And the browser's like, yeah. And then we're going to be like, use your HTML element class. And it's like, OK, I was going to do that. Anyways, thanks. And so that's effectively the code we're going to write. This is called comment-driven development. I mean, I would prefer to see comments like this in my code base because I would be like, oh, that makes sense. So we're going to create this little placeholder variable. And we're going to say, if the type of process is not equal to undefined, which effectively says, if there's a process, we know that we're in Node. And at this point, we can say, all right, if we're in Node, we're going to have to make a sort of like a blank HTML class. So we'll assign this placeholder variable to class HTML element, which actually doesn't even matter what you name this, but for readability's sake. Else we know we're in the browser, and we're going to say that HTML element is the existing HTML element. David, that's so smart. And then just to sort of make things a little nicer, we're going to say export class SC element because branding. It's hashtag branding. Extends from the underscore HTML element. So now, in my comment form. Instead of using HTML element, we're going to use SC element. Because within Node, it's just going to be a normal base class because we don't actually need the callbacks in Node. We just want to have access to our static functions or methods that we defined. And in the browser, it will still be a proper custom element. It's like win-win. It is. It's like two extra lines of code. However, you do lose some of the autocomplete within VS code because it doesn't go through all of the. Well, what if you were using TypeScript? That's yes. It would know the chain. You are correct. But that's for another time. Today, we're keeping it simple. All right. So we have SC element. And this is good. So now, what we can actually do is we need to set up our Babel building. I'm going to say that 10 times fast. And so Babel building. Babel building. Babel building. So Babel RC. And I have a little snippet for this. This is all my Babel setup. Like usually, you hear about like big webpack configs or crazy Babel setups. This is pretty simple. All we're doing is we're just taking anything that's used in 2015 and our components. And we're going to copy them to the functions directory, but make sure that they're in common JS format. So that way they will run. Right. So what I thought that Node already has imports and stuff. They do. But that's only in Node 8. And it's also only available underneath an experimental flag in Node 8. But I mean, we are at Node 9 now, I think? Node 9. I think Node 9. I don't know what the experimental. I've been using it for a while. So what Node version are we running on, Firebase? 6.11.1. That's a bit old, isn't it? It's a little old. We're working on getting to Node 8. We're going to cope. We're going to cope. The only place where it really hurts us is when we have to call .ben, which we'll do here in a little bit. Otherwise, we could use a single weight. Jason Miller just said he would give us $1 for pre-act reference. Here's your pre-act reference. I actually almost used pre-act for this. Oh, that was interesting. Yeah. Sorry, Jason. But we're not. I was very close. We're staying vanilla. All right, so now do refresh. And I didn't register it or do anything with it. But it still, page still works, so that's good. So what I want to do now is I want to actually get the component on the server. So I'm going to require from . slash components, slash sc comment form. OK. And so this is going to be a comment form. So it's interesting, because what we're used to from the import syntax works exactly the same, but require. So it almost doesn't change anything except syntax. And we can even see we have our components here in our sc form. Like, it's exports.commonform, all this badly stuff. I'm glad I don't have to do it. All right, so now what we want to do is we want to effectively be able to say, all right, we know we have comment form has this component method that will spit out its HTML, its outer element. So what I'm going to do is I'm going to go into the thing, and I'm going to actually create a comment. So I could and probably should use a templating language like a EJS, say, handlebars, most of that. I should have years and years of work in a developer community ecosystem. But I'm going to use HTML comments. And back to the basics. So maybe you're just going to replace those, right? You're just going to use them because we're working in the back end, as I said, we're using strings. We used each of eight encoding to tell nodes, give us strings, so we can manipulate them. So now we have comments in there. And I'm going to make a function that does this for me in a more readable way. And I want it to be really simple. And when I think simple, I think Java. So I'm going to have it be a part of the Java builder pattern. Oh, fancy. So now I'm going to say add comment form. And the way this will work is you'll be able to say, OK, everything's going to return this, but we're going to do a replacement up here. And then we're also going to have to add all the comment list and all those comments. The builder pattern is really nice if you have objects or instances that contain really complex and lots of optional options, all these things. Because this way you can iteratively build up the object with the data that you want. And then in the end, you say, like, build this for me. And then you get out your created object at the end without having to overwhelm a user with a constructor that takes a massive options object, which I often see in some JavaScript APIs. This is a really nice way around that issue. So basically, we're going to create a little helper function that says, all right, we're going to replace the page value with a holder and a replacement. And so in this case, we're going to say this dot replace, and we're going to take this comment form one. Yeah. And we're just going to, as a string. And we're going to say, all right, now take the comment form dot component. That's neat. So in the HTML, we just have little markers. Put the comment stuff here. Put the, what is the other one? The comment list here. The comment form here. Put the comment list here. And then we can actually reuse the templates we already written from our elements and inject them using the builder pattern. Yeah. So now, we're passing the index HTML, because that's the original page we're going to do all of the manipulation on. And then we're just going to build the page and send it back down. So now let's do another build. You know one thing I like about using yarn? Oh, I did that thing again where I modified the, no. I didn't even have it file open. I know. All right, I guess I'll write that again. All right, I can do that quickly. You entertain everyone. Yes. I actually just saw a question I found interesting. David, I think you confused simple with verbose. I don't think that's right. Because same, same word. Those two things are pretty much orthogonal in my view. Like, sometimes verbose makes things simpler for the reader. And I feel sometimes there's a misconception that people write code for the computer and not for other humans. Most of the time, the people who have to read your code are humans. Computers are just, you know, they have an engine. They will always read it the same way, but humans have, you know, like a human background and will interpret your code and will try to guess what your intention was. And so verbose can be a really good thing. Verbose is not expensive. We have JavaScript minifiers and very smart tools that squash your code down to a small size. I'm not saying you should not write elegant code and just do everything super explicit. Like, always make a variable 15 characters long when one character would have been fine. But I feel for example this page builder does a really good job at communicating what the intention is. You're not just doing string.replace two times and you're replacing somewhat cryptic comments with some other content, but you actually know, oh, I'm injecting. I'm adding a comment form. I'm adding a comment list. It adds semantics. And it's also a very extensible pattern. So it means later on if you want to suddenly inject more things, if we're going to build a more complex page, we can just add new methods to the builder without having to manipulate a cryptic HTTP getter function. So I think it's actually a really nice pattern to use. And yes, it's more verbose, but usually it helps other people understand your code. And that's a good thing. All right. So you caught big up? I did. You're editing the right file now? I am. Well, I actually copied and pasted it or copied it before I built because I was like, no. If you mess up once, you're probably going to mess up again. So I hedged my bets. All right, so this should work. All right, this is good. Copy the X. Now I'm all superstitious. And I'm fairly certain it's highlighted down there. It looks good. To me, it looks like you're editing the right file. All right, we built. Now what I'm going to do is do a refresh. And it looks weird. It does look weird. I mean, it looks like modern art, almost. I know why, though. You know why. That's good. Because custom elements are not display block by default. They're not display block by default. They're display inline. Have you ever seen your element all messed up? It's probably because it has not had any kind of block display. Because you started with divs. You started programming with divs. Then move to custom elements. And those are display inline. So I'm not sure you know. There is my overloaded one. Just display. Enter display block. All right. So now that'll work the next time. So we have this form. And so what I want to do now is, now that it's on the page, we need to first define it. Because that is something that is easy to forget to do. And so we're going to comment form, sce comment form, custom elements.define, sce comment form. And now we have a new element. So that should be back to working. So what we want to do is go to the connected callback inside of this. This is where we're going to actually say, when someone types something into the text area and clicks that button, we're going to emit our own custom event that we can then listen to add to Firestore. So it's an actual Firebase store. Yes. So now to do that, we need a handle on btnsend. It's that query selector, hashtag, dtnsend. And then what we'll end up doing is, say, this btnsend, add event listener of clicks. So when somebody clicks inside of our element, we're going to create an event, so a new custom event. And we'll call it comment sent, why not? And we're going to end up sending a detail object that I'm going to make right now. And so we want to get the value, or we'll just say the text. And that is from, let's just say, this.txt. We want to have all the data we need inside that event. So we're doing our own event, say, yo, a comment has just been sent, meaning somebody pressed the send button. So in that event, it should say, what was the comment? Because we want to edit our database. So it should be in there. So now we have our event, we dispatch it. One thing I like to do, too, is take the text areas value and clear it. So they sent it, the text doesn't have to remain. So now inside of index, we're going to grab this from the DOM. So scform is document.query selector of sc comment form, I believe. Yes. And then we can say add event listener. And we can say when a comment is sent. I love this feeling when you write your own event name into add event listener. It just feels like you're building a browser. Yeah, I feel very powerful. I just did my own event. It is cool. And so now what we'll do is we'll get a reference to Firestore. So we're going to say, we're going to grab a reference to a spot in your Firestore database. And we're going to add data to it. And in this case, we're basically just maintaining a list of comments, right? It's not a very complex database. No, this is fairly simple. So we say firebase.firestore.collection. So a collection is a list. And we'll create one at comments. We have a collection of comments in our database. And that's what we want to manipulate. That's what we want to manipulate my mouth. And so we want the text is p.detail.text. And now what's cool is we can actually say, all right, let's store the photo URL of our users because we can get that from sclogin.user.photourl. And we can even do the display name, which is sclogin. Probably going to destructure this, but OK. And this is actually called author name. With an h. With an h, user.displayname. So this is all information. You're adding a new entry to the collection because it's a new comment. And you want to have the text, the photo, the username. I also want to be able to firestore.fieldvalue.server timestamp. I tend to have auto completion for this. So basically what this is, it's a client-side placeholder that when it gets updated on the server, it gets inserted with server time rather than your client's time. Oh, that's interesting. Yes. So that basically means whatever, even though it might take two hours to reach the server because the person who sent the comment wasn't online, you will get the timestamp of what is when it arrived on the server side. Actually, it will know when there's latency compensation between the client and the server and it understands any clock skew between your device and your server. And so it will know how to compensate that time when it gets updated. Not bad. Yeah, it's pretty cool. Here's a question. Are you using K-Bub case instead of camel case to differentiate native events from your own events? I don't. I want to imitate the platform as closely as possible. So I try to look at how native elements do their things and try to imitate that because I think browser engineers probably know best. We have data. We do have data. Zoom in a little, maybe. So this is the Firestore UI dashboard console data viewer. I like the dashboard. Data editor. Please, please work. Please, please work. If you are nice to your code, sometimes it's nice back. So we can now add. And so now we can add. We need to render. So what we need to do is go back to our static once, close out here. And what we're, oh, no, not that at all. For reference, this is David trying to cope on a UK keyboard when you're used to an American keyboard. This is very true. It gets just as bad the other way around because I'm so used to the big enter key. And the Americans have the smaller enter key. And I will keep hitting backslash. I keep hitting backslash because of the way. Oh, yours is wider. Ours is taller. Right. It's just wrong for both sides. For both sides. Everyone's wrong. All right, so now I'm going to make a comment list. And the comment list component is, make sure I'm in the right folder. Comment list component is really interesting because it doesn't actually have a template. It's just a holder. It's basically, if we were using Shadow DOM, this would be a slot. But we're going to fake the slot, really. So let's do some copy pasting. Yes, copy pasting, always, always necessary. And go to comment list. And so I don't need a template, but I do need a component because I'm going to render this out from the server. And that's going to have some state. And I'm going to return HTML. Here we go. And it's going to basically. That string.raw hack really got a mileage out of that one. Oh, yeah. I see comment list. And I see comment list. And then inside here, I'm just saying state. So it could be, it's basically a slot. OK. So this is, oh, I don't want comment form. I want to call this comment list. And I'm going to make sure to register it before I forget to register it. All right. Let's say comment list, list, list is comment list. All right, great. So now it's defined. But we need to do the server rendering part of this. Oh, and before I can do the server rendering part, I need to actually make the sc comment. So I'm going to go into my components and do sc comment.js. And we're going to do more copy and pasting. People expect sometimes that professional front end engineers do something else. I think you become better as a developer when you know, when you copy and paste what you have to update after you've copied and pasted. You have to make, let's make mistakes, maybe what, copy and paste is a central skill. Yes. Especially in web developments. Inlining, I'm just going to do it myself. Exactly. All right, so now I'm going to paste in the insides of the comment. And it totally didn't format it for me. That's fine. Who needs formatting? I can tab. And so what we'll do in here is, is we can actually change this div to an image because we will have an image source. And we can use that state variable and say state.photo URL. Oh, so this is where you make the template kind of mutable by saying, like, give me the data that I need to put in here and I'll put it in the right slots. Yes. This is actually, still would still be one to one usable with lit html. Right. Which is what I, because lit can do things way more performally, way, like, that's way more complicated things to do that are in a good way. Like caching and pass instantiation and reusing of already instantiated templates. Yes. We're not doing that. No. So I like to keep it in line with that and then use it when I have to. All right, so now we have our template. I need to create the static component. That's going to return html. This is the last of the components. Oh, that's exciting. I need to have to set up, so I'm very excited. So you're saying after that we are done with server-side rendering? We have it? Well, after I server-side render these components. Sure. But it's simple enough. So this is the base look. Defining how our elements are supposed to be rendered. And then we can use that logic on both the client side and the server-side. And then we can render our components on the server-side, which is called server-side rendering. Demystifying the buzzwords of 2018. Is that what year it is? All right. So now we have this comment as this one. And so I need to go to my server. And in my server, I need to import two things. Actually, I need to import the comment list. Somebody just said, this looks like a good cross-site scripting attack vector. Yes, you're right. We didn't do any. That's why we are not deploying it. This is not production ready. You're definitely right. This is basically taking user inputs and rendering it into a page. There should definitely be some mechanism to sanitize that user input, but we're not doing that. Yeah. All right, so we have a comment list, comment, comment form. All right, so now inside of comment list, we're going to pass in, we're going to add comment list. And we're going to give it some state so it can render out the comments. So at this point, what we need to do is we need to go into our, we need to create a Firebase app. And so we'll say firebase.initializeApp. And what we're going to do, actually, is we're going to go into our dashboard, put our project overview, add Firebase to this, and it gives you your credentials, which will basically initialize your Firebase project to your actual Firebase project. So we need that now for the server side. Yes. Not because on the client side, we did the magic init.js import. Yes. So for the server side, we still need our credentials, obviously. Yes. So there is some magic in the Cloud Functions SDK. I just don't think I'm using it right now. I'm going to do it the safe way. The old way. So now we have all of our credentials in. We don't really need most of this. Like, I don't need a messaging or storage bucket. But I'm going to leave it all in there for now because why not? And what I need to do is get my comments. And so now that I have a Firebase app, I can say const comments ref. And this is just like we did on the client. So Firebase.firestore.collection of comments. And now that I have this, I can get them. But first, I'm going to order them by a timestamp. So that way they all come in sequential order. Because we're adding them with generated IDs. And the generated IDs are not guaranteed to be sequential. So if you just read them, they'll come in random order. But if we add that timestamp, we order by the timestamp. We'll get them. Let me guess. It's ordered by the timestamp. Pick up fast. Magic. And then we're going to call it .get, which is just a one-time get, not a real time. Because we're just doing what it's called. Yeah, we just want the initial snapshot. Snapshot, yes. It's a snapshot. So we want the snapshot, which is not, is your data, but plus other very useful things. So. Very useful things that you're going to discard? Yes. For now. Well, you can get your data back in different ways, which we will use versus the client and the server. So right here, we want to create our state variable, which we're passing in. And we're going to say snapshot.docs. And docs is the materialized state of your collection. So if something gets added, it gets added. If something gets removed, it gets removed from the array, which is different than an event stream. Or if something gets removed, you would want the event of what was removed. OK. So it's an array. So we're going to map over it. And we're going to use object.assign, because we want to merge in the doc.data with the ID. Because the data is not part of the ID, because unless you persist the ID yourself, what you do what you want to do. Paul just asked me what the German word is for server-side rendering. I want to know that. Dean Schleis says, I think I was fooled. I don't know if that's real. So if someone speaks German in the comments, this is how we know it's live. All right, we need some water. All right, so we got the data. Yep. You're augmenting it with the ID. All right. And what we're going to do here is actually do the replacement. And so we have the array of comment with the ID. And so what we're going to do is we're going to create the comments as one big string. So each we're going to create an individual string comment and join them together. That seems like some good old-fashioned JavaScript. Yes, it is. It is very, very old-fashioned. So now we have state, which is an array of comments. And we're going to return a new comment.component and pass in our C and our C.id, so we can ID it with its Firestore document ID. And then now this is going to be an array of strings. And we just want one big string.join. With nothing because we don't care about anything in between. We just want them concatenated, basically. Yes. So let's go grab the replacement token. We can actually delete all this now. Go to the comment list. Replace comment list with actually not just comments. We need the comment list.component pass through. That's like the slot, the comments. All right. So now we are calling that on both places. So now it's time for a build. It's like a, this is like it doesn't work. This is the exciting moment. Yeah. I call pressure to myself on this, too. I could have been like, this is not the hard part. All right. I zoomed in more than I. OK, there we go. All right. It's OK. All right. Don't need that page. Refresh. Refresh. So how do we know if it's server-side rendered? Because we can view page source. That's true. It's still refresh. Why is nothing happening? Why is nothing happening? Oh, I did build. Did I? You did. But are you actually? I probably have an error then. Yes. Unhandled promise retention. Comment is not defined. That seems like something that makes sense. Because let's see here. If we go into, oh, yes, comments. This is why I use TypeScript. Keep your plurals right. That's why I use TypeScript. It's something I run into very often. Also, the linter that I neglected to set up would have called that out for me. Refresh. Might have to rerun the server. No, I didn't. Woo. I did something weird, but we got comments. They are server-side rendered. And I probably put them outside of the document. Let's see here. I see comments. Comment form, comment list. So the comments are running outside the comment list for some reason. So we have this one outside, and then this one also outside. So that's interesting. That is interesting. I like this, because I haven't seen this one yet. So I get to. Probably. So we have, we're going to replace comment list with comment.component. And comment list.component, let's make sure. Oh, I bet you I know what this is. All right, where is comment list? OK, so we close out this tag. Sometimes you don't close out your tags. Weird things happen. Look at that page source, though. I close that out. OK, all right. This is actually weird in the page source. Oh, it is in the page, but error occurred. Well, that is not good so far. Oh, now we're all broken. Oh, did I close out of the, all right, let's see here. Oh, this looks weird. I timed out, probably because of all the errors we've had. I'm just going to restart the server. Yeah, just restart the server. Probably got it. Yeah, somebody else is also saying unclosed tag somewhere. The question is where? Yeah, if you can see that. I mean, that's why the page source should hopefully tell us that. OK, cool, we are running again. Got our weird. Still weird? You know, I kind of like that. I like how it's like a pyramid. Yeah, you know. Nice data flow. Maybe it's a feature, not a bug. All right, let's scroll down. So we have sccomitform slash sccomitlist. You know what it is? I don't see the end of sccomitlist. Oh, there it is. OK, so that, where it is. So we have our sccomit. Zoom in a little bit so that our viewers can help potentially. Actually. Oh, I'm just doing a lol. Diff tag after image tag. Diff? Yes. Yes, that is completely true. Yes, there it is. Whoever that person is. Thank you, viewers. You're always helpful. Like, legit, this is amazing. Yeah, and once they said it, I knew exactly that I had done that. Because I switched this to an image when it was a diff. See, that's why you don't have lines that are longer than your screen. I know. I actually felt like that was a no-no. Whatever. And then it comes back and bites you. Yep. All right, so we get out of the server running. And let's build again. Also, did you set display block on all your custom elements? I did. Good. We thought about that, because that was something. Not sccomitlist, but it doesn't get it. That's fine, because, yeah. So now we refresh. And there we go. So we have, this is server-side rendering. Is this server-side rendered? There's only one, one tiny bit left to do. And that's to have the client pick up on client-side rendering for the real-time-ness. True. That's one bit we promised to do. Are we going to do that? And that's it. And this is the quick part. So this is easy. So now what we're going to do is inside of our comment list, we're going to go to the connected callback. All right. And we're going to say, when this element gets connected, we're going to create a comment sref, which is just like what we did on the server, so firebase.firestore.collectionofcomments. And we'll say this.commentsref.orderby, because we want it to have the same order, timestamp. And we're going to say, on snapshot. Get back a snapshot. So this will be the first time it will be the full snapshot again, right? Yes. So we are going to get everything again, because we So will this also trigger on every new change? Every new change. OK. So whenever there would be a new snapshot, you will get a new snapshot. And we could do limiting and stuff like that. So it's not just everything. We don't have a lot of data right now. This is not production ready, Coach. But it's possible. We can do this. So at this point, we actually want to get the doc changes. Because the doc changes are the events as they occur, not the state of your collection. So basically, Firebase does different for you. You have a local state. You get a new state. And you can just look at the new state. Or you can also, just from this event, look at the differences. It will give you basically, I think, a list of items that have been added, items have been removed, and items that have changed. Yes. So that way, you can go through these three lists and handle them appropriately, fade out things that are removed, fade in things that are added, and just update updated items. It's up to you, really. And that's the cool thing. So you don't have to implement some kind of dissing engine yourself. You already get it. So all we're doing here is maybe you change over the type of, you switch over the type of the change. So if the type is added, it means this is some data that has been added to the collection. And you're probably going to handle that with rendering a new comment element. Correct. We've removed, we just removed the old one. And this is where the ID. So we have already been storing each comment on the server. Its ID gets inserted to its Firestore ID. And we use the underscore so there's no possible collisions. And so at this point, we can say change. Well, unlikely. Not impossible. So this way, you can just take the document ID and grab the element. And then if the type is removed, throw it away. So now we can actually say, if we get an added event, we get to see, OK, is the element in the DOM? Because if the element's in the DOM and it's added, we don't want to re-render it. We already server-side rendered it. We're just going to skip over it. So probably, I guess, the first snapper you get, everything is going to be of type added. Because Firebase is soup that you know nothing. But we did server-side rendering. So we know some things already that are already rendered. So we don't need to re-render them. But if it's not in the DOM, then we can tell it's been added afterwards. And we need to. So we're going to actually pass in this. And we're going to say const element. So we're going to say, good old document.create element of an sce comment. Here, we're going to say element.id is the, where is my tit back ticks, underscore doc.id. And then we can say element.innerHTML, which, if we're using lit or shout-out, we could this is not production ready code. Yeah, we're just going to use innerHTML for simplicity right now. And we're going to import our comment here in a second and say, comment.template, because we already have the element. And we're going to pass through the doc.data, because that's the state. And then we can say this dot append child of the element. Now, up here, I need to import a sce comment, or it will break. Comment, sce comment, excellent. And then now, I can just say this.add comment of change.doc, because it changes the type and it contains a document. So yeah, this is a new item. And here's the item that is new. And that's what we're going to use as the data set for the new comment item. And then if it's removed, right now I'm assuming the element's in the DOM, which is probably not, you know, probably should use some if checks, but this is not production ready code. And did we define, before I run everything, I want to make sure I did what we did not define a comment, which I don't know will matter, but we're going to do it anyways. Oh yeah, you do have to register it. Well, it doesn't have any interactivity. Oh yeah, but true. So it will look right, but it will not. It just won't be right though, you know? You'll know. I'll know. We'll look at it and it just won't be the same. Refresh. All right. So nothing will change now, because if we have to leave a comment for it to change. Now I hit send. And look at that, please. So this is actually kind of cool, because what happened is we send a comment, and our SC comment form element, dispatch an event that was picked up, and we put a new data set into Firebase. And that's all we did, because all the rest, Firebase went into a new event that said, hey, a new item is in your collection. And that was picked up by the comment list element and rendered it as a new comment, so that we didn't even do it ourselves. It was done through Firebase. And if I go onto my dashboard and I delete it, our please work is gone. Amazing. And we did talk about network resilience. So we're going to demo that, if we say offline. So now we no longer are connected. And so if I'm writing a comment and I was like, hey, I'm in a tunnel. I hit send. I'm offline, but it still appears. It still appears. So basically, the Firebase SDK takes care of that. And there's only four, whereas there's five here. Yeah, automatically. And then now if I go, see, we've been getting some network challenges. Let me talk to my server, please. And once it can, there, it just appeared. And if we had a service worker, and we enabled persistence, which is an option you have to do, it'll sync with indexedDB. And it will actually do full offline data persisting across refreshes. Damn. And that is your server-side rendered web component real-time network resilient comment section. Flaky network resilient. Flaky no. One word was missing. That was close. Well done. So we built the thing that we thought we were going to build. And we built it. And you all watched it live. And a lot of people were watching. I'm really happy that you all were talking in the chat and helping each other out. Great to see. As I said, we're going to put this on the GitHub. A link is going to be in the description on the video later on. I'm also going to tweet it out. You should follow David East on Twitter, underscore David East, or me, Desurma. I do these kind of live streams around every month. So subscribe to this channel. You get notifications about our announcements and new videos because we also do super charge micro tips and other things. Thank you so much for everyone who took the time of the day to watch us fiddle around. I hope you had a good time. I did. It worked, so. That's the best possible outcome. It works. That's great. Thanks, everyone, so much. And I will see you all next time.