AngularJS: Formulário de Contato
Neste tutorial iremos utilizar o angularjs juntamente com o PHP para o envio de emails de um formulário de contatos.
Requisitos para esse Tutorial
- O projeto Anterior pode ser visto aqui.
- WAMP, LAMP, MAMP e/ou Plano de Hospedagem.
- Conhecimento básico de PHP.
Instalação do Wamp
A instalação passo-à-passo pode ser vista neste link.
Desenvolvimento da Página de Contato
Primeiramente, iremos mover os arquivos do Tutorial anterior para a pasta www do seu wamp. Geralmente, localiza-se em c:\wamp\www . Em seguida, criaremos uma pasta com o nome ‘tutorial’ e a sua estrutura ficará parecida com esta:
- www
- tutorial
- css
- fonts
- img
- js
- templates
- index.html
- tutorial
Ao acessar http:\localhosttutorial em seu navegador, conforme nosso exemplo anterior, uma página semelhante à imagem abaixo deverá aparecer:
O primeiro passo será atualizar a versão do nosso angular, que referenciamos no arquivo index.html, conforme exemplo abaixo:
<!-- alteramos o angular da versão 1.3.14 para 1.4.1 -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
Feito isso, iremos editar o arquivo templatesheader.html, para acrescentarmos ao menu o link para contato.
<ul class="nav navbar-nav"> <!-- cada linha é um item do menu --> <li><a href="#/">Home</a></li> <li><a href="#/servicos">Serviços</a></li> <li><a href="#/precos">Preços</a></li> <li><a href="#/sobre">Sobre</a></li> <li><a href="#/contato">Contato</a></li> <!-- nosso novo item do menu --> </ul>
A seguir iremos acrescentar a rota, para carregarmos tanto o modelo da página de contatos como o seu controller. Para isso iremos editar o arquivo jsmain.js
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
// aqui dizemos quando estivernos na url / vamos carregar o conteúdo da pagina inicila a home
// no segundo parametro definimos um objeto contendo o templateUrl da nossa pagina home e o controller que irá
// preparar o conteudo e processar outros eventos da página que veremos posteriormente
.when("/", {templateUrl: "templates/home.html", controller: "HomeCtrl"})
// configuração das rotas bem parecidas para as outras paginas
.when("/sobre", {templateUrl: "templates/sobre.html", controller: "SobreCtrl"})
.when("/servicos", {templateUrl: "templates/servicos.html", controller: "ServicosCtrl"})
.when("/precos", {templateUrl: "templates/precos.html", controller: "PrecosCtrl"})
/* aqui você pode adicionar rotas para outras paginas que desejar criar */
.when("/contato", {templateUrl: "templates/contato.html", controller: "ContatoCtrl"})// nossa nova rota
// por último dizemos se nenhuma url digitada for encontrada mostramos a página 404 que não existe no nosso servidor
.when('/404', {templateUrl: "templates/404.html"})
.otherwise("/404");
}]);
Logo abaixo nesse mesmo arquivo, iremos criar o controller que será o responsável por pegar os dados preenchidos pelo usuário e enviar para o servidor, onde o mesmo encarregar-se-á de enviar o email de contato.
app.controller('ContatoCtrl', function ($scope, $location, $http) {
});
Iremos criar o HTML da página de contatos e posteriormente atualizar este controller. Para isso vamos criar o arquivo contato.html na pasta templates.
<div class="row" ng-if="!emailEnviado">
<div class="box">
<div class="col-lg-12 text-center">
<hr/>
<h2 class="intro-text">Contato</h2>
<hr/>
</div>
<div class="col-lg-12">
<div class="alert alert-danger" ng-if="frmContato.nome.$error && !frmContato.nome.$valid" ng-show="frmContato.nome.$dirty">
<strong ng-show="frmContato.nome.$error.required">Nome é Obrigatório!</strong>
<strong ng-show="frmContato.nome.$error.minlength">Nome deve conter no minímo 3 caractéres!</strong>
</div>
<div class="alert alert-danger" ng-if="frmContato.email.$error && !frmContato.email.$valid" ng-show="frmContato.email.$dirty">
<strong ng-show="frmContato.email.$error.required">Email é Obrigatório!</strong>
<strong ng-show="frmContato.email.$error.email">Email está Inválido!</strong>
</div>
<div class="alert alert-danger" ng-if="frmContato.assunto.$error && !frmContato.assunto.$valid" ng-show="frmContato.assunto.$dirty">
<strong ng-show="frmContato.assunto.$error.required">Assunto é Obrigatório!</strong>
<strong ng-show="frmContato.assunto.$error.maxlength">Assunto deve conter no máximo 100 caractéres!</strong>
</div>
<div class="alert alert-danger" ng-if="frmContato.mensagem.$error && !frmContato.mensagem.$valid" ng-show="frmContato.mensagem.$dirty">
<strong ng-show="frmContato.mensagem.$error.required">Mensagem é Obrigatório!</strong>
<strong ng-show="frmContato.mensagem.$error.minlength">Mensagem deve conter no minímo 30 caractéres!</strong>
<strong ng-show="frmContato.mensagem.$error.maxlength">Mensagem deve conter no máximo 300 caractéres!</strong>
</div>
<div class="alert alert-danger" ng-if="emailNaoEnviado">
<strong>Opss!</strong> Houve um problema ao enviar o seu contato, tente mais tarde.
</div>
<form name="frmContato" class="form-horizontal" role="form">
<div class="form-group" ng-class="{ 'has-error': frmContato.nome.$invalid && frmContato.nome.$dirty}">
<label for="nome" class="col-lg-2 control-label">Nome</label>
<div class="col-lg-10">
<input ng-model="formData.nome" ng-minlength="3" type="text" class="form-control" name="nome" placeholder="Seu Nome" ng-required="true">
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': frmContato.email.$invalid && frmContato.email.$dirty}">
<label for="email" class="col-lg-2 control-label">E-Mail</label>
<div class="col-lg-10">
<input ng-model="formData.email" type="email" class="form-control" name="email" placeholder="Seu E-Mail" ng-required="true">
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': frmContato.assunto.$invalid && frmContato.assunto.$dirty}">
<label for="assunto" class="col-lg-2 control-label">Assunto</label>
<div class="col-lg-10">
<input ng-model="formData.assunto" ng-maxlength="100" type="text" class="form-control" name="assunto" placeholder="Assunto" ng-required="true">
</div>
</div>
<div class="form-group" ng-class="{ 'has-error': frmContato.mensagem.$invalid && frmContato.mensagem.$dirty}">
<label for="mensagem" class="col-lg-2 control-label">Mensagem</label>
<div class="col-lg-10">
<textarea ng-model="formData.mensagem" ng-minlength="30" ng-maxlength="300" class="form-control" rows="4" name="mensagem" placeholder="Sua mensagem..." ng-required="true"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button type="submit" ng-disabled="!frmContato.$valid" ng-click="enviarEmail()" class="btn btn-default" ng-disabled="submitButtonDisabled">
Enviar
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="row" ng-if="emailEnviado">
<div class="box">
<div class="alert alert-success">
<strong>Email foi enviado com sucesso!</strong> Entraremos em contato em breve!
</div>
</div>
</div>
Verificamos que neste código HTML encontram-se algumas “tags” que naturalmente não fazem parte do HTML-Standard, pois são tags especiais do AngularJS. Abaixo, uma breve descrição de cada uma delas:
ng-if
A documentação oficial <https://docs.angularjs.org/api/ng/directive/ngIf> , nos demonstra a seguinte definição:
A diretiva
ngIfremove ou recria uma parte da árvore DOM com base em uma {expressão}. Se a expressão for atribuída angIfe avaliada como um valor falso, então o elemento é removido do DOM, caso contrário, um clone do elemento é reinserido no DOM.
Ou seja, nas linhas 9, 14, 19, 24, estamos usando o ng-if para mostrarmos o alerta, caso cada “input” contenha erro e ele não esteja válido.
ng-show
A documentação oficial <https://docs.angularjs.org/api/ng/directive/ngShow> nos demonstra a seguinte definição:
A diretiva ngShow exibe ou oculta o elemento HTML baseado na {expressão} fornecida. O elemento é exibido ou ocultado pela remoção da classe CSS. ng-hide . Essa classe do AngularJS define “display: none !important” no elemento.
Ou seja, ao contrário do ngIf que não cria o elemento, o ngShow cria o elemento e apenas altera a visibilidade do elemento conforme as expressões nas linhas 9, 14, 19, 24 . Usamos o ngShow para exibir o erro quando o campo html for alterado;
ng-class
Na documentação oficial <https://docs.angularjs.org/api/ng/directive/ngClass> a seguinte definição é apresentada:
A diretiva
ngClasspermite definir dinamicamente classes CSS em um elemento HTML por ligação de dados (databinding). Uma expressão que representa todas as classes a serem adicionados.
Ou seja, as classes de estilo são adicionadas ou removidas do elemento conforme as expressões. Nas linhas 35, 41, 47, 53 o ngClass é utilizado para adicionar a classe css ‘has-error’ caso o input contenha algum erro.
ng-model
A documentação oficial <https://docs.angularjs.org/api/ng/directive/ngModel> oferece a seguinte definição:
A diretiva
ngModelse liga (bind) a uminput,select,textarea(ou controle de formulário personalizado), para uma propriedade no ‘scope’ usando NgModelController, que é criado e exposto por esta diretiva.
Com isso, posteriormente é através dos ngModel que poderemos pegar os dados digitados em cada input e poderemos enviar para o servidor. Posteriormente, enviará para o email de contato.
ng-click
Na documentação oficial <https://docs.angularjs.org/api/ng/directive/ngClick> encontramos a seguinte definição:
A diretiva ngClick permite que você especifique o comportamento personalizado quando um elemento é clicado.
Na linha 61, usamos o ng-click para chamarmos a função que será responsável pelo envio dos dados do formulário para o servidor.
ng-input, ng-required, ng-minlenght e ng-maxlenght
Na documentação oficial <https://docs.angularjs.org/api/ng/input/input%5Btext%5D> a seguinte definição sobre ng-input é apontada:
Entrada de texto padrão HTML com ligação de dados angular, herdados pela maioria dos
elementos input.
ngRequired
Adiciona o atributo
required,necessáriopara restrição de validação do elemento, quando a expressão ngRequired avaliada como verdadeira. UsengRequiredem vez derequiredquando você quiser validar o required.
ngMinlenght
Atribui
minLength(mínimo). Erro de validação se o valor for menor do que minlength.
ngMaxlenght
Seta
maxLength (máximo).Erro de validação se o valor for maior do que maxlength.
Validação
Para podermos criar a validação o elemento <form> tem que ter o atributo name, e cada input também deve ter o atributo name. Ex: nomeForm.nomeInput
Com isso podemos acessar algumas propriedades:
- $error -> retorna true se existe erro
- $valid -> retorna true quando está válido
- $dirty -> quando o campo foi modificado (geralmente na digitação)
Na propriedade $error, podemos acessar os filhos que compõe a validação como required, minlenght, maxlenght, entre outros. Na linha 9 usamos no ngIf para verificarmos se no frmContato.nome$error existem erros. Já na linha 10 exibiremos a mensagem de obrigatório, caso o frmContato.nome.$error.required estiver vazio após a edição do campo.
Controller
Agora iremos trabalhar no controller que criamos anteriormente no main.js
app.controller('ContatoCtrl', function ($scope, $location, $http) {
//criando o objeto em branco, porém após o submit todos os dados estaram nessa variável
$scope.formData = {};
//variavel para ebir ou não a mensagem que o email foi enviado
$scope.emailEnviado = false;
//variável para exibir mensagem de erro ao enviar o email
$scope.emailNaoEnviado = false;
//função que será usada para preparar os dados no formato que poderemos usar no servidor para envio do email
var param = function(data) {
var returnString = '';
for (d in data){
if (data.hasOwnProperty(d))
returnString += d + '=' + data[d] + '&';
}
// Remove o último & que não é necessário
return returnString.slice( 0, returnString.length - 1 );
};
$scope.enviarEmail = function() {
$http({
method : 'POST',
url : 'email.php',
data : param($scope.formData),
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
})
//função de callback quando a promise for sucesso
.success(function(data) {
//verifica o retorno do servidor se o email foi enviado
if (data.enviado) {
$scope.emailEnviado = true; //ocultamos o formulário e exibimos mensagem de sucesso
} else {
$scope.emailNaoEnviado = true;
}
})
//função de callback quando a promise for erro (geralmente problema de conexão ou página não existente)
.error(function(error){
$scope.emailNaoEnviado = true;
});
};
});
No início do controller, definimos as 3 variáveis de controle de nossa página de contato, onde $scope.formData terá os dados preenchidos no formulário. Podemos perceber que no html contemos nos input formData.nome pelo fato de ser um objeto e no angular a palavra $scope não é necessária no template html. O mesmo vale para as outras duas variáveis que usamos para mostrar ou não a mensagem de envio, ou se houve algum erro de envio do formulário.
Em seguida temos 2 funções. A primeira é responsável por incluir os dados do formData no formato que iremos pegar no php. A segunda função é que vinculamos ao botão de envio, ele prepara os dados e envia para a url que definimos, e seu correspondente método, nesse caso POST. Isso porque usamos um serviço que o AngularJS possui, o $http, que permite a comunicação com algum servidor. Nesse caso estamos enviando os dados do formulário que será processado e enviado por email.
Este serviço é denominado promise ou promessa, que envia uma solicitação e aguarda este retorno para dar continuidade, neste caso, ao envio de dados. Duas funções são atribuídas a este processo: O success que significa basicamente que o processo obteve êxito em encontrar a url desejada e proceder com o envio de dados, recebendo true como resposta. Caso o retorno seja de fato um true, o email será enviado. Em caso de false, algum problema ocorreu no processo. E o error que neste caso, significa que ele não conseguiu achar essa url, ou de fato encontrou, mas retornou com um erro 404.
Parte do Servidor
E agora criaremos na raiz do projeto onde temos o arquivo index.html, criaremos o arquivo email.php
<?php
header('Access-Control-Allow-Origin: *');
$remetente = $_POST['nome'] . " <" . $_POST['email'] . ">";
$nome = $_POST['nome'];
$email = $_POST['email'];
$assunto = $_POST['assunto'];
$msg = $_POST['mensagem'];
$destino = "[email protected]"; // o email que irá receber os emails do site
$html_msg = "
<html>
<head>
<title>Contato - Site</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<font size="1" face="verdana">
<strong>EmpresaSA (Contato pelo Site)</strong>
</font>
<font face="verdana">
<p>
<font size="1">
<strong>Nome:</strong> $nome <br>
<strong>E-mail:</strong> $email <br>
<strong>Assunto:</strong> $assunto <br>
<strong>Mensagem:</strong> $msg
</font>
</p>
</font>
</body>
</html>
"; //MENSAGEM NO FORMATO HTML
$headers = "MIME-Version: 1.0rn";
$headers .= "Content-type: text/html; charset=utf-8rn";
$headers .= "From: ". $remetente ."rn";
$headers .= "Reply-To: ". $remetente ."rn";
$headers .= "X-Priority: 1rn";
$headers .= "X-MSMail-Priority: Highrn";
$headers .= "X-Mailer: Just My Server";
if (imap_mail($destino, $assunto, ($html_msg), $headers)) {
$data = array('enviado' => true );
echo json_encode($data);
}else{
$data = array('enviado' => false);
echo json_encode($data);
}
Na linha 10 você coloca o email que deseja receber as mensagens. Com isso poderemos testar, porém no servidor local não conseguiremos enviar email. Ao enviar os arquivos do projeto no servidor poderemos realizar este teste, e veremos a mensagem informando que o formulário foi enviado com sucesso. para saber como enviar os arquivos para o servidor veja este artigo. Até a próxima!
Download do Projeto
[wpdm_package id=’464′]
