AWS NodeJS App 04 – Formulário de Inclusão de produtos

Neste tutorial vamos criar um formulário para inclusão de produtos em nosso site. Mas antes de iniciar precisamos primeiro instalar uma nova biblioteca que será utilizada para geração das chaves únicas dos produtos. Para isso execute o comando no terminal do Cloud9:

npm install --s uuid

Também precisamos organizar nossa pasta de views, uma vez que agora teremos um segundo controlador para cuidar do formulário de produtos. Para isso dentro da pasta views, vamos criar duas subpastas: index e product. Mova os arquivos index.hbs e index2.hbs para a nova pasta index.

Para que a aplicação continue funcionando devemos alterar o código do indexcontroller para que os dois métodos das ações busquem o arquivo de template de dentro da nova pasta index.

Vamos iniciar a construção pelo formulário, dentro da pasta views -> product crie um novo arquivo com o nome de form.hbs e utilize o código de sugestão abaixo. A parte mais importante é o formulário em destaque abaixo onde vamos apontar a action do formulário para a rota /product/save utilizando o método POST. E criamos um tag input para cada um dos campos do produto: name, description e price.

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="max-age=604800" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="author" content="Bootstrap-ecommerce by Vosidiy">

<title>UI KIT - Marketplace and Ecommerce html template</title>

<link href="/images/favicon.ico" rel="shortcut icon" type="image/x-icon">

<!-- jQuery -->
<script src="/javascripts/jquery-2.0.0.min.js" type="text/javascript"></script>

<!-- Bootstrap4 files-->
<script src=/"javascripts/bootstrap.bundle.min.js" type="text/javascript"></script>
<link href="/stylesheets/bootstrap.css" rel="stylesheet" type="text/css"/>

<!-- Font awesome 5 -->
<link href="/fonts/fontawesome/stylesheets/fontawesome-all.min.css" type="text/css" rel="stylesheet">

<!-- custom style -->
<link href="/stylesheets/ui.css" rel="stylesheet" type="text/css"/>
<link href="/stylesheets/responsive.css" rel="stylesheet" media="only screen and (max-width: 1200px)" />

<!-- custom javascript -->
<script src="/javascripts/script.js" type="text/javascript"></script>

<script type="text/javascript">
/// some script

// jquery ready start
$(document).ready(function() {
	// jQuery code

}); 
// jquery end
</script>

</head>
<body>
<header class="section-header">
<nav class="navbar navbar-top navbar-expand-lg navbar-dark bg-secondary">
<div class="container">
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="navbarNav">
    <ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="http://bootstrap-ecommerce.com">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item"><a class="nav-link" href="html-components.html"> Documentation </a></li>
<li class="nav-item dropdown">
	<a class="nav-link  dropdown-toggle" href="#" data-toggle="dropdown">  Dropdown  </a>
    <ul class="dropdown-menu">
	  <li><a class="dropdown-item" href="#"> Menu item 1</a></li>
	  <li><a class="dropdown-item" href="#"> Menu item 2 </a></li>
    </ul>
</li>
<li class="nav-item"><a class="nav-link" href="http://bootstrap-ecommerce.com"> Download <i class="fa fa-download"></i></a></li>
    </ul>
  </div>
</div> <!-- container //  -->
</nav>
<section class="header-main shadow">
	<div class="container">
<div class="row align-items-center">
	<div class="col-lg-3 col-sm-4">
	<div class="brand-wrap">
		<img class="logo" src="images/logo-dark.png">
		<h2 class="logo-text">LOGO</h2>
	</div> <!-- brand-wrap.// -->
	</div>
	<div class="col-lg-6 col-sm-8">
			
	</div> <!-- col.// -->
	<div class="col-lg-3 col-sm-12">
			<a href="#" class="widget-header float-md-right">
				<div class="icontext">
					<div class="icon-wrap"><i class="flip-h fa-lg fa fa-phone"></i></div>
					<div class="text-wrap">
						<small>Phone</small>
						<div>+97150 2813773</div>
					</div>
				</div>
			</a>
	</div> <!-- col.// -->
</div> <!-- row.// -->
	</div> <!-- container.// -->
</section> <!-- header-main .// -->
</header> <!-- section-header.// -->

<!-- ========================= SECTION PAGETOP ========================= -->
<section class="section-pagetop bg-secondary">
<div class="container clearfix">
	<h2 class="title-page">Produto</h2>
</div> <!-- container //  -->
</section>
<!-- ========================= SECTION INTRO END// ========================= -->

<!-- ========================= SECTION CONTENT ========================= -->
<section class="section-content bg padding-y">
<div class="container">

