Olá Pessoal,
No post 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.
Lets go…
Starting…
Vou considerar que você já conhece Spring e Hibernate, portanto o post 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:
@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 post com Jersey e conhece WebService já sabe o que significa essa classe. Em poucas palavras, é através dessa classe 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 posts 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=“http://code.angularjs.org/1.0.6/angular-resource.min.js”></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, 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 inicio 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 inicio 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):
Clique no link Create
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=“button” value=“Save” ng-click=“newOne()” ng-disabled=“customerform.$invalid”/>
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 a 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 post.
Abraços, see ya!!