Front End

14 mar, 2014

Série AngularJS: Aplicação JEE com AngularJS – Parte 06

Publicidade

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:

  1. Crie um projeto webapp, de preferência com o maven;
  2. Adicione as dependências do Spring, hibernate, banco de dados, Jersey;
  3. 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:

angularjcustomerproject

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:

&lt;script src=<i>"http://code.angularjs.org/1.0.6/angular-resource.min.js"</i>&gt;&lt;/script&gt;

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):

angularjscustomerrunning

Clique no link Create:angularjscustomercreate

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:

&lt;input type=<i>"button"</i> value=<i>"Save"</i> ng-click=<i>"newOne()"</i> ng-disabled=<i>"customerform.$invalid"</i>/&gt;

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:

angularjscustomerformvalid

Clique em save. Se clicar mais de uma vez acontece isso:

angularjscustomerlist

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:

angularjscustomerbd

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!