How to build multiple web apps with Elixir thanks to umbrella — Bonus 1: Let’s use Bootstrap

A (wannabe) guide to learn how to create web apps with Elixir 1.9, Phoenix 1.4, Docker (for development), Git (including submodules) to version each app, PostgresQL, Redis, API calls and deployment.

Cédric Paumard
8 min readSep 6, 2019

--

This is the first bonus chapter. It takes place after the third chapter. It is addressed mainly to people who follow the main guide, even if they probably don’t need it. However, you can take it also as a standalone: any Phoenix project with a front will be enough to follow.

In this chapter, we will see how to use Bootstrap to make a better interface for an administration website. We will focus on an entity called AdminUser which as four fields and multiple template already created (but not styled). We also implemented a login form that we need to take care of, and the layout. But before all of that, let’s do some modifications in our assets folder.

Note: This is an extremely simple chapter. In the case you never did any CSS or HTML, you should read some articles first, so you know how it works. For Bootstrap, if you understand what a class attribute in HTML is, then there is nothing else you need to know. As this chapter is extremely simple, I would greatly suggest to try to do it by your own.

Optimizing the assets

Go to admin/assets/css/. Here, we will create two files: _variables.scss and _libs.scss. We won’t put any code in _variable today, but its purpose is to contain Sass variables such as your color themes, sizes, etc. On the other hand, _libs should look like this:

A lot of import lines, which allow us to customize the import from both Bootstrap and Fontawesome. You should comment any line you don’t use in purpose to make your CSS file lighter.

Because we put bootstrap in _libs.scss, we can delete _bootstrap.scss.

Having new files and removing one means we have to modify app.scss. It should now look like this:

@import "variables";
@import "libs";
@import "custom";

And, that’s it for our asset optimization. It’s not a lot, but removing useless files is part of the optimization, and is easy to do.

Making it look better

Once we are done with optimizing the assets, let’s modify our templates. We should begin with the one we always see:

The layout

Go to lib/admin_web/template/layout. Here, there is only one file: app.html.eex. As most apps.html (or index.html), it contains the head tag, the body, and inside the body, a script tag. But there is also the header. Because Bootstrap navbar can get big, let’s create another file name header.html.eex where we will put the header and the code from Bootstrap. Here are the two files after modification:

Let’s decrypt both files now, and begin at the beginning with the head tag. The only real difference with normal HTML is the link rel="stylesheet"… which contains a function generating a path to our assets. The script line, at the bottom, should act the same way.

Then, come the render header.html, inside an if. The condition checks if the user is connected. If he’s not, the header won’t appear. Otherwise, the render function will render header.html. The function also take another arguments, which is the structure containing all argument passed to the layout.

The rest was already here. Flash will display a flash message, as you can guess. The second render is way more adaptable, as its purpose is to display the template passed from the controller.

About the header now. We put a lot of code in it. As said previously, you can find most of it in the Bootstrap documentation. What we took from the previous header are the link functions. They generate a a tag with a href to the associated route. We just added a class attribute to match the Bootstrap.

Note: each path need to call a different Routes depending on the resource. If you want to create a link to Page for example, you will need to call Routes.page_path. If you want to create a link to an Admin User, then Routes.admin_user_path should be used.

Finally, we added a class attribute to our logout to make it looks like a button. Nothing really important here.

The login page

This is a file we fully created by ourselves in the part 3.2 of the guide How to build multiple web apps with Elixir thanks to umbrella. There will be some modification, such has adding some div and labels, but it definitely isn’t as big as the header. Once again we will take most of the lines from the Bootstrap form documentation, resulting in something looking like this:

<section class="row">
<div class="col-lg-8 offset-lg-2 col-md-10 offset-md-1 col">
<%= form_for @changeset, Routes.auth_path(@conn, :login), fn f -> %>
<div class="form-group">
<label for="email">Email address</label>
<%= email_input f, :email, class: "form-control", id: "email", placeholder: "Enter email" %>
</div>
<div class="form-group">
<label for="password">Password</label>
<%= password_input f, :password, class: "form-control", id: "password", placeholder: "Password" %>
</div>
<%= submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</section>

Remove everything coming from the Bootstrap documentation, and there aren’t a lot of modifications done. In each input, we added an id , a placeholder and a class attribute, allowing us to reproduce the input field from Bootstrap.

Like the logout button, don’t forget to add the class attribute in the submit, so it looks like a Bootstrap button. And now, you should be done with the login page.

Admin_user templates

Here come the heavy work. Because we created our entity in three steps between two applications (Database and Admin), the template weren’t populated with the fields of our entity. Which means we have to add the fields in our templates, in addition to the Bootstrap. So, let’s begin with the beginning.

index.html.eex

