Tutorial Step 2 - Adding New Items
Our goal in this step is to create a simple, web form based panel for adding new items to the shop.
Contents
Form generator
In the previous tutorial step, before we could browse the service content, we had to add some items manually, using Erlang shell. It could be easily done by passing a ready-to-use record to the wtype_item:create/1 function. Unfortunately, it can't be done in this way - we have to provide user some way to enter the data in its browser and send it to the server.
So far, we did not even touch item_types record. Right now, Erlang Web's form generator will use it for automatic form building process.
At first we should add rules to the dispatcher (put them right after the /item/show/N rule):
1 {static, "^/item/add$", "item/add.html"}.
2 {dynamic, "^/item/do_add$", {item_admin, do_add}}.
The first rule is responsible for displaying the form for our item. The second one will deal with actual item creation - will be associated with submit button placed on the generated form.
Look at the templates/item/add.html template:
<html> <head> <title>Adding new item</title> </head> <body> <h1>Adding new item</h1> <wpart:form type="item" action="/item/do_add" /> </body> </html>
wpart:form is responsible for the form building and is fully described here.
And the result (http://localhost:8080/item/add):
Hmm, it seems that everything works correctly, but... We have completely no clue what is the meaning of the particular fields!
Adding the fields description and comments
Let's then edit the item_types record definition:
1 -record(item_types, {
2 id = {integer, [primary_key, {private, true}]},
3 name = {string, [{description, "Name"},
4 {max_length, 256}]},
5 description = {text, [{description, "Description"},
6 {max_length, 10000}]},
7 price = {float, [{description, "Unit price"},
8 {min, 0.0}]},
9 available = {enum, [{description, "Is item available?"},
10 {choices, "true:Yes|false:No"},
11 {chosen, "true"}]}
12 }).
Much better! But user still does not know about the limitations for the particular fields - we should inform him about the constraints:
1 -record(item_types, {
2 id = {integer, [primary_key, {private, true}]},
3 name = {string, [{description, "Name"},
4 {comment, "Maximum length is 256 characters"},
5 {max_length, 256}]},
6 description = {text, [{description, "Description"},
7 {comment, "Maximum length is 10000 characters"},
8 {max_length, 10000}]},
9 price = {float, [{description, "Unit price"},
10 {min, 0.0}]},
11 available = {enum, [{description, "Is item available?"},
12 {choices, "true:Yes|false:No"},
13 {chosen, "true"}]}
14 }).
Wonderful! But ugly as well - we should definitely start playing with CSS file!
Modifying CSS file
Erlang Web form generator uses some pre-defined HTML classes for the particular field types:
each of the model's fields is surrounded with some container with attribute class set to form_entry and attribute id set to long_field_name (by default container is set to div element and long_field_name in case of item's field price will be item_price)
the description element is a tag label with an attribute for set to the id of the target element
the comment element is a tag span with a class set to form_comment and id set to long_field_name_comment
the input element is surrounded with span tag with class set to form_input
Let's create the CSS file (docroot/style.css):
.form_container {
position: relative;
width: 80%;
}
.form_entry
{
margin-top: 25px;
margin-left: 20px;
position: inherit;
}
.form_input {
position: absolute;
left: 40%
}
.form_comment {
position: absolute;
top: 17px;
left: 0;
font-size: small;
font-style: italic;
}
.form_error {
font-variant: small-caps;
color: red;
}
div#item_description, div#item_available
{
margin-bottom: 40px;
}Then we should add a proper entry to the dispatcher configuration file:
1 {static, "^/style\.css$", enoent}.
And at the end edit the item/add.html template (add this line to the head section of the template):
<link rel="stylesheet" type="text/css" href="/style.css" />
That's all: refresh the browser and see how does it look:
Saving the data to the database
Ok, we are now ready with our form, we should now focus on the most important thing - saving the elements to the database. As we could see in the code above, dispatcher points to the item_admin:do_add/1 function as a handler for the /item/do_add request. We will use Erlang Web's generator once again:
$ ./bin/generate.erl controller --app shop --name item_admin Exported functions (separated with ,): do_add File (...)/lib/shop-0.1/src/item_admin.erl created successfully!
And the result:
1 -module(item_admin).
2 -export([do_add/1]).
3
4 do_add(_Args) ->
5 %% put the do_add function body here
6 ok.
Let's write some code then:
1 do_add(_Args) ->
2 case validate_tool:validate_cu(item, create) of
3 {ok, Item} ->
4 wtype_item:create(Item),
5 {redirect, "/item/all"};
6 {error, _Reason} ->
7 wpart:fset("__edit", wtype_item:prepare_validated()),
8 {template, "item/add.html"}
9 end.
Remember that we can never trust the data that is not validated: in order to check if all the constraints are satisfied, we are calling validate_tool:validate_cu/2. It should either return us a message that the entered data is correct or an error. In case when everything is all right we will simply save the content to the database and redirect user to /item/all URL. Otherwise, we should go back to the form, but with some fields filled and the error messages set.
If everything is all right, after entering a correct data into form:
we should be redirected to the /item/all and see one more item:
When we click on the new item, we should see:
Great! But what is going on when we enter the incorrect data?
Hit the submit button and see the result:
The error messages are surrounded with the span tags with class set to form_error. The messages are held inside the config/errors_description.conf. If you don't like the default ones you can change them easily.
Correct the data, hit submit and enjoy the new items in your shop!
Download
This tutorial step is over, the package containing the sources of the application can be downloaded from here.
