Sometimes the easiest way to learn a new piece of technology is to start building. Our first example project will be a simple todo app like the ones on todomvc.com. Our app will have many simple features:
If you want, you can view this tutorial in video form and follow along.
To start, let's generate a new app:
gem install volt
volt new todo_example
cd todo_example
You'll notice that Volt created a todo_example
folder and filled it with the scaffolding for a new Volt project, along with other common things like a Gemfile and sensible .gitignore. Volt apps are built as nested components, and your app starts with a component called main
, which has a controller and some views.
To run the server:
bundle exec volt server
When you save changes to a file, volt will automatically reload the file and push the changes to anyone viewing your page. Let's create a new page while leaving the server running.
First, create a new file, app/main/views/main/todos.html
and give it some basic content:
<:Title>
Todos
<:Body>
<h1>Todo List</h1>
And then add a /todos
link to the navbar, which is rendered from app/main/views/main/main.html
:
...
<:Body>
<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<:nav href="/">Home</:nav>
<:nav href="/todos">Todos</:nav> <!-- New link -->
<:nav href="/about">About</:nav>
</ul>
...
And also add a route for todos in app/main/config/routes.rb
:
client '/about', action: 'about'
client '/todos', action: 'todos' # New route
...
Once all these changes are saved, you will be able to navigate to the page we created for the Todo List.
Next, we want to add a way for users to add a todo to the list with a form, so we'll start by adding to the body of todos.html
:
...
<:Body>
<h1>Todo List</h1>
<form e-submit="add_todo" role="form">
<div class="form-group">
<label>Todo</label>
<input class="form-control" type="text" value="{{ page._new_todo }}" />
</div>
</form>
Anything in {{ }}
is executed as Ruby code. Code in controllers and views is compiled to JavaScript (using Opal and runs in the browser. Above we are binding the value of the form to a member of the page
collection. In Volt, there are a number of different collections, page
is a temporary in memory collection, and will be lost if you navigate away or refresh. Any value that gets bound in the view will be automatically updated in all places they are shown. We'll take advantage of this by adding a method to app/main/controllers/main_controller.rb
:
...
def add_todo
page._todos << { name: page._new_todo }
page._new_todo = ''
end
...
This method will append a hash to page._todos
with the value of page._new_todo
and clear out page._new_todo
.
Note: Notice that in add_todo
we did not need to initialize an empty array into page._todos
. This is because Volt will automatically initialize pluralized attributes to an empty Volt::ArrayModel
. There is no need to initialize the attribute beforehand. Also, when we append a hash to a Volt::ArrayModel
it will automatically be converted to a Volt model.
To be able to see the page._todos
collection, we'll add a table to our page:
...
<:Body>
<h1>Todo List</h1>
<table class="todo-table">
{{ page._todos.each do |todo| }}
<tr>
<td>{{ todo._name }}</td>
</tr>
{{ end }}
</table>
...
Now, once everything is saved and reloads, any time you submit by hitting enter, it will add to the list and clear out the form. Volt is reactive and intelligent, so any time the list is updated, only the new elements will be drawn; it won't redraw the entire list.