Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
  • Overview
  • Transcript

2.2 Full-Stack Reactivity With Milo

In this lesson I’ll show you how you can use Milojs to create a full-stack reactive connection. You’ll see how data flows from the client model to the server, into the database, and back out to other clients. Follow along as I work through a fully functional Slack-like chat application which uses Milojs and manages to get away with very little server code.

Related Links

2.2 Full-Stack Reactivity With Milo

In this video I'm going to be talking about creating a full stack reactive connection from the client model to the server, into the database and back out to other clients. To do this will be using Milojs, let's take a quick look at the architecture that we're using. In the client there is a model that manages the state of our slack-clone application. It looks like this with channels, property of the model, which has the list of channels. And the messages property which has the various messages in each of those channels. When a change happens on the client messages will go from our client model through web sockets to a server model which is also a Milo model. Those changes will then be propagated to the DB. Changes in the DB will be detected. In the server, and get broadcast back out to the clients, also using web sockets. So let's get started. If you get a chance to check out the other videos on this subject, you'll see just how powerful Milo Models are. Unlike something like Angular, which takes a polling approach to model changes, Milojs uses a push technique. Milo Models are very chatty and will admit messages about all the changes at all levels. If open up this models tutorial for example and we go into the console we can see this is the data contained within our user object if I get hold of the friends array and push a new friend. You can see the various changes firing that we've got hooked up to the user dot friends property. So this is the change for the base friend array and here's a change for the particular index a new item that was added. Change messages come with lots of information about what type of operation it was and the old and new data that this change represents. After using Milojs for a while we considered how easy it would be to pass these messages back and forth across the server in order to create a full stack two way data binding. So as a proof of concept we created the slack clone. You can check out the slack-clone for yourself by going to GitHub or by running this line in the command line. Git clone and then this slack-clone git address. So let's take a look at the code. We'll ignore for a second the service side component and just look at the client side code. We have a basic HTML file which has a couple of custom Milo components, such as the channels pane where the channels are loaded in, and then some other components which are out of the box Milo components with facets attached. So this channels list is going to be a list of channels. And inside each channel item we're going to have the idea of the channel as a dumb data component and the title as well. Here is a button to create a new channel. This has an events facet so that we can easily subscribe to the click event. We have user handle where the user enters their name. That's a custom component as well. And then we have the actual channel of data with it's metadata and a list of message items as well. So the reason that some of these are custom components and some are not is basically because the custom components will need some kind of custom code. So let's take a look at some of these custom components. So if we ignore the webs socket code that's passing the messages back and forth, you'll see that the only client side code in the index file is calling Milo Binder. Milo Binder will iterate over all the elements in this HTML instantiating components to manage the various elements to which they're attached. So, let's start off by taking a look at the channel's pane. It's this component here and as you can see it creates a list of the channels and it has a create button that opens up a small form to create new channels. The channels pane is a standard Milo component with a container facet which means it will contain child components and it wants to be able to access them inside its scope. And it subscribes to the children bound method which is fired on all components when when all child components inside that component have all been finished binding. So on children bound, we're gonna get access to our channels list and we're going to bind that channel's list data facet with the DB channels property. Now DB for the front end, it's just a Milo model. But it basically has this data loaded into it. We have a channels property and a messages property. Channels contains information about the channels. We can see it's already been updated without JavaScript channel that we just added. Every messages just has all the messages obviously linked by channel ID. As some of you may notice is that we essentially have one big model managing the entire application state that's facilitated by Milojs' model pods as you can see, we get access to them, hold DB there, but our ChannelsPane is only interested in the channels model path, of the overall model. It then links that in a two way binding, with the DOM. We're also listening to click events onCreate a channel button and that's going to call a function called createChannel which as you can see essentially creates a brand new dialog and contains the rest of the form data and allows us to make new channels. I won't go into this in detail but you can see that we're generating the form for that dialog using a form schema. This is a functionality that's part of Milo UI which is a separate library, which facilitates generating UI using Milo. The form schema itself contains a list of items, and each item has a type, a label, and a model path. This model path is very important, because it allows us to specify a specific part of the form model, or view model to this input component should attach to. It means that later on, if we move this element around and even nest it more deeply into the DOM, it won't change a model because this will still point to the .title property of the model. We also have a bit of validation here and here we have a combo list which is loading in a list of different tags that we can use to tag our channel. Now, what does it mean by the fact that this is a two way data connection to the model? What it essentially means is that any changes that happen in the DOM will also be picked up by the mind of connection and will be propagated to the model. The easiest way to show that connection is if I change the span and make it content editable. The span is now content editable. If I change this here, make it Welcooooooome, you consider that change has propagated into the Milo model which have been picked up by a website connection. Which has gone over the server into the DB and back out to this other client. That's because Milo knows how to deal with DOM elements and it knows how to pick up change messages in those elements. I can even go look at the DB in the server now and you'll see that Welcome has been changed to Welcooooome as well. So before we take a look at the server. I just want to show you one more interesting thing about Milo and that is the CSS facet. As I've mentioned before, the Milo minder will create deep data connections between anything that is considered to be a data source in Milo. Now, the data facet which controls DOM data and access to the DOM data. That's a data source. A Milo model or model path is also a data source. So we can connect those together. But we also went ahead and made the CSS facet. And we made that facet follow the same API as any other data source. That means that our CSS can also be considered a data source. This means that we can have data driven CSS and our CSS facet allows us for a very declarative way of defining the CSS rules. So when userHandle property changes, it's gonna run through this isAuthor function. And if the userHandle is equal to the current users handle or name, then we're gonna have a specific CSS class set. We can see that already on these messages here. If it's a message typed by myself, you see it has slightly different style. It's got bold and a blue color for the message item. Whereas messages touched by other people do not match that rule. So they'll come without the current user class we can see there. This is very powerful way of managing your CSS styles and allows you to base the design of your application and your components on the state of the data of the app. Okay, so we've been playing with this for a while. Let's take a look at this live reactive data connection between server and client. So on the server you'll see that our database module is basically just creating a new Milo model around db.json. So, this is the data going into the model, and then it's listening out for change data, on that model. Change data is a higher level event, that's used by models, to pass their changes about to other data sources. It's not generally used by users. You as a user would usually subscribe to a particular property on the model. The changed data event, gets fired off for everything. We're then throttling this updateDB function, which basically just overwrites the DB JSON file with a new stringified version of the whole DB. Now this is obviously not in any way efficient or performant. But if you were writing this is a real application it wouldn't be particularly difficult to work out exactly what subset of the model had changed and to update just that part in the DB. And of course your DB wouldn't be a json file. But as an example, it's a nice simple way to get our point across. Every time that changes the actual Milo Model, that's going to update in the database. Then we have our main server file. Now most of this file is pretty straightforward. It's a simple express app and when you hit the root, it's going to send you the index file that's fine. What we're doing here though, we've required socket IO and on connection to socket IO, we're gonna emit the entire state of the DB. Once again, you could totally work this out so it's only admitting a part of the state, depending on how your application is structured but we're descending around the whole DB. And then we're subscribing to data changes on the socket. This is going to be a custom message that we're going to admit on the socket in the client. So when data changes happen, we're going to post a message to the DB with message data. We have to specify postMessageSync because all messages by default in Milo are asynchronous. This is for performance reasons in the client, and to prevent UI lock up when lots of messages are being sent. We will then go socket broadcast emit these data changes to all the other clients. This will make sure that we only emit those changes to all the other clients, rather than the one that actually sent us the message. In the client to complete the connection, all we need to do is listen for connection on socket. When we get that db message that was emitted on connection, we're going to set our DB to the initial data and then we're gonna subscribe to data changes. This subscription will be listening out for changes and we're gonna subscribe to data changes on our client model so that we can pass all those messages, all those change messages on. Through the sockets to the server whenever our local client database changes. And the socket data changes subscription is listening to those data changes coming from the server and this all we need to do is unsubscribe from data changes. Post a synchronous data change message to our local DB and then resubscribe after a defer. This is just to make sure that we don't re-admit those changes, and end up with an infinite circular loop. When we post those data changes to our local DB, because it's a milo.Model, it's going to admit those changes locally within our application. And the changes in the db will update the DOM as it goes through these two-way data connections set up using mylo.minder. Here's the one binding the list of channels to the channels list in the DOM. And here's another one binding the messages in the messages DB to the list of messages in the DOM as well. So there you can wee with very little code on both the server and the client were able to create a two way reactive data binding between server and client and despite it not even being that much code it's even simpler when you just think about it. We have to do this little dance here around un-subscribing and subscribing so that we don't get a socket connection but to put it into words all we're doing is passing Milojs model changes. Over WebSockets, between client and server and back out to other clients again updating the database along the way. Well that's all for this video. I hope of showing you how powerful Milo can be and in particular Milo models and Milo data connections using Minder. It's my intention to split apart core Milo components. So they can be used separately in applications without having to use the whole framework. If you check out the Milojs GitHub organisation, you should be able to find some of those components that have already been split out. And by the time you see this video, I should hopefully finished splitting out Milo messenger which is a very useful mix-in along with Milo models which could be used in any application as a powerful reactive JavaScript model. Well that's all for me, bye.

Back to the top