This template will display, in a table, every admin_users and some data about them. The table is already here. It already contains actions, but it doesn’t contain any data.

Let’s create four table header (th), and put a field name in each of one (Email, Username, Accreditation, Actions). You may want to add a class to the Actions th to put the text in the right, class named text-right.

Once it is done, let’s focus on the table content:

<td><%= admin_user.email %></td>
<td><%= admin_user.username %></td>
<td><%= admin_user.accreditation %></td>
<td class="text-right">
<%= link to: Routes.admin_user_path(@conn, :show, admin_user), class: "nav-link" do %><i class="fas fa-search"></i><% end %>
<%= link to: Routes.admin_user_path(@conn, :edit, admin_user), class: "nav-link" do %><i class="fas fa-edit"></i><% end %>
<%= if admin_user.accreditation == "super_admin" do %>
<%= link to: Routes.admin_user_path(@conn, :delete, admin_user),method: :delete, data: [confirm: "Are you sure?"], class: "nav-link disabled" do %>
<i class="fas fa-times"></i>
<% end %>
<%= else %>
<%= link to: Routes.admin_user_path(@conn, :delete, admin_user),method: :delete, data: [confirm: "Are you sure?"], class: "nav-link" do %>
<i class="fas fa-times"></i>
<% end %>
<% end %>
</td>

The first lines are just data from each admin user, thanks to the loop. What really matter’s here are the actions.

As you can see, the code change kind a lot. We kept the links, but added a class nav-link and also replaces the text by a Font Awesome icon. Though, there is a trick here.

If the user is a super admin, we probably don’t want to delete his account, right? Unfortunately, I haven’t found a way to have a conditional class in this particular case, neither do I want to spend too much time trying to figure how to do it.

So, we have to add a condition, which should check if the user is a super admin or not. If it is, we add the class disabled, making it impossible to click on the remove button. And that’s about it for the table, but not for the file.

Once again, the button. Let’s move the create link just under the title. You also want to make it a little bit more looking like a Bootstrap button, so, like many times before, add a class attribute and put "btn btn-primary mb-4" inside. We need the margin bottom, otherwise there will be no space between the button and the table.

show.html.eex

This one is kind of given. We already have a list, and the buttons done (well, they miss a class, but you know which class is missing and how to add it, so you should do it right now).

There is probably a Bootstrap way to show data in a list, but sometimes, it is best to go simple.

<li>
<strong>Username:</strong>
<%= @admin_user.username %>
</li>
<li>
<strong>Email address:</strong>
<%= @admin_user.email %>
</li>
<li>
<strong>Accreditation:</strong>
<%= @admin_user.accreditation %>
</li>

As you can see, there is not a lot going on. The field names are inside a strong tag, to be more visible, and are followed by the data. And that’s it.

New, Edit and form.html.eex

First of all, both new.html.eex and edit.html.eex have only 3 lines, which are similar. We could remove them, but because you may want to add elements depending of the situation, we won’t.

Though, we will move the back button to the form, to have a better looking element (event if we won’t redirect to the previous page).

Note: if you want to redirect to the previous page, you can take a look at a library called NavigationHistory.

Once again, we put fields inside labels inside divs. Each field as its own id and placeholder, and shares the same class with the other fields. The only thing we did here is to put the password in an if statement, so it will only be accessible when you create a user.

This was the easiest option. But we can’t change someone’s password. And because there are a lot of ways to modify a password, I didn’t want to impose anything. From a random new password to a mail with a link to an administrator modifying himself the password, your solution may not be the same as mine, and the simplest one is definitely not the most secured.

Also, the simplest one (which is to have a field) imply to modify the back-end, so the field becomes optional. But, we are not here to do any back-end.

So, it is your time to play and to make decisions! But before, I’ll have you write 7 lines of CSS.

Add some CSS into it

Overall, everything we have done is good looking. And that’s why we won’t write a lot of CSS. But, there are two things which need some work. You could have guessed. The first one is big, and in almost all pages. The second one is just something on multiple lines instead of one.

The image in the header, and our actions in the admin_user index. You probably know what to do, but because I already gave you a lot of simple answers, I will also do it here:

# assets/css/_custom.scss
header nav.navbar img {
max-height: 40px;
}
td > .nav-link {
padding: 0.5rem 0.2rem;
display: inline;
}

In the header, we reduce the size of the image.

In the .nav-link, we put the display as inline, so each icon would be in the same lines. Also, we change the padding to make them closer.

That’s all for today! I hope you didn’t need this bonus tutorial, and if you did, I hope it helped you to understand the basics of the Phoenix template. If you are interested to learn more about testing in Phoenix, you can find more in my new article!

--

--

Cédric Paumard

Elixir developer, bass player and probably too curious.