Olá, pessoal!
No artigo de hoje vamos ver como persistir os dados que estão no nosso front-end com AngularJS em uma base de dados, mas o nosso back-end é Java usando Spring, Hibernate e Jersey.
Vou considerar que você já conhece Spring e Hibernate, portanto o artigo irá direto ao assunto. Veremos apenas como fazer com que os dados do front-end chegue ao back-end. É necessário que você tenha o Jersey no Controller do lado do Server-side. Fiz um post sobre o AngularJS e Jersey.
Antes de começar:
- Crie um projeto webapp, de preferência com o maven;
- Adicione as dependências do Spring, hibernate, banco de dados, Jersey;
- Tenha a lib do angular no projeto ou use a versão online.
O nosso projeto
É muito simples, apenas um formulário que cadastra um customer. Veja o projeto:
Vou considerar que você já tem a camada de serviço DAO e application-context do Spring devidamente criados e funcionando.
Adicionando dependência no pom.xm Jersey-Spring
<dependency> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-spring</artifactId> <version>1.8</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> </exclusions> </dependency>
Configurando o Jersey-Spring
No arquivo web.xml deixe assim:
<servlet> <servlet-name>jersey-servlet</servlet-name> <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <!-- Aqui é o package onde vai ficar o controller --> <param-value>com.camilolopes.jersey.services</param-value> </init-param> <init-param> <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name> <param-value>true</param-value> </init-param> </servlet>
Crie a classe CustomerController.java conforme a seguir (deve ficar no package com.camilolopes.jersey.services):
@Controller @Path("/service") public class CustomerController { @Autowired @Qualifier("customerService") private CustomerService customerService; //substituir pela sua classe de Service gerenciada pelo Spring @GET @Produces(MediaType.APPLICATION_JSON) public List<Customer> getCustomer() { List<Customer> list = customerService.getListCustomers(); return list; } @POST @Consumes(MediaType.APPLICATION_JSON) public void saveCustomer(Customer customer){ customerService.save(customer); } public CustomerService getCustomerService() { return customerService; } public void setCustomerService(CustomerService customerService) { this.customerService = customerService; } }
Se você viu o nosso artigo com Jersey e conhece WebService, já sabe o que significa essa classe. Em poucas palavras, é através dela que o front-end e o back-end se comunicam através de um objeto JSON. Então, quando o angular precisa enviar algo para o back-end, ele vai precisar chamar algum serviço disponível nessa classe, e temos apenas dois: um GET e outro POST.
Criando o app.js
$app = angular.module('app',['ngResource']); $app.config(function($routeProvider,$httpProvider,$locationProvider){ //routes $routeProvider. when('/',{templateUrl:'view/customers.html',controller:customerController}). when('/create',{templateUrl:'view/form.html',controller:customerController}). when('/list',{templateUrl:'view/customers.html',controller:customerController}). otherwise( { redirectTo:'/' }); $httpProvider.responseInterceptors.push(function($q,$rootScope){ return function(promise){ return promise.then(function(response){ return (response); },function(response){ $data = response.data; $error = $data.error; if($error && $error.text){ console.log("ERROR: " + $error.text); } else{ if(response.status=404) console.log("page not found"); } return $q.reject(response); }); }; }); });
Esse código acima apenas cria rotas com base no que for chamado no browser. Por exemplo, se chamarmos /create vai ser carregado a página form.html e o controller customerController que ainda vamos criar.
A outra função é, em caso de erro no response, podermos exibir um conteúdo customizado. A novidade que temos nesse arquivo é:
$app = angular.module('app',['ngResource']);
Lembra que nos artigos anteriores passávamos [] para o segundo parâmetro do module? Agora estamos dizendo que vamos usar ng-resource para conectar a um WebService. Adicione ao seu projeto angular-resource.js ou use a versão online:
<script src=<i>"http://code.angularjs.org/1.0.6/angular-resource.min.js"</i>></script>
Criando customerController.js
Vamos agora criar o controller do angular, portanto crie um arquivo JavaScript chamado customerController.js. Não vamos organizar o projeto, então pode colocar dentro de webapp mesmo:
function customerController($scope,$resource,$location){ //estamos criando um objeto e linkando com o webservice CustomerController.java Customer = $resource("rest/service/"); //estamos criando uma função que carrega todos os customers. $scope.loadAll = function(){ Customer.query( function(data){ $scope.rows = data; }); }; //quando chamado cria um novo customer e salva. $scope.newOne = function() { var c = new Customer(); //estou atribuindo o nome digitado ao atributo do domain Customer. c.name = $scope.nameCustomer; c.$save(); }; }
No início parece estranho, mas é porque é diferente do que estamos acostumados. Coloquei a explicação em modo de comentário inline para facilitar o entendimento. Não se preocupe se no início se sentir desconfortável com a estrutura, também me senti, mas com o tempo fui aprendendo melhor e vendo que faz sentindo a forma que o angular trata o binding.
Criando o arquivo index.html
Vamos carregar as libs, então esse arquivo terá libs e a ng-view:
<html ng-app="app"> <head> <meta charset="UTF-8"> <script src="http://code.angularjs.org/1.0.6/angular.min.js"></script> <script src="app.js"></script> <script src="customerController.js"></script> </head> <body> <a href="#/create">Create</a> <a href="#/list">List</a> <script src="http://code.angularjs.org/1.0.6/angular-resource.min.js"></script> <div ng-view></div> </body> </html>
Criando o form.html
<body ng-controller="customerController"> <h2>Register Customer</h2> <form name="customerform"> <div> <label>Name</label> <input name="name" ng-model="nameCustomer" require/> {{nameCustomer}} </div> <div> <label>Phone</label> <input name="phone" ng-model="phoneCustomer" /> {{phoneCustomer}} </div> <div> <label>E-mail</label> <input name="email" ng-model="emailCustomer" type="email" required /> {{emailCustomer}} </div> <div> <input type="button" value="Save" ng-click="newOne()" ng-disabled="customerform.$invalid"/> </div> </form> </body>
Criando customers.html
Aqui vamos listar os customers cadastrados:
<div ng-init="loadAll()"> <table id="tableData"> <thead> <tr> <th>ID</th> <td>Nome</td> </tr> </thead> <tbody> <tr ng-repeat="row in rows"> <td>{{row.id}}</td> <td>{{row.name}}</td> </tr> </tbody> </table> </div>
Observe que temos ng-init. Essa diretiva invoca o método que carrega todos os customers. Em seguida temos a diretiva ng-repeat que funciona como um forEach do Java rows; é a variável que criamos no customerController.js.
Testando
Assumindo que seu BD está up, suba a aplicação (caso tenha criado um maven Project, apenas digite mvn tomcat:run via linha de comando):
Observe a url e o botão save. A url tem alguma relação com as rotas que criamos? E o botão save, por que está desabilitado? Ele está desabilitado porque fizemos isso no form.html:
<input type=<i>"button"</i> value=<i>"Save"</i> ng-click=<i>"newOne()"</i> ng-disabled=<i>"customerform.$invalid"</i>/>
Olhe para a diretiva ng-disabled. Ali estamos dizendo que se o formulário for invalid desabilite o botão. E o que é um formulário inválido? Nesse caso, se os campos que marcamos como required não estiverem preenchidos, é um form inválido. Observe que quando preencher os campos requeridos, automaticamente o botão fica habilitado:
Clique em save. Se clicar mais de uma vez acontece isso:
Click no link List. Poderiamos redirencionar usando $location.path(“nomeDaRota”);
Apesar de termos vários outros atributos no form, salvamos apenas o name. Mas sinta-se à vontade em praticar e salvar os demais. Conferindo no banco:
Pronto. Salvando dados no BD com AngularJS, Spring, Jersey, Hibernate. Simples, não?
O projeto completo está no meu GitHub no repositório dedicado às minhas brincadeiras com angularJS: https://github.com/camilolopes/workspaceAngularJs
Sempre estarei subindo projetos novos, brincando com o framework… Se quiser acompanhar basta me seguir no github.
Enfim, espero que tenham gostado do artigo.
Abraços!