Tutorial Part 2: Todo App - Team Members

This is part 2 of the tutorial - it expects that you have completed part 1. Today we will be adding a list of team members using the Pogo stack. Later you will be able to assign todos to them. We will be:

  • Adding navigation
  • Storing team members in a Postgres database
  • Using HTML and vanilla javascript for the Frontend
  • Using partial views to filter displayed team members

While the application is simple in its own right, the concepts here can apply to much more advanced apps.

What We'll Be Building

Base Setup

Initial Setup

Your directory structure should look like this carried over from the previous tutorial:

Pogo To do app final directory structure

Database Setup

In the terminal navigate to your app folder cd path/to/pogo_todo/app.

Open up psql on the command line and connect to your database by running \c db_todo test. Now, we'll create a table to contain the team members. All we need is an id for each person, their first name, last name and email address, run the following in psql:

create table team_members
(
   id serial,
   first_name varchar,
   last_name varchar,
   email varchar
);

Let's add some sample team members to loop through later. Run the below to add a few todo items.

insert into team_members (first_name, last_name, email) values
   ('David'   , 'Berner'     , 'dave@pogostack.org'       ),
   ('Sandor'  , 'Clegane'    , 'sandor@houseclegane.com'  ),
   ('Gregor'  , 'Clegane'    , 'greg@houseclegane.com'    ),
   ('Rickon'  , 'Stark'      , 'winter@iscoming.net'      ),
   ('Berrick' , 'Dondarrion' , 'thoros@myr.org'           ),
   ('Jojen'   , 'Reed'       , 'jojen@reed.org'           ),
   ('Ilyn'    , 'Payne'      , 'axeman@kingslanding.com'  ),
   ('Podrick' , 'Payne'      , 'pod@housepayne.com'       ),
   ('Davos'   , 'Seaworth'   , 'davos@nofingers.net'      ),
   ('Alliser' , 'Thorne'     , 'ali@watcheronthewall.org' ),
   ('Alex'    , 'Nedoboi'    , 'alex@pogostack.org'       )
;

That's it, your database is ready to rock!

Server Setup

Update your pogo_server_config.txt with the lines highlighted below. This allows the server to render the two pogo-html files we're shortly going to shortly.

#
# Configuration settings for PoGoStack Server
#
# NOTE: parameters and values below MUST be split by just one space (no tabs, no multiple spaces)
#

### Mandatory parameters ###

config_connection_string user=test password=test dbname=db_todo sslmode=disable
config_default_page index
allow index
allow team
allow list_team_table

### Optional parameters ###

config_server_name PoGoToDo
#config_listen_address localhost:4200
#config_authentication_type basic

If your server is already running type CTRL + C to cancel it as we'll need to start it again to take on the config changes. Changes to this file are the only time you should need to restart.

Start the server going by running the following in a new terminal window: export GOPATH=~/gocode ; go run ../pogocore/pogo_server.go.

Building the team page!

As in part 1, for the sake of getting up and running quickly we'll be using Bootstrap to style our the table.

We recommend splitting your application by feature, so create a directory inside your app folder called team. Inside this new folder create a file called team.pogo with the the following markup:

# Team Page

team

p_page   varchar Team
p_filter varchar -

<?

?><%
%><%= psp_html_head('p_title=' || p_page, CU) %>

<body>

<div class="container-fluid">

   <form class="form-inline">
      <div class="form-group">
         <label class="sr-only" for="exampleInputAmount">Filter:</label>
         <div class="input-group">
            <div class="input-group-addon"> 	
               <span class="glyphicon glyphicon-search"></span>
            </div>
            <input class="form-control" type="text" onkeyup="current_filter = this.value; return f_load_table('team_table');" placeholder="Filter" autofocus>
          </div>
      </div>
   </form>
   <br>

   <div id="team_table">

   </div>

</div>
</body>
</html>

So far, we've just created an empty shell that the table is going to live in.

You probably will have noticed some new syntax though. You'll note there is a code snippet that leads with psp. This stands for "pogo server pages" and is used for calling partial views.

For example, on the line <%= psp_html_head('p_title=' || p_page, CU) %> we are calling a partial view called html_head and requesting it be rendered on the page. Let's create that view now.

Create a new directory in your app folder called layout and inside this create a new file called html_head.pogo.

Your directory structure should now look like this:

Pogo To do app feature and layout directory structure

Fill your html_head.pogo file with the following mark up:

# HTML head partial

html_head

p_title varchar -

<?

?><%

%><!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->
	<title><%= p_title %> | Pogo Todo App</title>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">


</head>

This code has been extracted as it is very likely to appear on all pages. It has a variable called p_title which is where we can pass in a string that we have referenced in the title tag.

Compile your code from your app folder by running ./compile-all.sh or if you're using Sublime Text you can set up build scripts to automate compilation.

In your chosen browser navigate to localhost:4200/team. There is not much to look at, but you should notice that the title has been persisted to your active browser tab.

Title tag passed through via a title tag

Adding navigation

Create a new file called navigation.pogo inside your layout folder and fill it with the following mark up:

# Pogo Todo app nav

navigation

p_active varchar -

<?

?><%

