Editor’s note: We found an interesting series of articlesLearn 30 New Technologies in 30 Days, is being translated, an update a day, year-end gift package. Here’s day one.


So far in this series, we’ve discussed JavaScript technologies such as Bower, AngularJS, GruntJS, PhoneGap, Meteor, Ember, and TimelineJS. In today’s Learn 30 New Technologies in 30 Days, I decided to learn about Yeoman, a productivity tool for front-end development. This article starts with the basics of Yeoman, and then we’ll develop an Ember application using Yeoman. This article will not cover the basics of EmberJS, so refer to Day 19.

What is Yeoman?

Officially, Yeoman is not just a tool, but a workflow. It consists of three parts, YO, Grunt and Bower, which are used for project start-up, file operation and package management.

  1. Yo: Yo is a project initializer that generates a set of project files necessary to start a certain type of project.

  2. GruntJS: GruntJS is a Javascript based command-line build tool that helps developers automate repetitive tasks. Read Day 5 for more information.

  3. Bower: Bower is a package manager for client-side technology that can be used to search, install, and uninstall web resources such as JavaScript, HTML, CSS, and so on. Read Day 1 for more information.

Why do I care about Yeoman

If you need to convince yourself to learn Yeoman, you can read WhyYeoman on the Yeoman website.

The installation

Install with NPM:

npm install -g yeoman

Install the Yeoman Ember generator

Yeoman relies on generators to provide scaffolding for Web projects. All modern JavaScript MV* frameworks have generators. This article uses Ember:

npm install -g generator-ember

We will develop a social bookmarking application that allows users to submit and share links. You can check out the app here. This is the same app we developed on Day 19.

Making the warehouse

Today’s sample application code is available on GitHub.

Create an Ember application

Now let’s get started with application development. Start by creating the project directory, then run the Yo Ember command, which will ask you if you want to use Twitter Bootstrap. I used to use Bootstrap on my own projects, so I chose Yes.

; mkdir getbookmarks ; cd getbookmarks ; yo ember _-----_ | | |--(o)--| .--------------------------. --------- | Welcome to Yeoman, | ( __ ) | ladies and gentlemen! | /___A___\ '__________________________' | ~ | __'.___.'__ [?]  Would you like to include Twitter Bootstrap for Sass? Yes

Yeoman then creates an Ember application that automatically installs dependencies using bower and NPM.

Now let’s look at the Ember application generated by Yeoman. Apps have three top-level directories: app, node_modules, and test. There are also configuration files:.bowerrc,.gitignore,.jshintrc, gruntfile.js, and package.json. The directory structure is shown below.

All application related code is in the app directory. The directory structure follows the best practices of EMBER.

  1. The bower_components directory includes client dependencies such as Ember, Twitter Bootstrap, and so on. Bower installs all dependencies in all folders.

  2. The image directory contains the application-related images. Yeoman will optimize the images in the image directory.

  3. Index.html includes all Ember.js dependencies, all bootstrap dependencies, and build annotations (which gruntfile.js can use to replace or remove referenced non-optimized scripts and stylesheets).

  4. The scripts directory contains the controller, view, model, and route for the Ember application.

  5. The styles directory contains the CSS files associated with the application.

  6. The templates directory contains the application’s handlebar templates.

Now we can start the preview server:

grunt server

This opens the application using the default browser:

Generate the Story Model

We created an Ember Model: Story in the GetBookmarks application we developed in Day19. Yeoman’s secondary generators can be used to build parts of the project, including the Model. Run the following command to generate the Story Model:

yo ember:model Story

The output of the command is as follows:

create app/scripts/models/story_model.js invoke ember:controller:/usr/local/lib/node_modules/generator-ember/model/index.js create app/scripts/controllers/stories_controller.js create app/scripts/controllers/story_edit_controller.js create app/scripts/routes/stories_route.js create app/scripts/routes/story_route.js create app/scripts/routes/story_edit_route.js invoke ember:view:/usr/local/lib/node_modules/generator-ember/controller/index.js  create app/scripts/views/story_view.js create app/scripts/views/story_edit_view.js create app/scripts/views/stories_view.js create app/templates/story.hbs create app/templates/story_edit.hbs create app/templates/stories.hbs create app/scripts/views/bound_text_field_view.js invoke ember:router:/usr/local/lib/node_modules/generator-ember/controller/index.js conflict app/scripts/router.js [?]  Overwrite app/scripts/router.js? overwrite force app/scripts/router.js

This will generate story_model.js in the app/scripts/models directory, along with the corresponding view, controller, and route.

Let’s change story_model:

Emberapp.Story = DS.Model.extend({
  url : DS.attr('string'),
    tags : DS.attr('string'),
    fullname : DS.attr('string'),
    title : DS.attr('string'),
    excerpt : DS.attr('string'),
    submittedOn : DS.attr('date')
});

Restart Grunt Server for the changes to take effect.