<div class="row">
	<main class="col-sm-9">

<article class="card">
	<div class="card-body">
		<form action = "/product/save" method="POST">
		  <div class="form-group">
		    <label for="inputName">Nome</label>
		    <input type="text" class="form-control" id="inputName" name="name" placeholder="Nome do produto">
		  </div>
		  <div class="form-group">
		    <label for="inputDesc">Descrição</label>
		    <textarea rows="5" cols="33" class="form-control" id="inputDesc" name="description" placeholder="Descrição do produto"></textarea>
		  </div>
		  <div class="form-group">
		    <label for="inputPrice">Preço</label>
		    <input type="number" min="0" max="1000" step="0.25" class="form-control" id="inputPrice"  name="price" placeholder="Preço do produto">
		  </div>
		  <button type="submit" class="btn btn-primary">Salvar</button>
		</form>
	</div> <!-- card-body .// -->
</article> <!-- card product .// -->



	</main> <!-- col.// -->
</div>

</div> <!-- container .//  -->
</section>
<!-- ========================= SECTION CONTENT END// ========================= -->



<!-- ========================= FOOTER ========================= -->
<footer class="section-footer bg-secondary">
	<div class="container">
		<section class="footer-top padding-top">
			
		</section> <!-- //footer-top -->
	</div><!-- //container -->
</footer>
<!-- ========================= FOOTER END // ========================= -->


</body>
</html>

Para poder visualizar esse novo formulário vamos criar dentro da pasta controller um novo arquivo chamado productcontroller.js Nele vamos criar a classe ProductController e a ação form que irá renderizar o formulário.

class ProductController{
    async form(req,res,next){
        res.render('product/form');
    }
    
}
module.exports = ProductController;

Em seguida, dentro da pasta routes, vamos criar um novo arquivo chamado product.js onde vamos carregar os módulos de rotas da biblioteca Express para direcionar a nova rota /form para a ação form do controlador do produto.

var express = require('express');
var router = express.Router();

var ProductController = require("../controller/productcontroller");
var productController = new ProductController();

/* GET users listing. */
router.get('/form', function(req, res, next) {
    productController.form(req,res,next);
});


module.exports = router;

Por fim podemos modificar o arquivo app.js para carregar nosso novo arquivo de rotas e incluir a rota /product que será tratada por ele.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var productRouter = require('./routes/product');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/product', productRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

Em seguida execute a aplicação, utilizando o comando nodemon, a acesse a URL de teste da aplicação /product/form para verificar se o formulário é carregado corretamente.

Agora que nosso formulário esta pronto, precisamos criar o código necessário para receber os dados e incluir no DynamoDB. Para isso vamos alterar o código do productService para carregar a função v4 da biblioteca UUID, e em seguida criar um novo método na classe chamado save. Esse método deve receber o product por parâmetro, e verificar se sua propriedade id é nula, caso afirmativo, chamar a função uuidv4 para criar um id randômico, e em seguida utilizar o método put do mapper do DynamoDB para inserir o produto no banco de dados. O restante do código da classe permanece igual.

const uuidv4 = require('uuid/v4');

class ProductService{
    async save(product){
        if(product.id == null || product.id === ""){
            product.id = uuidv4();
        }
        mapper.put({item: product}).then(() => {
            return product;
        });
    }

Em seguida vamos modificar a classe ProductController para importar o ProductService e a referencia para a classe Product, e inserir um novo método em ProductController chamado save. Este método receberá os dados do formulário, criará uma nova instância da classe Product passando os dados do formulário para cada atributo, e em seguida chamando o método save da classe ProductService. IMPORTANTE: esse método temporariamente irá redirecionar os usuários para a rota raiz da aplicação.

var ProductService = require("../services/productservice");
var productService = new ProductService();
const Product = require("../models/product");

class ProductController{
    async form(req,res,next){
        res.render('product/form');
    }
    async save(req,res,next){
        var product = new Product();
        product.name = req.body.name;
        product.description = req.body.description;
        product.price = parseFloat(req.body.price);
        
        productService.save(product);
        
        res.redirect('/');
    }
}
module.exports = ProductController;

Para concluir, devemos alterar o código da classe product.js para incluir a nova rota /save do método POST que executará a ação save do ProductController.

Execute a aplicação utilizando o comando nodemon, e acesse a URL da aplicação /product/form. Preencha os campos com os dados de um novo produto e pressione o botão Salvar. O novo produto deverá ser apresentado na tela inicial do site.

Ao consultar os dados no DynamoDB, observe que os dados são armazenados corretamente e o campo id foi gerado de forma randômica com sucesso.