ElixirConf 2021 - Chris Nelson - LiveView and Web Components

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Music] thanks everybody for coming to live view and web components uh my name is chris nelson um if you want to find me on twitter superchris is my twitter handle and yeah we're going to be talking about live view and web components or as i like to call it a more live-y way to extend live view and i will try to back that up during the course of my talk and you can tell me at the end whether you believe me or not so um i have been doing web development a long time i'm with a group of developers out of cincinnati ohio that we've been doing elixir development for like i don't know like five years or something like that a while and we've really gotten to love live view in the last couple years we have a number of applications in production using live view uh and enjoy it very much and find it to be like a lot of you just a more productive better experience for building what we used to do with single page apps we can now do an elixir but what really i'm going to be talking about mostly is kind of where the edges are where do you find live view to be not a perfect fit and when you run into that situation what do you do about it so let's start with just looking at the live view docs and from just straight this is like cut and pasted out of the live view docs uh live view provides rich real time user experiences with server rendered html that bolding is mine and then later on when it's not a fit we have animations menu and general ui events that do not need the server in the first place are a bad fit for live view i feel like bad fit's a little strong but like i said this is literally just taken out of their documentation and i'm going to add from my own experience a few other cases where i found to be live view to be not the tool that i reach for or where i feel like live view needs a little help and some of those are cases where there's a really good client side solution already and it's just a much easier experience to pull them in than to try to reimplement them from scratch and live view and typical examples that we run into in the real world are maps and calendars you could obviously do those things in live view if you wanted to but man that would be a lot of work and there's stuff that you can just pull off the shelf that works pretty well and then the other case where you really kind of need to reach for something else are when the information is really primarily or even only available on the client side and examples of those might be geolocation or time zones so obviously from the docs and from our experience it's really clear that live view is good at dealing with html that's what it says it does it's good at rendering html it's good at rendering it dynamically so basically kind of the premise of a lot of what i'm going to be talking about is what if we could make html better if we could would this let us do more things in live view that maybe we couldn't do before so that's what i'll be getting into um sorry there will be some javascript in this talk um sorry not sorry i'll try to keep it to a minimum really the focus is how to do live view better so this font i stole from the movie there will be blood the javascript there is a little unfortunately it looks a little more like javascript than javascript i had a java hot picture in here but i took that out because i was afraid to get sued um anyway let's talk about web components uh how many people are kind of familiar with web components already so most people good so i'll go through this really really quickly just to give the people that maybe aren't as familiar enough contacts to follow what i'll be doing the rest of the talk but really they are several specs from the w3c the body that produces the specs for html and everything in your web browser there are a few different specs that work together uh the kind of marquee one that's really maybe the well not maybe it is the most important is custom elements we'll talk about what that is uh custom events i have a little asterisk there because it's technically not part of the web component specs but i see it as so integral for working with custom elements that i'm kind of lumping it in there on my own uh and then the spooky shadow dom because it's october i gotta add the spooky but uh that's literally the name of the spec and i'll talk about what that is uh but one of the really important things to point out this is relatively recent these specs are now supported in chrome firefox safari and edge the browser formerly known as iel though i guess not really but the point is it's supported in all the major browsers without a feature flag out of the box now that's fairly recent so the uh the enablement that these specs provide i feel like as a community is still pretty new and we're still figuring out what this gives us the ability to do uh so i still see it as like early times for this stuff um so custom elements um actually it's pretty simple under the covers the main thing they let you do is define your own html elements you do that by registering an es6 class associating with a tag name the only rule there is that tag name must have a dash in it so that your browser can disambiguate custom elements from regular elements and you have some callbacks that you can implement to specify the behavior of your custom elements that's pretty much all there is to custom elements out of the box it's actually pretty simple um custom events are pretty much just what the name implies they let you make up your own dom events so instead of just having like click and scroll and things like that you can make up your own custom events like foo and bar and bounds changed we'll talk about later you can name them whatever you like you can give a detailed property that holds a payload of arbitrary information associated with that event it's actually a pretty old spec it came along before custom elements but i don't think it gets as much press as it deserves that's just my own opinion and it's been well supported in all the browsers for a really long time oops i went too fast so shadow dom we're not going to dive into this super deeply it is really worthy of exploration and probably talk of its own but this is not the conference for that talk but basically what it is is an isolated dom within the main dom uh and no css goes in or out unless you want it to and there's a way to do that now which is actually pretty cool it's called shadow parts i'm not going to talk about it much in this talk because i don't have time to get to everything but shadow parts is really cool and it lets you build shadow dom and let your designers style things the way they want it's pretty great um the shadow dom most libraries will render into a shadow dom an isolated dom for your custom element by default uh and the cools feature of shadow dom that i'm not going to go into a lot again are slots so you've heard about like live view just starting to get into slots well the original slot idea comes from shadow dom and really what it lets you do is you can have a custom element with content inside of it and decide how to display that content so it gives you a lot of power and we'll actually see that used here in an example so i'm going to start with a really really simple example of where i reached for a custom element and how it turned out in this simple example uh we're actually it doesn't actually have any uh integration with live view but it kind of falls into that case of like things where you really don't necessarily need to reach to look for a live view so uh i'm gonna make sure that my example is still running uh okay i actually didn't need to do that that's okay so this is an example really uh straight out of a real app although i've changed the app to protect the innocent or guilty as the case might be oh demos are fun oh yeah sorry i did that on purpose i put it on a different port so i wouldn't collide and then i forgot about it of course all right so this is an actual app uh tweaked slightly to conceal the identity and in this app if we look at in the inspector and turn on uh the browser simulation mode we'll see that we have lovely responsive design here and we have the menu that was there on the sidebar that collapses into the the hamburger menu and of course when you click that guy it expands the menu and so it's this expand contract element that i'm talking about here in this example and if we look at this guy and inspect him we'll see there's a nav mobile toggle custom element that is that guy there and then there's an outer nav bar element and that's what controls that expand contract thing so when i actually did this on the project my designer said hey this is what i want to do i want to pull in an extra javascript library to do that and i was like well just just give me a minute and see what i can come up with so i'm going to walk through this code and show you what it looks like because it's a pretty good simple example introduction like i said there's two elements there there's the nav bar which is the outer menu element and then there's a nav mobile toggle which is the inner button that just toggles the menu on or off that's it so what do these guys actually look like let's start with the nav mobile toggle this is the entire code for that custom element that's it there's like nine lines there's a class that extends html elements that connected callback is just a function that gets invoked by your browser when your element is wired and associated and booted into the dom and what we're doing there is we're just listening for a click event when the person clicks on the toggle button we dispatch an event that event is called toggle menu and that bubbles true just says like go up the the chain so that's it it doesn't actually do anything it just says a thing happened uh and then the last line of code there is just associating the tag name with the class so that's all there is to that guy on the other side we have another line nine lines of code all that guy does is when it ends up in the dom this is the outer nav bar itself it listens for that custom event called toggle dash menu and it toggles the class open and that classless toggle is actually um it's uh i don't know what it's built into the browser it's a dom there's not a library that provides that that's built in i hadn't used that before so i thought that was cool um and uh that true just says i'm listening for a child event not an event directly on myself and then like before i associate it with the tag so that's it that's the entire example um and it's not so much what's in here that's exciting to me as what's not in here that makes me happy about this code um first of all there's nine lines in each custom element i think that's pretty cool um there is no framework or library that i have pulled in at all to build this uh it turns out there's no shadow dom because i just i didn't need one um they're just plain old html elements that's it um there's no actual client-side rendering or templating going on they're just pure there's a button and then there's a container element and there's some css that styles it but that's from outside and of course there's no server interaction and really why would you want that you're just toggling a menu open or close so um just kind of an example that you can use custom elements to do things it can be simple you don't need any external dependencies and that's pretty nice so let's kind of uh oh yeah sorry and and the kind of theme that i guess i i kind of want to convey when you think about what to do with custom elements or what to do with web components is really about building the html that you wish you had so if you come to a problem and think wow i really wish you know i could do this in live view if i just had an html element that does blah you know you can do that now and it might not be as hard as you think so how am i doing on time okay i'm pretty good um i'm going to go to the kind of other side of the complexity spectrum just a little bit and talk about another place where i often need to reach for something outside of live views and that is every time i bring in a map to an app that i'm building so uh i'm going to set up kind of a little bit of a contrived example here but it's it's pretty close to reality on some apps that i've built we're going to say that we want to build an app that renders a map and within that map it is displaying pins from a database search within a displayed area and what we're also going to do is whenever the user pans that map we're going to go do that search again to get the pins in that new area where they are so that's the entire app pretty simple but also matches up with pretty closely with things i've done so let's think about how we would want to do that obviously we really don't want to roll our own map completely in live view that would be silly there's lots of good third-party javascript like leaf led and google maps to choose from but if we bring in some of that third-party third-party javascript excuse me if we bring in some of that third-party javascript we're going to need to write a live view hook to connect it up to actually make it work but what would we actually need if we wanted to do this completely in live view what would it take for us to be able to do that well what if we had a map element that displayed a map and what if we within that map element we could use other elements to render the pins onto that map and what if we had an event that we could send up to live view when the user pans around that map well if we had those things maybe we could actually implement this whole thing in live view without having to do any custom javascript or stuff um well it turns out i set it up this way there's actually another library this is com you know this is just a custom element library called lit google maps uh lit google maps the lid is for a library called lit which is pretty cool but for our purposes the main thing that's important to us is lit google map surprise surprise displays a map also it happens to emit a bounds changed event when the user pans around the map along with lit google map there's another custom element called lit google map marker that just draws a pin on the map so by pulling in this npm i have custom elements and i think i pretty much have everything i need except the problem of how do i tell a live view when the bounds changed event happened so it's not like click or one of the phoenix live view events that live view already knows about bounds changed is in fact a custom event with a payload that contains the new boundaries so we need some way to send custom events to live view and it turns out that's a problem that i've actually solved uh via an npm package called phoenix custom events hooks this is a published package so just npm install it and what this actually does for us is it gives us a hook that lets us send custom events to live view and so this is how we can associate that bounds changed event and just handle it in live view just like we would any other event so let's actually see this in action real quick and uh what i'm just going to bring up is uh the repo that is the demo project for phoenix custom event hook [Music] do i have the right hopefully i have the right one i'm just going to make sure that i have the right api key hooked up otherwise it won't work and what i did for my example is i loaded a database one of my side interests is i'm a pilot so i loaded the database of all the airports in the country along with their lat long and their airport identifier and i'm using that kind of my data set to search so basically what this guy is doing he's just displaying a map with pins wherever there's a airport and these aren't just like big airports that you would fly a jet plane into a lot of these are like little tiny airports that you would fly your little plane into and like columbus municipal airport in indiana if you're looking for some place to fly to within 50 miles of cincinnati that has a nice little diner on the field that's a good place to go but what this is doing is every time i'm moving this map around it's going back and researching and getting a new set of pins so let's see what the code for this looks like so to start with in our app.js we're going to need to have npm installed phoenix custom event we'll import it and then when we build our live socket we just pass it in as a hook that's all it takes to actually use phoenix custom event and then let's look at the template first so this is actually using this is what actually is in that view rendering that map we have the outer map custom elements we're giving it the api key that's for google to be able to do its thing we're using the phoenix hook phx hook attribute set to phoenix custom event that's what tells it this element needs to be able to send custom events into live view and then we have a cust couple of custom event bindings i've simplified the syntax for this a little bit in the newer version but this is kind of the more flexible syntax in that it lets you say this custom events dom event goes to this event over on the phoenix live view side and in this case that's handy because we have a bounce changed event that we want to send up as bounds changed we have another event that file that fires when the initial tiles for the map are loaded we also want to send that up as if it were bounds change just to simplify our code the the rest of it is just straight up elixir template i'm looping over a set of airports pulling out their lat long name and identifier and then i'm using another element within that for the map marker itself for each airport in that set so that's what draws the map and that's what draws the pins on the live view side this is where i'm actually listening to that balance changed event and it's just like any other phoenix live view event except for it has a payload that can be arbitrary stuff and in this case my payload is those uh the points around the rectangle basically north east west south those things have lat longs in them i'm not going to show you that part but basically i just have a context function that if i give it that kind of structure it knows how to use post gis to search the database and find all the airports that are within that rectangle and so that's all there is to that any any questions on that so far i have not i guess it if i scrolled the map just correctly i could probably do that if you get within a tightly constrained space i've probably been to all of them so that's cool when we actually have a custom element that already does what we want to do but let's now look at the cases yeah i'm doing so far okay on time let's now step through what it would take if we didn't already have a custom element that we could pull in that that did what we wanted to do um so um creating custom elements um like we saw earlier you can just write your own just by extending html element but when you get a little farther along you might want a custom library to give you a little help i like lit but there are a whole lot of different options to choose from but some of the things that you might want a library to help with are being able to automatically re-render what's inside that element when a property or attribute changes uh serializing or deserializing data and then providing some way to do client-side templating if that's something that you need so i'm going to walk through an example and of course all examples by law have to be to-do lists i think that's true okay i just made that up but i lack creativity so that's what i'm doing um so let's see what it would take to build a to-do list element uh and hook it into live view so to start with um we're and i'm going to just jump to the commit rather than do all this stuff uh by hand we're going to make a new project we're going to npm install lit and then we're going to create our element so let's see what that looks like so i have a series of commits in this project and i'm just going to walk through the relevant code let me give you a little font upload is that pretty readable for everybody okay cool so this is our to-do list custom element uh we're extending lit element which is a library from google from lit and it just gives us some ergonomics the main thing that we have here is we can declare some properties and we're declaring an items property which becomes an item's attribute as well which we'll see used here in a minute and we're saying that's the type of that guy is going to be an array and then we have a render function that we implement that is just returning a javascript template literal that is just returning a list of all the items by mapping over them and outputting a list item and then we associate our tag name so nothing too exciting just yet but i want to show you what's there going back to [Music] the next step um oh sorry i went back instead of forward to the next step what we want to do is we want to create our live view and actually use that element and show what that looks like so if i check out the next step in the progression oops sorry that is the wrong somehow that is the wrong window sorry about that oh that's why so check out if we go to that point in the progression now we actually have our live view and at this point all he's going to do is in the mount he's going to make up a couple two up two items in the template the entire template is just using that element which takes the the attribute from the socket assigns that contains that list of items and passes it straight into our to-do list and if we run that guy we'll see that oh my bad i forgot to shut down the other one first oh so i'm going to start that guy again i'm going to get out of that full screen mode because that's messing me up and of course it probably was working if i just go to the right route sorry about that okay so this is displaying our to-do list and now of course what we want to see is how do we actually add a to-do list to our item so what we want to do next is in our to-do list custom element we want to actually add an item input so we can type a to-do in and we want to dispatch a to-do event and just so i i think i'm going to i think i'm going to jump to the set the section where i actually handle the add to do as well so i'm going to go two steps here so if i go to this step what we should actually see is a to-do list where we can put your left foot in and we can of course shake it all about so how does that work let's go through that code briefly if we go to our component at this point what we've done now is we've added a input in the template and a button wired up to an event well wired up to a function really more properly that function actually dispatches a custom event of add to do with the detail that has the item that they typed in that text input in the live view template now what we need to add at this point is our phoenix custom event hook again and i'm using the simpler syntax for how to send events up i have a phoenix send events attribute and i just list them as a comma delimited list so in this case i only have one which is add to do and then finally handling that ad to do is just pulling off the item from the payload of that event and just sticking it on to the end of the to do items so this is kind of showing if you're building a custom element from scratch how that might look okay i'm still all right on time so i have one more example that i want to show you with you with you guys and that is uh wrapping third-party javascript so this is a case where uh you might encounter where you have some library that does a thing that you want to do but there is not a custom element for it already and the example i'm going to work through is from a real project and that is calendaring i wasn't able to find a custom element that did calendaring just displaying a calendar really the way i wanted to but there was a pretty good available it's not really open source but it was available for free to do what i want called full calendar dot io and what i'm going to do is i'm going to pull in full calendar to display our to-do items on a calendar just kind of see how that looks i'm not going to go through at that in this small steps as i went through the other things because that would take too long but i'm going to show you what it looks to to do all that we're going to make this to-do list calendar and basically what we're going to need to do we're going to need to add due date to to do's which i'm not going to walk through i'll just show you what it looks like to have that but it's it's just you know instead of a string we're going to have an object with a string and a date no big deal we're already going to have installed the full calendar core and day grid those are just the pieces of the library that we need and then uh the part that i think is probably more interesting we're going to create that to do calendar element and then actually add that to our template and what this is going to let us do is we're going to have two different elements but live view is going to be the source of the information for those elements so instead of those elements need to like interact with each other through javascript they're just going to be dumb all they're going to do is basically get data and render and dispatch events when things happen and live view is going to be the orchestrator of the information that kind of pattern i found to work pretty well for building live view apps it lets us really do the hard work and heavy lifting and live view and elixir like we want to do and just kind of use html more the way it was designed to display stuff and emit events so how's this actually look oh yeah that's getting a little ahead i'm going to keep saying i'm not going to full screen i'm going to end up full screening but that's okay so at this point we've changed our to do items so that they're a little bit more complex and we just have strings and due dates and we have our handle event really doing the same thing it's just a more complex data structure we also have one more function here which we need in our view and all this function does is it takes the to do items and turns them into the data structure that the calendar is expecting on the custom element side our to do calendar which i'll show you the output of here in a second this is where we're actually interacting with our javascript library called full calendar and we're just using its api and the way it works is it wants an element and because we're a custom element we can just pass ourselves as the element to tell it to render into and then where this is just some setup code to tell it we want a day grid and then finally we give it the events which are the data passed in from our attribute we have to do a little bit of work to tell the calendar that when something changes we need to re-render and that's a little bit tedious just because that's the way this library works unfortunately where we need to tell it hey all your data's gone now here's some more data but the outcome of it ends up pretty nice in that in our template we have two elements our to-do list and our calendar and we're just giving it the same two items in each case and if we go back into our demo and reload we see that we have a calendar with several items from hokey pokey already scheduled we'll add another one on october 18th and poof it appears on the back so i think that's my final demo i had prepared but i just wanted to kind of come back and summarize a couple things some lessons that we've learned using this kind of idea in the wild that have turned out well for us this idea of passing data into our custom elements via attributes or properties but attributes are really simple and that you can actually just use the template and eex to pass in the data and then of course the idea of when you need to communicate something back from your custom element into live view custom events work really well for that another thing that we've learned um actually you know from making mistakes to be blunt um smaller custom elements are better the smaller the better um what we've learned is that you know in some cases you know i've had some situations where i've gone like well this thing that i'm building really has nothing to do on the server it doesn't need to do anything on the server um but you know by taking something that wasn't a fairly complex situation and doing it as a custom element you can end up with a custom element that has its own fairly complex internal state if you find that yourself in that situation you might want to think even though i don't technically need to do this on the server letting live view take care of it still might be the right move because what i what we've learned is that elements that have their own complex state really end up being like not a lot of fun to to work with and fiddle with and uh and maintain so smaller elements is better um you know that guideline of like do i need to do anything on the server or not you can look at that more of like a rough guideline not a hard rule that's what i've kind of decided so multiple little custom elements that you let live you pass them their state that works really well another thing somebody mentioned this in another talk already but it's pretty darn important i've definitely been bit by this one if you have a custom element that doesn't have a shadow dom that renders its own content you need to make sure you tag that with phx update equals ignore if you have a shadow dom like there's nothing phoenix is not going to step on you because there's you know it doesn't have access to the shadow dom but if you have like just plain old dom children that your custom element is building they will get stepped on by phoenix live view and it's a source of much confusion and frustration and you can lose hours of your life going what the heck is going on so that phoenix update equals ignore uh yeah file that away that'll save you some pain uh another kind of set of things that we've found helpful is being able to test our custom elements custom elements themselves are not hard to test in terms of testing your javascript there's a really good project web test runner that runs your tests using puppeteer or playwright which uses real headless browsers that's pretty important when you're using apis like um that are newer because you know like a fake dom it's not good enough um the other thing that we've found helpful is if you want we are pretty big fans of wallaby and end and tests to make sure our app actually works and if you're writing wallaby tests that interact with something inside of a shadow dom that can get really fiddly and there's no out of the box support for that but i ended up being able to write a feature in wallaby which i have as a pr right now it's not been merged yet so if you want this feature you'll need to use our fork which is just uh gaslight slash wallaby gas light is the name of my company and it will give you a function called within shadow dom and within shadow dom will let you uh manipulate the shadow dom directly from uh wallaby code so that's pretty handy um so yeah i think oh yeah i'm right at time so uh i don't i don't think there's anybody else in this room right after me so if if there's a few questions i'm happy to answer them thank [Applause] your you list items you had a syntax that was like item equals and then a json object was that just how heats work yes yeah the the this the curly braces uh maybe we can i think it's just teeks that you're talking about but maybe we'll just if i can get there i'll get there are you talking about this guy yeah yeah yeah that's just heat syntax this is the attribute and it's just pipelining it into a function that formats those to-do list guys the way that the calendar wants them oh oh oh so that is basically handled by lit which basically knows how to deserialize json i basically just told my element i have this attribute his type is an array and that's enough that it will go like oh if somebody puts a string of json in this attribute i know what to do and i'll just take care of it for you so yeah that's lit that's doing that but good question thank you yes sir there's probably yeah there's definitely some things that i didn't walk through one of which is i'm pulling things out of the shadow dom to interact with them and i didn't have time to walk through that in any amount of detail but i'm just pulling out the values from the item input and due date input and sending those along sure anybody else uh do you think that using web components and something like surface is compatible they look on their face to be like different yeah they look to be fairly different they look at first similar but they're they're different in terms of everything's happening on the server or with surface everything's happening on the client with web components so i i have a dream and my dream is that i could have a custom element that just like is a live view and renders it from live view but that's magic that i haven't gotten to yet but not quite it could just be some simple as just like instead of this server rendered live view if you put this custom on the page it makes a connection back over web sockets and then render some stuff it wouldn't have to be that fancy it would just require a different level of connectivity with live view that i haven't sorted yet but that's why it's sort of in the dream fantasy idea stage all right well thanks a lot [Applause]
Info
Channel: ElixirConf
Views: 2,563
Rating: undefined out of 5
Keywords: elixir, liveview
Id: xXWyOy9XdA8
Channel Id: undefined
Length: 45min 17sec (2717 seconds)
Published: Sun Oct 24 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.