Creating a simple YUI Application Framework and Node app

22. June 2012

 

In this tutorial we are now going to implement a web app end to end using YUI and hosted in Node.  It isn’t actually going to do anything much, but it will illustrate everything you need to create a client and server in JavaScript using YUI App and Node.  Over time we will add more bells and whistles, but there is actually everything you need here to create a full functioning web application.

 

After looking in to it a little closer, YUI is incredibly well documented, but actually implementing YUI App Framework in a real world environment there are gaps of information missing.  Generally all you are going to find is samples with a gigantic HTML files where all of the script is located in a single file, which obviously isn’t good form nor very maintainable.  Unfortunately, when you actually set about splitting your YUI App into separate files and templates, you are completely on your own!  In fact, this tutorial may in fact be the first one on the subject on the internet, I certainly couldn’t find one. 

 

That said, there are still some great resources I referred to over and over well figuring this stuff out.  First and foremost, was the App Framework link I posted earlier.  It effectively illustrates using models and views, just not how to organize them across files in a complex project.  The GitHub contributor app was another great sample, but it again, was effectively one giant HTML file.  Finally there is the photosnear.me application’s source code up on GitHub.  It is a full YUI App/NodeJS sample and is certainly worth reading, but as an example for people learning it is frankly a bit too clever.  Plus it renders templates out using Node, something I wanted to avoid.

 

Alright, lets do a quick overview of how program flow works, don’t worry, it’s actually a lot less complicated than it looks. It is initially over-engineered for what it ends up accomplishing.  However, in the end you basically have the bones of everything you need to create a larger more complex application and a code structure that will scale with your complexity.

 

image

This ( in the image to the left ) is the file hierarchy that we are about to create.  In our root directory are two files, server.js which is our primary NodeJS application, while index.html is the heart of our web application, and where the YUI object is created.

 

Additionally, in a folder named scripts we create a pair of directories models and views.  Models are essentially your applications data, while Views are used to display your data.  Finally within the views folder we have another folder templates which is where our handlebar templates reside.  If you have done any PHP or ASP coding, templates are probably familiar to you already.  Essentially they are used to dynamically insert data into HTML, it will make more sense shortly.

 

We are going to implement one model, person.js, which stores simple information about a Person, and one view person.View.js which is responsible for displaying the Person’s information in the browser, and does so using the person.Template.

 

Now lets actually take a look at how it all works, in the order it is executed.

 

First we need our Node based server, that is going to serve the HTML to the browser ( and do much much more in the future ).  Create a new file named server.js

var express = require('express'), server = express.createServer(); server.use('/scripts', express.static(__dirname + '/scripts')); server.get('/', function (req, res) { res.sendfile('index.html'); }); server.get('*', function (req, res) { res.redirect('/#' + req.url, 302); }); server.listen(process.env.PORT || 3000);

 

Essentially we are creating an express powered server.  The server.use() call enables our server to serve static ( non-dynamic ) files that are located in the /scripts folder and below.  This is where we serve all of our javascript and template files from, if we didn’t add this call, we would either need to manually map each file or you will get a 404 when you attempt to access one of these files on server.  Next set our server up to handle two particular requests.  If you request the root of the website ( / ), we return our index.html file, otherwise we redirect back all other requests back to the root with the url appended after a hash tag.  For more details, read this, although truth is we wont really make much use of it.  Finally we start our server to listen on port 3000 ( or process.env.PORT if hosted ). Amazingly enough, these 9 lines of code provide a full functioning if somewhat basic web server.  At this point, you can open a browser and browse to http://localhost:3000, well, that is, once you start your server.

 

Starting the server is as simple as running node server.js from your command line.  This assumes you have installed NodeJS and added it’s directory to your PATH environment variable, something I highly recommend you do.  Now that we have our working server, lets go about creating our root webpage index.html.

<!DOCTYPE html> <html> <head> <title>GameFromScratch example YUI Framework/NodeJS application</title> </head> <body> <script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script> <script src="/scripts/models/person.js"></script> <script src="/scripts/views/person.View.js"></script> <script> YUI().use('app','personModel','personView', function (Y) { var app = new Y.App({ views: { personView: {type: 'PersonView'} } }); app.route('/', function () { var person = new Y.Person(); this.showView('personView',{model:person}); }); app.render().dispatch(); }); </script> </body> </html>

 

The most important line here is the yui seed call, where we pulling in the yui-min.js, at this point we have access to the YUI libraries.  Next we link in our model and view, we will see shortly.  Ideally you would move these to a separate config file at some point in the future as you add more and more scripts.  These three lines cause all of our javascripts to be included in the project.

 

The YUI().use call is the unique way YUI works, you pass in what parts of YUI library you want to access, and it creates an object with *JUST* that stuff included, in the for of the Y object.  In this case, we want the YUI App class ( and only it! ) from the YUI framework, as well as our two classes personModel and personView, which we will see in a bit more detail shortly.  If you use additional YUI functionality, you need to add them in the use() call.

 

