Ecto Associations in Phoenix LiveView: One-to-Many

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi my name is Dennis Beatty welcome to another elixir tutorial to start out I want to say I'm sorry for being a my a for the past few months I got married at the end of July and so a couple months before that the months since have been a little bit crazy but now my life starting to calm down calm back down again and so I'm hoping that I'll be able to get back to a more regular cadence but if you're interested in elixir in Phoenix and live view and ecto I highly recommend you subscribe to my channel basically I'm starting a new series right now and I want to make sure that you get the updates as each video comes out about a year and a half ago I gave a talk about ecto at Lonestar elixir calm down in Texas and originally I plan to speak about ecto associations and how to set up many to many relationships and stuff like that but I ended up changing my talk to cater more to how to observe data just because that's kind of what I was working on at podium at the time but I got a comment on my last post asking me to talk about ecto associations and how to manage them in in Phoenix live view so I've decided that I want to create a full series to cover that one of the most important underlying principles of relational databases is that they're made up of tables that are related to each other related relationship relational that's kind of how that name came about but those relationships allow you to more easily manage your data by keeping it in separate tables in ecto these relationships are usually referred to as associations basically you have a row in one table associated with one or many other types of data and other tables there's kind of three main types of associations that we work with there's the one to one the one to many and the many to many so I won't go into too much detail on all three of these in this post but if you aren't super familiar with relational database design and you kind of want to get a head start on the other two there's a site called database guide that has pretty good articles on what a relationship is and on the three types of relationships so I'll link to those in the blog post as well each post in this series is going to cover one of the three types of associations and how we can use it in our database and manage it with phoenix-like view for all three parts of this series we're going to be working with the example of an admin panel doing user administration normally the use case wouldn't really be worth setting up the the real time features just because it's it's a lot of work without a ton of reward but Phenix live view makes it so simple that it becomes worth it so basically if you have multiple admins trying to do administration at the same time you don't want them to be overwriting each other's work and so by making the pages use kind of real-time functionality with Phoenix live view you can make it so that they'll see the updates in real time and they will be able to to know if they're about to overwrite something that they should and so it just makes your admin panel a little bit safer so before we begin you're gonna want to make sure that you have kind of the latest version of everything I'm making this video using Erlang 22 and elixir 1.9 and depending on when you're watching this those might not be the newest versions anymore but those are the versions that I'm gonna be using and so if you're using anything different you might have to make some slight adjustments to make it work for you you also want to have the newest version of the Phoenix generator I'll include the command in the blog post to make sure that you are using that so let's go ahead and get started we're gonna create a project and we'll call it user admin so if we just say mix Phoenix dot new user admin and then let it do its thing fetch and install your dependencies and while that's going let's talk a little bit about one-to-many relationships because that's what we're gonna be focusing on in this post they usually tend to be the most common type of relationship deal model and they can be really useful for maintaining data integrity basically in our example we're going to be modeling users and roles so in many applications users can have a role like an administrator an editor a subscriber a basic user and typically they can only have one role but each role can be given to many different users because of this users and roles it's best to find as a one-to-many one role poor mini users kind of relationship and so I'll kind of show a picture in the in the blog post as well of the data model that that this uses and so if you have any other questions about this please let me know on Twitter hopefully I can clarify it for it I can clarify it for you a little bit better otherwise let's go ahead and start setting up our database well actually I take that back first we want to go in and add Phoenix live view to our project so if we go back it looks like my dependencies are done compiling so we're gonna go ahead and CD into the user admin directory and then I'm going to run mix active create and so that generates my database I'm actually gonna run mix after that reset because I was running through this example and forgot to clear my database earlier and so now I'm gonna open up my editor I'm gonna use Adam with the developer flag just because I created or updated the elixir language to include live eex syntax highlighting but that hasn't been merged in yet and so I'm still using the developer version with my updated code so that we get that syntax highlighting so with this in place let's go ahead and head over to the Phoenix live view github repo and debride me on this has the installation instructions I'd recommend following that rather than me because they might have changed since I made this video so we'll start out by in our mixed IP access file we're going to want to add Phoenix live view there and save that and then we'll run mix steps get again to get that dependency then we're gonna want to run mix Phoenix gen dot secret 32 to create a secret for us and then we'll be copying that into our endpoint config so we go into config whoops that is the wrong thing you know what I'm gonna call get in it to start a git repo and that's gonna make my searching a little bit better so if I go into my config now it takes me to the correct one and then I can post here and then grab the secret that was generated okay and then in our router we need to and then use your admin web you'll be adding some new controller stuff okay then exposing the socket points and adding the JavaScript dependency then let's go ahead and install well that's happening socket in our app that GS file and finally import our CSS okay and with all that done we now have UNIX live view in our project so to start out let's go ahead and do our database migrations so when you set up a database when you set up a one-to-many relationship in a database typically what you're doing is adding a foreign key which is an ID that corresponds with the idea of a row in a different table into the table that makes up the one in the relationship so in this case we're going to have a role ID field in the users table that refers to the ID field in the roles table hopefully this will make a little more sense for you as we're actually writing it up so in order to properly set up that foreign key Postgres requires us to have the mini table already in place in this case rules and so let's go ahead and start by creating our rules table I'm just going to use the ecto generators to do this so if we type the mix ecto gen migration create roles it's gonna take a second to compile and it will actually create that migration I'm just gonna copy that okay and so then let's go ahead and say we want to create a table and call it rolls and we want that to have a name and that will just be a string and so with that let's go and migrate it to migrate and so now that table is created and so let's create another one let's say I mix egg third gender migration create the users and so let's go into that now and so this one we want to say I want to create a table called the users and we'll have a name for the user as well and that'll be a string but then I want to add a role and so I'm gonna add a column called role ID and it's going to reference the roles table okay and so let's go ahead and run our new migration as well and so with that in place we should have our database all set up so let's add some schemas so that we can actually manage that with hecto so if we create in Lib user admin let's create a new file we'll call it users BX and for now I'm just going to make that an empty module and then let's create a folder called users as well and that's gonna have a role and it's also going to have a user schema and so starting with our role we will all that user admin users dot roll and we're gonna want to use echo dot schema and that is going to have a schema in it called rolls with the field that we created in our table called name and that'll be it and then in our user schema call that user admin whoops dot users user again we'll use active schema and then that schema is going to have you is going to have a name as well but this one remember we have that relationship with our roll we're gonna want to say that it belongs to a role and that needs to reference the schema to which it's related or associated and so we're gonna want to associate that user admin got users dot role and so with that in place we have our schemas all set up so now is where things start to kind of get interesting we're gonna go ahead and create our actual live views so let's go ahead and start by in user admin web we will create a new folder and we'll call it live and then in this new live folder well actually inside of that let's create a new folder as well call it user live and then inside of that folder then we'll create file called new DX and so we'll call this user admin web got user live new and so this is gonna be our actual live view so we want to say use Phoenix dot live view and this is similar to a controller if you're more familiar with the Phoenix without live view so our live view requires two functions he has started and that's gonna be a mount function which will take in a session in a socket and it needs to return okay with the socket and then a render function and that's gonna take in signs and then we need to return a template for now I'm just gonna use an empty empty web view template sigil and so that just makes kind of an empty template and then in our router we're gonna want to say come on live so if they go to slash users slash new I'll take them to user live dot new which will be linked to our I'd live file and so with that setup let's go ahead and start up our server so if we say makes PHX sir oh it's not compiling undefined function live interesting so that means that our endpoint probably didn't work out properly turn on our endpoint our use your admin web didn't work out properly it looks like our Live View stuff didn't go in there so let's go ahead and go back and grab that router if we save that and then try this again there we go so let's go ahead and go to that page okay so we don't get anything there but the slash users slash new now we're seeing our basic template but the template that we told it to render is just blank and so let's go ahead and create that template so if we go in to use your admin web slash templates well actually first let's create the view so if we go into the view and then we say a new file and we'll just call it user view module use your admin web user view and that was just use user and web view okay and then let's go in and create the template so if we go into templates and create a new folder whoops new folder and we call that just a user and then let's create a new file inside of that and we'll call that nude HTML GL eex so we're gonna just want to have a header little say new user and then while the form and our form is gonna have a field set close those and so inside of our field set we're gonna want a label and it'll be for a named field and the label is just gonna say name and they'll have our input and ad for it will be named field so that the label can point to it and then we're gonna have a label for a role field and that'll just say role but this one's gonna be a select so say slice and use role field and then we're gonna give it some options so if we say administrator let's say the moderator let's say user [Music] and let's say guests I think that's all we'll do for now okay so with those I think that'll be all of our options for roles and so then let's add a Smit button and there we have it kind of our basic form so let's modify our life our light view so that it will actually display that so we alias our view and then we tell our view to render new HTML and give it our signs then it should render that so let's go ahead and refresh this page and there we go so we got our form so now if you go to if you go to that page you'll see your form but Phoenix actually has some really nice helper methods to make our HTML form a little bit cleaner so let's take advantage of those so let's go back and we'll create a change set that the form can take advantage of and then we'll add those helpers so if you go into your users context and actually let's start in our user schema let's create a new change set of function so we'll start by importing active change set and then we'll create a def change set and that will take in a user to be changed and then a map with the changes to be made and so we'll take the user and then we'll cast it to a change set with the changes but we're only going to allow the name right now to be changed and then we'll validate that the name is there will validate that is required so I'll just make sure that we're not trying to create any users without names so now let's create a function in our users context that we'll call this so in here we'll alias our user schema so I'm gonna actually write little Doc's for this function so we're gonna create a change set change a user and then the type spec on it as well so we're gonna have a change user function that's gonna take and a user and then it will take in the map of the changes and then it's going to return an ecto change set type so now let's actually create that function so change the user take in a user and the changes will default it to an empty map and then we're just gonna call user dot change set with the user that's given to us and the changes okay so with that in place let's go into our light view and we're going to actually use that so let's Elias user admin users and then we'll also alias user admin users user so then in our mount function so when the live view is first mounted and set up we're going to call a change set equals users change user and we'll give it a blank and empty user and then we need to assign that change set to our socket change that has changed set and this makes it so that it'll be available to our ligh BTX template so now let's modify the form to actually use this so if we go over to our form we're going to change this out we'll say F equals form 4 and then the change set that we're getting from our live view we want our form - not really submit anywhere but we're going to add this Phoenix submit is create attribute which tells Phoenix live view that when the form is submitted we want to send a message to our live view telling it to create a user so then we're also going to add a helper down here so we're gonna replace our submit button and we'll say submit with title create and then we're gonna use a special function area special attribute the Phoenix disabled with creating so what this will do is when our form is submitting it's going to disable the button and change the text to creating and so then finally let's make a couple other changes we're gonna change out our label here and that'll be for the name field and we use our 4 as well and then we'll change out our input as well and so this is saying to go ahead and use the the F which is grabbing the data from the change set and this will make it so that if your changeset actually has data in it if there's a name in the change set it will populate this input with that name so it gives us a lot of really nice functionality out of the box that we don't have to write code for so now if you reload your browser the forms gonna look exactly the same but if you submit the form now you'll notice it turns red and displays a little spinner and if we look we get a nice fat red error in our terminal and basically it's saying that user live new handle event with a narrative 3 is undefined a private so we can see that it's trying to call this handle event function it's passing in a create string and then some some data for that user just setting the name to blink so we're gonna handle that right now and actually create a user so let's go ahead and create the function in our context to create that so if we go into our context let's add an alias for user admin repo and that's what's gonna let us actually interface with our database and then I'm gonna create a new function so create a doc for it and that's just gonna say create a user and so the type spec will have create user and that'll take in a map of attributes for the user and it's going to just return either an OK tupple with the user in it or it'll return an error tuple with a change set in it so now let's actually create the function so taking those attributes and then we'll start with an empty user will create a change set with the attributes that we have and then we'll call repo insert which should insert it into the database and return one of these two types that we've put here and so now we have a function that should be creating a user in the database so let's actually hook it up to our live view so we saw this function definition here so we're going to go ahead and create this handle event function so let's say handle event and then we notice that it's looking it sends this create string which if you remember is what we specified in our template so how do match on that and then it sends this map of data that has a user key that has our user data so we want to grab that so let's pattern match on that and pull out the the user attributes and then our third you see it has the arity of three and our third argument is this socket so let's get that and so we're gonna want to do is we're going to want to call that create user function so that's in our context will say users create user I'll pass in those attributes that we got and then we're gonna want to match on those so if we get an okay user Tuffle and response that means things worked out properly so we'll take our socket and we'll go ahead and we'll put flash on it which is a nice little success message there shows at the top I'll show you that in just a second so that'll be of the info type and we'll just say user successfully created okay and then we're going to redirect and this will tell our our live view to to redirect to a different page and send it to routes live path socket and then the current module so basically this tells it to put this flash on there and then redirect to the same page and so with that in place we'll return a tuple with a no reply atom and then the socket now if things don't work out we'll have an error temple with the change set in it and so for that case let's return and so will say no reply and then we'll assign to our socket the new change said and so the new change that will actually have the errors on it and so we'll be able to kind of display the errors to the user so let's change up our template so that we can display those so if we go back to our template then we can add error tags so underneath the name field we will go ahead and say error tag F and then name and so with that we'll be displaying that error so now if we refresh the page we should be able to submit that form so you'll see if I try and create one with a blank name that validate required is going to fail and so it's going to put that error there but if I do one with a good name oh it still fails why is that function routes dot life path is undefined oh I forgot to alias routes so in here let's say alias user admin webbed routered Coker's as routes and with that in place just use a different one and create and it still fails why is that Oh leave us because I forgot to recompile let's try just restarting the server and then refresh the page try it again so if we just say Dennis create and then we get that that flash that says user successfully created so that flash data only shows up once so if we refresh then it goes away and we won't see it again so it's kind of a cool little nicety to have so with that we have successfully built a user creation form so if you look at the running application you'll see that we're doing an insert into the database right here but you'll also see that we're only inserting the name and not the selected role so let's change that so that we're working with the roles to our roles probably won't change very often so we're just gonna put them directly into our database so we're gonna do that using our seeds file and this can be really nice for creating development data so that you have kind of some dummy data to work with when you're developing your app and then so that you don't have to have your whole production database on your local machine and that kind of gets rid of some security concerns as well but in this case we're just gonna use it for for inserting our roles into the database so we'll start by a aliasing our repo this just makes it a little easier to work with and then we're gonna a Lea's our user schema as well so the users dot or sorry role schema so let's create a quick list of roles so we're gonna have an administrator a moderator an editor a user and a guest I think that might be that's one more than then we created in our template so we'll have to update that Osei eat'em done each so we're gonna loop over these and we'll say take each of the roles and with that rule we'll insert into our database a roll with that name and so that we were not reading repo dot insert five different times we're just letting the code do it for us and so let's go ahead and run the seeds so get out of there and we're on mix run Prive repo CD xs and when you do that you'll see all these insert statements and so we can see that all of our roles got inserted so let's start running that server again so now your database is gonna have five different roles feel free to add more if you bike like I said that's one more role than we put in each team on the template so we're going to actually change our template so that it's grabbing all the roles from the database instead of just relying on hard-coded ones so let's make a function in the users context that will get all the roles so doc and this is going to list all the roles in the database whoops wrong button and so type spec we'll just call this list roles and it's just going to return a list of roles and so we'll say list roles and all we have to do here is we'll say repo all role okay and so now let's set up the light view to use this function so when we're mounting the light view we're gonna grab our change set and then we'll grab a list of roles as well and then we need to assign that to our socket and then we should have that so now we're going to need to actually change our template so if we go into our template we're gonna make a few changes here so let's change out our label it's just say equals label f will be role but we're actually gonna say role ID when you're working with one-to-many relationship it's usually easiest to work directly with the ID field you can use other functions like there's put a soak or cast a soak for once many relationships where the the relationship the the one side of the relationship is in the database you typically will just work with the ID for that because it just makes things a lot simpler so our role ID and then we'll set it to be for the role field and then we're gonna want to generate this select so we have this big chunk of code and we're actually going to pare that down quite a bit so we'll just call it select for the form F and it'll be for the field role ID and then we'll say we want the ID on this to be role field but then we have to actually specify what our data is going to be and so Phoenix HTML helpers expect the the data for a select to be in either a keyword list or a list of of two item tuples that have kind of the name to be displayed and then the value for it and so what we're gonna do we can't just say roles like that we're gonna need to turn that into a tuples so let's call enum map to map over all the roles and then we'll convert them into a tuple that will have the name of the role as the the name to be displayed and then the idea of the role to be the value that gets set and so with that we should be we should be putting it into the proper format and so then let's just add a an error tag underneath in case somehow they manage to enter some bad data that doesn't make it through our change set function and with that we'll save it and okay acted up queryable not implemented for role given module does not exist so that would seem like I forgot to alias and I did let's change this to alias our role and our user and then we'll refresh this and then it works so you'll notice we had four roles and now we have five in there and those match the five that we created with our seats file so you can try saving another user but if you look at the database or if you look at the query you'll see that we're still not putting that role there so let's actually get them saving this should actually be the simplest step but we just need to change our change set function so right now we're just casting the name let's just change that so we're also casting the role ID and let's make that role ID required so that every user has to have a role and so let's make sure that this works so if I say then that's four and then I changed as four I just leave them as an administrator now when I go and look I can see okay insert a name and a role ID dennis four and one which is the ID for administrator so without were successfully saving both the user's name and their role and we're making use of a one-to-one relationship however right now there's really no way to see the users that we've created and so let's create a user list page where we can watch new users being created and display that role that was chosen for them and see how to how to display that from one to many relationship so in order to get our list of users we'll start by adding a new function in our context and so I'm going to create the doc for that and we'll just say I list all of the users in the database and then type spec for it we'll say list users and that's just going to return and this abusers then our function will be very similar to list roles say list users will call a repo all user and with that in place oh looks like I have a syntax error here what did I do wrong expected token and now let's see what the error looks like here okay and users line 38 it looks like oh right there we're just missing that closing parenthesis okay so let's start it up again and so in our live view well you know what we're actually going to be creating a new live view so in our user live folder let's create a new file and we'll just call this one index so if we say def module user admin web user live index and then we use unique style live view then alias user admin users will create our mount function that takes in a session and a socket okay so here's where we'll use it will say users sorry users equals users list users and then we'll say okay sign to our socket the key is set to users and then our render will take in the signs and then we'll just use an empty sigil again okay so we have our light view and so now let's hook that up in our router we'll just say up above say lot of users with nothing after it we'll go to user live index and so now if we go to that page just slash users with no new after it we see that that loads properly and so now let's go ahead and create a template so if we go into our templates directory for users let's create a new file index.html le e X so here we're going to want to have our list of users so look at a table and so that's gonna have two columns so I have a name and I'll have a role and so then keep in our body oops we're going to once you actually loop over the users in the list so we use a for comprehension to do that so free user in users let's go ahead and create a table row and that's gonna have a cell that will have the name and one that will have the role so we can't just use it out rule because the rule is is also a struct so we'll say user dot robot name to get the name of that role and with that end and then close out our table so save that so now let's actually hook it up to our live view where did that go there so we will alias in our user view and then we'll say user view render index.html pass any assigns okay so at this point if we go to our browser we should see a list of users except we're actually getting this error so this is happening because the role is not loaded that's because our list users function isn't pulling in the users roles all we have on the user's struct is that role ID and so we're going to need to actually pull in the roles so we'll want to do that using the repo dot preload so in our users context in our list users function let's actually change this out so start with the user and then before I'm sorry we'll do repo dot all but then we'll preload the role and so this will tell it okay grab all the users once you have all the users then get all the roles that are specified in the role IDs and join those onto the users so if you refresh all our users should have rolls on them except that if you remember I created some users before we had our requirement that all users have a role and so some of my users their role is set to nil so I could go in and fix that but instead I'm just going to say mix exited up reset which is going to wipe out my database rerun my migrations rerun my seeds and now if I run my server again I should just have an empty table which I do so now let's go to users such new and create a new user and now if I go back to my table you'll see I have a user and the role has joined on to it so pre-loading the role is going to work pretty well but currently it's going to hit the database once to get all the room all of the users and then again to get all the roles and so we can make that a little bit more performant with a join so let's go back into that code and instead of instead of calling reproduct reload here what we'll do is we're going to import exit query and this will let us create a slightly more complicated query so we'll pipe the user module in to join and we'll do an inner join we'll take the user that we already have and we'll join in we'll say our for role in the Association you for user and then role and so what this is saying is we want to actually perform this join using sequel joining the role on to the user so that we'll get both of those tables at the same time and then we want to preload we'll say we don't really care about the user for the pre but we want the role and then we'll just say stick the role from here into the role on the user struct so if we run that now we can do the same thing but if you look here rather than doing a select from users and then a select from roles we can instead just do a single select with an inner join and so doing it that way is typically just a little bit more performant but before you make that decision in in your app I would make sure you can time kind of the time that it takes to to pull both of those one of the time versus the time to do a join and in some cases it might not make sense to do a joint especially once you get several different tables into the equation so right now we're using Phoenix live view but we aren't really taking advantage of the real time capabilities of WebSockets and channels so let's use Phoenix pub/sub and let our user subscribe to all the changes that are made to users so when a user has the user list up they'll be able to see any new users that are added and it'll automatically update so because we use the Phoenix generator to setup our app initially it already set up user admin dot pubs up for us so we'll just go ahead and start by adding a subscribed function in our users context so if we go to our users context in the editor let's create a subscribed function so I'll start with the doc let's say I subscribe to updates to the user list and so the type spec on that I only know this because I already looked it up basically the this the function that we're going to call inside of here will return either okay or I'm an irritable so call subscribe and that's just going to call out to Phoenix pub/sub dot subscribe and then it's going to subscribe using the user admin pub sub server and then let's create a topic as a module attribute that we can use elsewhere so if we say topic and we'll just use the module name for a topic so then let's also create a broadcast change function and so the point of this one is going to be to actually tell the pub sub server hey we made it change so that it can notify anybody who's subscribed so for this one it's going to be a private function so we won't bother putting it a doc on it but still put our type spec so if we say a broadcast change and it'll take in a user topple or an okay tough fool with a user on it and then it'll take in a list of atoms and that's going to be the the name of the vet to send out and so let me make sure I have the correct number of parentheses there and then that's going to return ether okay or an error to pull with the term so our function will say broadcast change and then it'll take in okay with the result of the change and then the event name and then what we'll do with that is will say Phoenix pub some broadcast to use your admin dot pub/sub our server on the topic that we set up and then the event that we want to broadcast will be coming from this module it would be of the event that we pass in and then we'll also send the resulting change and so we're turning a tuple with okay and then the result which is gonna be kind of the same thing that we're already passing in so that we can stick this in a pipeline and so it looks like line 53 I'm missing a closing parenthesis hey you know thought I checked two right there it looks like save it now it works okay and then let's actually call that broadcast change from our create user function so if we say a broadcast change and then we'll say user was created okay so now we're allowing someone to subscribe to updates and then when I users created we're broadcasting out that a user was created so that anybody who's subscribed will get that event and can handle it so we need to tell our live view to actually listen to that so if we go in to our live view on Mount let's just say users dot subscribe and say hey if anything happens we want to get a message and then let's actually handle that message so we'll handle the callback handle info and it's going to be expecting a message from users and it will be an event on the user right now it'll only cover created well it will only send events for a user created but we're going to actually accept any events that come in so that we can handle edits and other things in the future we don't really care about the created user right now if you you can take it upon yourself to try to make it a little bit more performant by grabbing the user that's sent and appending it then to the list but for now we're just going to whatever that happens we'll just say users equals users dot list users so we'll grab all of our users from the database again and then we'll return a no reply temple where we'll assign to our socket a new user list and so with that in place we can actually set up a second window so we will have in one window the user list which I'm gonna refresh oh that's not quite working so no function Clause matching in Pheonix top pub/sub dot subscribe to so saying our topic is not binary okay so that's because in our users context I've just set it to be the module I just said it to inspect the module that will convert our atom module name into a string so if i refresh now that loads just fine so if I go to localhost 4,000 slash users slash meal this will let me create new users so let me just add my wife make her a moderator and so you'll see when I created that user she automatically appears on the left side so if I created a third one I'll add my brother and I'll make him an editor and again he just shows up automatically so there you have it I'm working with one too many associations in real time I'd recommend keeping this code on your machine because we're gonna continue building off of it in part 2 when we go over many to many relationships those ones are a little bit more difficult so I'd prefer that we just already have the boilerplate set up out of the way so that we can just focus on making mini too many relationships work if you'd like an exercise for the meantime I'd recommend making another light view to add new roles and then use user admin pub/sub to make the new roles show up automatically in that drop-down whenever they're created so that the user doesn't have to refresh the page to set a user to a new role as always I'd love to hear how this tutorial went for you did you get anywhere did you get stuck anywhere how did it go if you have any questions about what I said or you wish I'd make a video about a different topic let me know either on Twitter at DNS BTY or if you prefer leave a comment on this video I actually love that because it helps the video get seen by more people as well so either way is great and I'll try to get back to you as quickly as I can thanks for watching if you liked this I'm going to have a link to a tweet that I sent out about it please retweet that help other people to see it also like I said before please subscribe to the channel if you haven't already I hope that this is going to be helpful for you and if not there's probably gonna be more videos in the future that I hope will be even more helpful thanks again for watching have a great day
Info
Channel: Dennis Beatty
Views: 2,613
Rating: 5 out of 5
Keywords: elixir, phoenix, phoenix liveview, phoenix live view, liveview, live view, phoenix tutorial, liveview tutorial, live view tutorial, elixir tutorial, phoenix liveview tutorial, phoenix live view tutorial, programming tutorial, websocket, realtime tutorial
Id: JF733I6CQB4
Channel Id: undefined
Length: 58min 14sec (3494 seconds)
Published: Tue Aug 20 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.