Published: 2016-04-09
By: MJ Rossetti
This post is part of a series for Rails developers who want to get started with Node.js. After creating the application’s controllers, its time to create the views. After following along to the end of this post, the goal is to have a working navigable application.
Let’s add view partials to represent re-usable parts of the page.
touch app/views/_head.ejs
touch app/views/_header.ejs
touch app/views/_bootstrap_flash_messages.ejs
touch app/views/_footer.ejs
NOTE: Underscored file names denote view partials (incomplete pages).
Edit the files according to the following templates:
<!-- app/views/_head.ejs -->
<head>
<title><%= title %></title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<!-- app/views/_header.ejs -->
<header>
<!-- FLASH MESSAGES -->
<%- messages('_bootstrap_flash_messages') %>
<!-- SITE TITLE AND NAVIGATION -->
<h1><a href="/"><%= title %></a></h1>
<a type="button" class="btn btn-primary pull-right" href="/robots/new">
<span class="glyphicon glyphicon-plus"></span> new
</a>
</header>
<h2><%= page_title %></h2>
<!-- app/views/_bootstrap_flash_messages.ejs -->
<% if(messages){ %>
<% Object.keys(messages).forEach(function (type) { %>
<% messages[type].forEach(function (message) { %>
<div class="alert alert-<%= type %> alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<%= message %>
</div>
<% }) %>
<% }) %>
<% } %>
<!-- app/views/_footer.ejs -->
<hr style="margin-top:2em;">
<footer>
<p>
<a href="#">source</a>
</p>
</footer>
<script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
<script type="text/javascript">
console.log("bootstrap flash")
$().alert('close') // closes data-dismiss="alert" http://getbootstrap.com/javascript/#alerts-methods
</script>
In these view partials, we’re installing Twitter Bootstrap and designing our site’s navigation and flash messages.
Now let’s add more views to handle basic CRUD functionality for our robots app, including robot-specific views and partials.
mkdir -p app/views/robots/
touch app/views/robots/index.ejs
touch app/views/robots/show.ejs
touch app/views/robots/new.ejs
touch app/views/robots/edit.ejs
touch app/views/robots/_form.ejs
mkdir -p app/views/robots/table
touch app/views/robots/table/_row.ejs
touch app/views/robots/table/_header_row.ejs
Edit the files according to the following templates:
<!-- app/views/robots/index.ejs -->
<!DOCTYPE html>
<html>
<% include ../_head %>
<body>
<% include ../_header %>
<table class="table table-bordered table-hover table-responsive" style="width:100%">
<% include table/_header_row %>
<% robots.forEach(function(robot){ %>
<% include table/_row %>
<% }); %>
</table>
<% include ../_footer %>
</body>
</html>
<!-- app/views/robots/show.ejs -->
<!DOCTYPE html>
<html>
<% include ../_head %>
<body>
<% include ../_header %>
<table class="table table-bordered table-hover table-responsive" style="width:100%">
<% include table/_header_row %>
<% include table/_row %>
</table>
<% include ../_footer %>
</body>
</html>
<!-- app/views/robots/new.ejs -->
<!DOCTYPE html>
<html>
<% include ../_head %>
<body>
<% include ../_header %>
<% include _form %>
<% include ../_footer %>
</body>
</html>
<!-- app/views/robots/edit.ejs -->
<!DOCTYPE html>
<html>
<% include ../_head %>
<body>
<% include ../_header %>
<% include _form %>
<% include ../_footer %>
</body>
</html>
<!-- app/views/robots/_form.ejs -->
<% if(locals.robot){ %>
<% var robot_name = robot.name %>
<% var robot_description = robot.description %>
<% }; %>
<form class="form-horizontal" method="POST" action="<%= locals.form_action %>">
<div class="form-group">
<label for="robotName" class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="robotName" placeholder="My Robot" value="<%= robot_name %>">
</div>
</div>
<div class="form-group">
<label for="robotDescription" class="col-sm-2 control-label">Description</label>
<div class="col-sm-10">
<textarea class="form-control" rows="3" name="robotDescription" placeholder="All the things..."><%= robot_description %></textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
<!-- app/views/robots/table/_header_row.ejs -->
<tr>
<th>Id</th>
<th>Name</th>
<th>Description</th>
<th>Created At</th>
<th>Updated At</th>
<th> </th>
<th> </th>
</tr>
<!-- app/views/robots/table/_row.ejs -->
<% robot_path = '/robots/'+robot.id %>
<% edit_robot_path = '/robots/'+robot.id+'/edit' %>
<% destroy_robot_path = '/robots/'+robot.id+'/destroy' %>
<tr>
<td><%= robot.id %></td>
<td><a href="<%= robot_path %>"><%= robot.name %></a></td>
<td><%= robot.description %></td>
<td><%= moment(robot.created_at).tz(moment.tz.guess(robot.created_at)).format('YYYY-MM-DD [at] HH:mm:ss zz') %></td>
<td><%= moment(robot.updated_at).tz(moment.tz.guess(robot.updated_at)).format('YYYY-MM-DD [at] HH:mm:ss zz') %></td>
<td>
<form action="<%= edit_robot_path %>" method='GET'>
<button class='btn btn-warning' type='submit'>
<span class="glyphicon glyphicon-pencil"></span> edit
</button>
</form>
</td>
<td>
<form action="<%= destroy_robot_path %>" method='POST'>
<button class='btn btn-danger' type='submit'>
<span class="glyphicon glyphicon-trash"></span> delete
</button>
</form>
</td>
</tr>
NOTE: You’ll notice undesired repetition of code across
index.ejs
,show.ejs
,new.ejs
, andedit.ejs
. Theexpress-ejs-layouts
module enables us to use a common application layout, but it conflicts with our custom bootstrap-styled flash messages, so in preference for app functionality over code DRY-ness, we will stick with these views for now.
At this point, you should be able to click around the application without breaking anything, even though database functionality is still missing.
It’s time to enhance this application’s functionality by connecting a datastore. Choose your own adventure (6a or 6b):