We create our app and configure it to have a single view named personView of type PersonView.  Then we set up our first ( and only route ), for dealing with the URL /.  As you add more functionality you will add more routes.  In the event a user requests the web root, we create a person model.  Next we show the personView and pass it the person model we just created.  This is how you connect data and views together using the YUI app framework.  We then render our app and call dispatch(), which causes our app url to be routed ( which ultimately causes our person model and view to be created. If you aren’t used to Javascript and are used to programming languages that run top down, this might seem a bit alien to you at first.  Don’t worry, you get used to it eventually… mostly).

 

Now lets take a look at our model person.js

YUI.add('personModel',function(Y){ Y.Person = Y.Base.create('person', Y.Model, [],{ getName:function(){ return this.get('name'); } },{ ATTRS:{ name: { value: 'Mike' }, height: { value: 6 }, age: { value:35 } } } ); }, '0.0.1', { requires: ['model']});

 

Remember in index.html in the YUI.use() call where we specified personModel and personView, this is how we made those classes accessible.  By calling YUI.add() we add our class into the YUI namespace, so you can use YUI.use() to included them when needed, like we did in index.html.

 

Next we create our new class, by deriving from Y.Model using Y.Base.create(),  you can find more details here.  We declare a single function getName(), then a series of three attributes, name, height and age.  We set our version level to ‘0.0.1’ chosen completely at random.  When inside a YUI.add() call, we specify our YUI libraries as a array named requires instead of in the YUI.use call.  Otherwise, it works the same as a .use() call, creating a customized Y object consisting of just the classes you need.

 

Now lets take a look at the view, person.View.js

 

YUI.add('personView',function(Y){ Y.PersonView = Y.Base.create('personView', Y.View, [], { initializer:function(){ var that=this, request = Y.io('/scripts/views/templates/person.Template',{ on:{ complete:function(id,response){ var template = Y.Handlebars.compile(response.responseText); that.get('container').setHTML(template(that.get('model').getAttrs(['name','age','height']))); } } }); }, render:function(){ return this; } }); }, '0.0.1', { requires: ['view','io-base','personModel','handlebars']});

 

Like person.js, we use YUI.add() to add personView to YUI for availability elsewhere.  Again we used Y.Base.create(), this time to extend a Y.View.  The rest that follows is all pretty horrifically hacky, but sadly I couldn’t find a better way to do things that way I want. The first horrible hack is that:this, which is simply taking a copy of PersonView’s this pointer, as later during the callback, this will actually represent something completely different.  The next hack was dealing with including Handlebar templates, something no sites that I could findon the web illustrate, because they are using a single HTML file (which makes the task of including a template trivial).

 

The problem is, I wanted to load in a Handlebars template( we will see it in a moment ) in the client  and there are a few existing options, none of which I wanted to deal with.  One option is to create your template programmatically using JavaScript, which seemed even more hacky ( and IMHO, beats the entire point of templating in the first place! ).  You can also precompile your templates, which I will probably do later, but during development this just seemed like an annoyance. The photosnear.me site includes them on the server side using Node, something I wanted to avoid ( it’s a more complex process over all, and doesn’t lend itself well to a tutorial ).  So in the end, I loaded them using Y.io. Y.io allows you to make asynchronous networking requests, which we use to read in our template file person.Template.   Y.io provides a series of callbacks, of which we implement the complete function, read the results as our template, “compile” it using Y.Handlebars, we then “run” the template using template(), passing it the data it will populate itself with.  In our case, our name, age and height attributes from our personModel.  template() after executing contains our fully populated html, which we set to our views container using the setHTML() method.

 

Finally, lets take a look at person.Template, our simple Handlebars template:

<div align=right> <img src="http://www.gamefromscratch.com/image.axd?picture=HTML-5-RPG_thumb_1.png" alt="GameFromScratch HTML5 RPG logo" /> </div> <p><hr /></p> <div> <h2>About {{name}}:</h2> <ul> <li>{{name}} is {{height}} feet tall and {{age}} years of age.</li> </ul> </div>

 

As you can see, Handlebar templates are pretty much just straight HTML files, with small variations to support templating.  As you can see, the values {{name}}, {{height}} and {{age}} are the values that are populated with data.  They will look at the data passed in during the template() call and attempt to find matching values.  This is a very basic example of what Handlebars can do, you can find more details here.

 

Now, if you haven’t done so, run your server using the command node server.js, if you have set node in your PATH.

 

Then, open a web browser and navigate to http://localhost:3000, and if all went well you should see:

 

image

 

 

Granted, not very exciting application, but what you are seeing here is a fully working client/server application with a model, view and templating .  There is one thing that I should point out at this point… in the traditional sense, this isn’t really an MVC application, there is no C(ontroller), or to a certain extent, you could look at the template as the view, and the view as the controller!  But don’t do that, it’s really quite confusing! Smile  Just know, we have accomplished the same goals, our data layer is reusable and testable, our view is disconnected from the logic and data.  Don’t worry, the benefits of all of this work will become clear as we progress, and certainly, once we start adding more complexity.

 

In the near future, we will turn it into a bit more of an application.

 

 

You can download the project source code here.

Programming , , , , ,







blog comments powered by Disqus