This is part 1 of a 2-parts post that will show how to build a real world application using CycleJS and SocketIO. I recently wrote the same application but with ReactJS and RxJS. If you have not watched the CycleJS Egghead course by André Staltz, then I highly recommend you to do so first. I am building this based on the lessons from the above Egghead course.
In this part 1, we will accomplish the followings:
- Incorporate MaterializeCSS. You might not be using this particular CSS framework, but once you see how it’s done with one, you can model the code to make it with others.
- From the get go, we will start planning to build the application like we do with ReactJS and with Lego toys, i.e. we make smaller components, then put them together to make bigger components and keep repeating that.
- We will make use of the Model View Intent pattern.
At the end of part 1, the application will look like
Nothing too exciting here yet. But like in a real chat application, there’s a text input where users can type. To send the text, users first type some text, and then can either click on the Send button or press the Enter/Return key. Regardless of either way, the text entry must then be blanked out while the focus remains on the text entry. This sounds like a simple and no brainer thing, but not doing so will fail the application.
Stand up the application
So first, let’s do a bunch of npm install commands. First the “compile-time” packages:
npm install webpack -g
npm install webpack --save-dev
npm install babel-core babel-loader babel-preset-es2015 babel-preset-stage-0 --save-dev
Then the “run-time” packages:
npm install rx @cycle/core @cycle/dom --save
Before going on, here is the structure of how I organize the files:
Inside the src folder is where we will be writing all the CycleJS files. The dist folder will contain the generated bundle.js file.
By the way, I hide the files that are specific to my environment (.gitignore, etc) by covering them in red.
Now we can write the index.html file which includes the necessary CSS files. It only contains a single div element called app.
Since we just installed babel and webpack as shown above, we will now be able to write CycleJS codes and import them to main.js, and when we run webpack command, everything will be bundled into the bundle.js which will be generated in the dist folder.
So, here’s the webpack.config.js file:
Static virtual DOM
It’s time now to write the main.js file. If we refer back to the screenshot above, we will structure the application UI as having 4 components. The first one is the appBar which is at the top. The second is the chatPane at the left and it contains the chat messages. The third is the presencePane at right pane and it shows active users in the chat room. The 4th and last is the textEntry area that contains a text input and a Send button.
In this part 1, we will leave out all the socketIO related code so the chatPane and presencePane will look empty. That’s OK because we will add them in part 2.
What I would do next is to generate the static virtual DOM tree. This static virtual DOM tree will have all the 4 components above, each in its own file as a building block.
The entry point is main.js and it’s location will be in the src directory. This is how it looks like:
As a side note, an alternative and shorter way to specify the class attribute in CycleJS hyperscript is
div(‘.col .s6’, [
div(‘.col .s6’, [
There is also another point to remember. The name of the function main() is arbitrary, but run comes from cycle core.
The source code for the view() function for appBar, chatPane, presencePane, and textEntry can be viewed here.
At this point, what we get when we run the application is the page that looks like the one the screenshot above, but it doesn’t do anything yet.
Now we’ll add the intents. The intents are the action that comes from either the Send button click event or the Enter key pressed event. So in the textEntry.js, we’ll add and export a couple of intent functions:
Then to consume it in main.js
In here, we also add the model() function, so model, view and intent are finally all present.
When we create the 2 intent function in textEntry.js, we want the future text stream coming from the text input where the users type. But instead of mapping to e.target.value like this
const textStream$ = DOMSource.select(‘#input-msg’).events(‘keyup’).map(e => e.target.value);
and like this
const textStream$ = DOMSource.select(‘#input-msg’).events(‘keyup’).filter(textStream => textStream.keyCode !== 13).map(e => e.target.value);
we just to keep it a little more generic, because later we will need the target to set the focus, i.e. keep the focus on the text input upon users clicking the send button.
The view so far does not depend on the model, hence the view() function does not take the model as an argument. This will change we incorporate the socketIO observables.
You also see a function called EffectHttp() here. This function handles the side effects such as subscribing to the text stream (typed by users in the text input) and take the text value and will make the HTTP post. This side effect handler is also responsible for blank out the text input element and set the focus on it.
The code for part 1 can be found here.
Here’s a demo on codepen (have to defeat the purpose of modules so everything fit in codepen, concept and most code stays the same)
Stay tuned for part 2.