%><nav class="navbar navbar-inverse">
   <div class="container-fluid">
      <div class="navbar-header">
         <a class="navbar-brand" href="#">Pogo Todo</a>
      </div>
      <ul class="nav navbar-nav navbar-right">
         <li<% if p_active = 'Team' then %> class="active"<% end if; %>>
            <a href="team">Team</a>
         </li>
         <li<% if p_active = 'Home' then %> class="active"<% end if; %>>
            <a href="index">Todos</a>
         </li>
      </ul>
   </div>
</nav>

At the top of this file we have a variable called p_active we will be using this to identify the page that the user is on. You'll notice we use <% if p_active = 'Team' then %> class="active"<% end if; %> to check if the active page is 'Team' and add a class called active if it is true.

So how does it know what page we're on? We'll set that up now. In your team.pogo file add the highlighted line below.


[...]
<body>

<div class="container-fluid">
<%= psp_navigation('p_active=' || p_page, CU) %>
   <form class="form-inline">
[...]

Remember the psp part is what tells pogo it is calling a partial, in this case a file called navigation. It is passing in p_active as a parameter which is set to the value of the variable p_page. We set p_page to Team at the top of the team.pogo file earlier.

The other thing we'll want to do is make this work for our earlier Todo page so open up index.pogo and add the following highlighted line:


[...]
index

p_page varchar Home
p_action varchar (none)
p_id integer 0
p_checked integer 0
p_name varchar (none)

[...]

and replace:


<!doctype html>
<html>
<head>
<!-- META -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->

<title>Pogo Todo App</title>

<!-- STYLES -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"><!-- load bootstrap -->

</head>

with this single line: <%= psp_html_head('p_title=' || p_page, CU) %>. We extracted all this content out earlier into it's own partial so we don't need to duplicate that code.

In the same file insert the following highlighted text:


[...]
<body>

   <div class="container-fluid">

      <%= psp_navigation('p_active=' || p_page, CU) %>

      <!-- HEADER AND TODO COUNT -->
      <div class="jumbotron text-center">

[...]

Compile your code in the usual way. And refresh your browser. You should now see a nav bar and be able to toggle between the two pages that look like the below:

Creating the table

Create a new file called list_team_table.pogo inside your team folder. Your directory structure should look like the below:

Pogo To do app final directory structure

Fill your list_team_table.pogo file with the following code:

# Pogo team members table

list_team_table

p_filter varchar -

<?
   r record;
?><%
%>
<table class="table table-striped">
   <thead>
      <tr>
         <th>First Name</th>
         <th>Last Name</th>
         <th>Email</th>
      </tr>
   </thead>
<%
   for r in
   (
      select first_name, last_name, email from team_members
   )
   loop
%>
   <tr>
      <td><%= r.first_name %></td>
      <td><%= r.last_name  %></td>
      <td><%= r.email      %></td>
   </tr>
<%
   end loop;
%>
</table>

Now we'll reference this partial from our team.pogo file. Add the highlighted line below:


[...]
</div>
</form>
<br>
   <div id="team_table">
   <%= psp_list_team_table('-', CU) %>
   </div>

[...]

This should result in you being able to see a rendered table with all the team members we added earlier. Compile your code and refresh your browser. You should see something like this:



Currently though, the filter doesn't work. When you type, nothing happens. So we'll fix that up with some javascript.

Creating JS files in Pogo

Create a new directory inside your app called scripts and inside that create a file called js_functions.pogo. Your directory structure should look like the below:

Pogo To do app final directory structure

Fill your js_functions.pogo with the following code:

# Pogo js functions

js_functions

<?
?><%
%>
<script>
"use strict";

var current_filter = "";

function f_load_table(p_id)
{
   var request = new XMLHttpRequest();
   var parameters_string = "p_filter=" + current_filter;

   request.onreadystatechange = function()
   {
      if (request.readyState == 4 && request.status == 200)
      {
         document.getElementById(p_id).innerHTML = request.responseText;
      }
   };

   request.open("POST", "list_team_table", true);
   request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
   request.send(parameters_string);
}

</script>

We'll need to make sure this javascript partial is called so we'll be using the trusty psp function again. In your team.pogo file add the following highlighted line.


[...]
</div>
</form>
<br>
   <div id="team_table">
   <%= psp_list_team_table('-', CU) %>
   </div>

   <%= psp_js_functions('-', CU) %>

 </div>
 </body>
 </html>  

This script gets called when a user types into the input field and reloads the list_team_table.pogo partial and passes in the letters typed as a parameter.

All our files are hooked up now, but currently the partial won't know what to do with these letters so there is one final step. Add the following highlighted lines to your list_team_table.pogo file:


[...]

   <th>Email</th>
</tr>
   </thead>
<%
   for r in
   (
      select first_name, last_name, email from team_members
 
      where
      (
         p_filter = '-'
         or first_name ilike '%' || p_filter || '%'
         or  last_name ilike '%' || p_filter || '%'
      )

   )
   loop
%>
<tr>

[...]

Compile your code from your app folder by running ./compile-all.sh or if you're using Sublime Text you can set up build scripts to automate compilation.

Refresh your browser and now typing letters from the first name or last names of the users should filter the list.

Part 3 demonstrating Pogo's awesome built-in user authentication is coming soon.