Install the Ember LocalStorage Adapter

We will use HTML 5 localStorage to store the data. Install the adapter using Bower.

bower install --save ember-localstorage-adapter

Then update the index. HTML page to add a dependency:

<script src="bower_components/ember-localstorage-adapter/localstorage_adapter.js"></script>

Also update app/scripts/store.js and configure the application to use LSAdapter:

Getbookmarks.Store = DS.Store.extend();
Getbookmarks.ApplicationAdapter = DS.LSAdapter.extend({
  namespace: 'stories'
});

Update the routing

Modify the router. Js:

Getbookmarks.Router.map(function () {

  this.resource('index',{path : '/'});
  this.resource('story', { path: '/story/:story_id' });
  this.resource('story_edit', { path: '/story/new' });


});

Submit a new report

Let’s start by adding the form that will appear when the user accesses #/story/new. Modify the app/templates/story_edit. HBS:

<form class="form-horizontal" role="form"> <div class="form-group"> <label for="title" class="col-sm-2 control-label">Title</label> <div class="col-sm-10"> <input type="title" class="form-control" id="title" name="title" placeholder="Title of the link" required> </div> </div> <div class="form-group"> <label for="excerpt" class="col-sm-2 control-label">Excerpt</label> <div class="col-sm-10"> <textarea class="form-control" id="excerpt" name="excerpt" placeholder="Short description of the link" required></textarea> </div> </div> <div class="form-group"> <label for="url"  class="col-sm-2 control-label">Url</label> <div class="col-sm-10"> <input type="url" class="form-control" id="url" name="url" placeholder="Url of the link" required> </div> </div> <div class="form-group"> <label for="tags" class="col-sm-2 control-label">Tags</label> <div class="col-sm-10"> <textarea id="tags" class="form-control" name="tags"  placeholder="Comma seperated list of tags" rows="3" required></textarea> </div> </div> <div class="form-group"> <label for="fullname" class="col-sm-2 control-label">Full Name</label> <div class="col-sm-10"> <input type="text" class="form-control" id="fullname" name="fullname" placeholder="Enter your Full Name like Shekhar Gulati" required> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-success" {{action 'save'}}>Submit Story</button> </div> </div> </form>

Now, visit http://localhost:9000/#/story/new to see the form to submit the story.

Update StoryEditController to keep the data in local storage:

Getbookmarks.StoryEditController = Ember.ObjectController.extend({ save: function(){ var url = $('#url').val(); var tags = $('#tags').val(); var fullname = $('#fullname').val(); var title = $('#title').val(); var excerpt = $('#excerpt').val(); var submittedOn = new Date(); var store = this.get('store'); console.log('Store .. '+store); var story = store.createRecord('story',{ url : url, tags : tags, fullname : fullname, title : title, excerpt : excerpt, submittedOn : submittedOn }); story.save(); this.transitionToRoute('index'); }});

List all the stories

The next feature we will implement is to display a list of stories in the sidebar.

In application_route.js, we get all the stories from the local store.

Getbookmarks.ApplicationRoute = Ember.Route.extend({ model : function(){ var stories = this.get('store').findAll('story'); return stories; }});

Then we update application.hbs to add links to the title of each story:

<div> <nav class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">GetBookmarks</a> </div> <div class="collapse navbar-collapse navbar-ex1-collapse"> <ul class="nav navbar-nav pull-right"> <li>{{#link-to 'story_edit'}}<span class="glyphicon glyphicon-plus"></span> Submit Story{{/link-to}}</li> </ul> </div> </nav> <div class="container" id="main"> <div class="row"> <div> <div class="col-md-3"> <div class="well sidebar-nav"> <table class='table'> <thead> <tr><th>Recent Stories</th></tr> </thead> {{#each controller}} <tr><td> {{#link-to 'story' this}} {{title}} {{/link-to}}  </td></tr> {{/each}} </table> </div> </div> <div class="col-md-9"> {{outlet}} </div> </div> </div> </div> </div>

The application’s user interface refreshes.

Check out individual reports

Finally, the ability to view individual stories when you visit http://localhost:9000/#/story/:id will be added. :id corresponds to Story ID. Modify story_routejs.

Getbookmarks.StoryRoute = Ember.Route.extend({ model : function(params){ var store = this.get('store'); return store.find('story',params.story_id); }});

Modify the app/templates/story. HBS:

<h1>{{title}}</h1>
<h2> by {{fullname}} <small class="muted">{{submittedOn}}</small></h2>
{{#each tagnames}}
  <span class="label label-primary">{{this}}</span>
{{/each}}
<hr>
<p class="lead">
      {{excerpt}}
</p>

Build for the production environment

Finally, we run the Grunt Build command to create a distributable application. The grunt build command converts the source files in the app directory into distributable applications in the dist directory.

grunt build

That’s all for today. Keep feedback.


Day 24: Yeoman Ember–The Missing Tutorial