#How does KeystoneJS render it's Admin UI
KeystoneJS is an open source framework for developing database-driven websites, applications and APIs in Node.js. It's built on Express and MongoDB.The easiest way to get started with KeystoneJS is to use Yeoman Generator.yo keystone
will scaffold a new KeystoneJS project for you, and offer to set up blog, gallery, and enquiry (contact form) models + views.If you'd like to try the demo at first,here it is.
When I wrote this,keystone's stable version is 0.2.39,and 0.3.0 not published yet,so if you have a different version of KeystoneJS,the content maybe different.
##Generate a KeystoneJS Project
First up, you'll need Node.js >= 0.10.x and MongoDB >= 2.4.x installed. Then, install the Keystone generator:
$ npm install -g generator-keystone
With the generator installed, create an empty directory for your new KeystoneJS Project,I will call it nodecoffee, and run yo keystone in it:
$ mkdir nodecoffee
$ cd nodecoffee
$ yo keystone
The generator will ask you a few questions about which features to include, then prompt you for Cloudinary and Mandrill account details.
These accounts are optional, but Cloudinary is used to host the images for the blog and gallery templates. You can get a free account for each at:
- Cloudinary - Image serving and management in the cloud
- Mandrill - Transactional email service by Mailchimp
When you've got your new project, check out the KeystoneJS Documentation to learn more about how to get started with KeystoneJS.
##Mysterious Admin UI
Besides the project's code yo generated for you as the following structure,Keystone gives you a beautiful, customisable Admin UI based on your models.To sign in to Keystone's Admin UI, run node web
start your application and go to localhost:3000/keystone. Use the email and password you put in the update script, and you'll be redirected to Keystone's home page.
List 1: project structure
|--lib
| Custom libraries and other code
|--models
| Your application's database models
|--public
| Static files (css, js, images, etc.) that are publicly available
|--routes
| |--api
| | Your application's api controllers
| |--views
| | Your application's view controllers
| |--index.js
| | Initialises your application's routes and views
| |--middleware.js
| | Custom middleware for your routes
|--templates
| |--includes
| | Common .jade includes go in here
| |--layouts
| | Base .jade layouts go in here
| |--mixins
| | Common .jade mixins go in here
| |--views
| | Your application's view templates
|--updates
| Data population and migration scripts
|--package.json
| Project configuration for npm
|--web.js
| Main script that starts your application
Click Posts in the left Nav panel of home page,you will be redirected to the Posts Page.It's just like any list views you may see lots of times,the presentation maybe different,but if you draw boxes around every component (and subcomponent) in the page,and you will find they are all familiar components,such as Create button,Search box,Items table etc.There is also a Post Item page,when you click link on post title in list table,you will be redirected to it.
But you cann't find anything about Admin UI in generated code,no templates,no routes,only models.So where is it come from?What happens when you access keystone's Admin UI?
##The common routes&templates of Admin UI
KeystoneJS is built on Express,so it's easy to find out how does KeystoneJS render the Admin UI from it's source code if you are familiar with Express(thanks all of the comment to the code).You can check out it's source code from KeystoneJS's Github repository:
git checkout https://github.com/keystonejs/keystone.git
First,there is a /lib/core/routes.js file,it has a routes(app)
function called by function mount
in /lib/core/mount.js file,adds bindings for the keystone routes,for example:
// List and Item Details Admin Routes
app.all('/keystone/:list/:page([0-9]{1,5})?', initList(true), require('../../routes/views/list'));
app.all('/keystone/:list/:item', initList(true), require('../../routes/views/item'));
The middleware initList(protected)
in the route binding defined in this file too,it defines the req.list
based on :list
param in URL.In KeystoneJS, your data schema and models are controlled by Lists, and documents in your database are often called Items.To query data, you can use any of the mongoose query methods on the list.model
.For example: to load the last 5 posts with the state published, populating the linked author, sorted by reverse published date:
List 2: Loading Posts
var keystone = require('keystone'),
Post = keystone.list('Post');
Post.model.find()
.where('state', 'published')
.populate('author')
.sort('-publishedAt')
.limit(5)
.exec(function(err, posts) {
// do something with posts
});
So before the handler process the request,KeystoneJS get the registed List
instance based on the param of list's key in URL with list
function,and put it into the request object for query data.And the binding lead us to the handler,or more specifically,Express middleware in file /routes/views/list.js,every list (and item) pages in Admin UI have same components but based on different models,so this is why KeystoneJS use one handler renders different views。The handler populate the pagination query of List
based on the sort,search filters from req.query
,execute it and pass the results to keystone.render
in the callback,function render
defined in file /lib/core/render.js,from the code and passed in arguments we can see the list template file is /templates/views/list.jade.
This lead us to another important component of KeystoneJS--Keystone Fields,and a Javascript library for building user interface--React.
Keystone Fields allow you to easily add rich, functional fields to your application's models. They are designed to describe not just the structure of your data, but also the intention of your data.In the /index.js file:
keystone.Field = require('./lib/field');
keystone.Field.Types = require('./lib/fieldTypes');
Field
constructor defined in file /lib/field.js,all FieldTypes defined in directory /lib/fieldTypes inherit Field
,and every FieldType has correspond React component files in /fields/types/。And then in /admin/src/ directory,there is a fields.js require
all those fieldTypes React components and exposes them.And an app.js file exposes the Form
components for Keystone Admin UI,and render it in /templates/views/item.jade:
List 3: Render form(from /templates/views/item.jade)
script.
Keystone.list = !{JSON.stringify(list.getOptions())};
Keystone.item = !{JSON.stringify(list.getData(item))};
Keystone.wysiwyg = { options: !{JSON.stringify(wysiwygOptions)} };
App.Views.Item.renderForm(document.getElementById('item-form'), Keystone.list, Keystone.item);
But this app.js is not directly used,in the /templates/layout/base.jade:
script(src="/keystone/build/js/app.js")
The /keystone/build/js/app.js in is built from /admin/src/app.js by gulp,see gulpfile.js.
##Conclusion
So as the conclusion,KeystoneJS Admin UI's routes binding in the /lib/core/routes.js file,request handled by /routes/views/list.js and /routes/views/item.js.And then template files /templates/views/list.jade and /templates/views/item.jade render the list and item based on column's filedType.