Esse capítulo aborda a criação de um módulo AngularJS completo, contendo controlador, serviço CRUD e interface para listagem, criação, modificação e deleção de objetos de uma entidade.
A implementação abordada é continuação do plugin criado no capítulo anterior. O CRUD será em cima da entidade Item.
Antes da abordagem da implementação do módulo em si, vamos criar uma base para testes. Uma rota /meu-plugin/itens, um arquivo de visão e um template part com o template angular da listagem.
Primeiro, deve-se criar um arquivo JS com o esqueleto do módulo contendo controller e service vazios. É necessário modificar o conteúdo das requisições para que o Angular não envie um JSON no corpo da requisição, pois o Slim não vai parsear corretamente.
// arquivo assets/js/meu-plugin/ng.module.item.js(function (angular) {var module =angular.module('module.item', ['ngSanitize']);// modifica as requisições POST para que sejam lidas corretamente pelo Slimmodule.config(['$httpProvider',function ($httpProvider) {$httpProvider.defaults.headers.post['Content-Type'] ='application/x-www-form-urlencoded;charset=utf-8';$httpProvider.defaults.headers.put['Content-Type'] ='application/x-www-form-urlencoded;charset=utf-8';$httpProvider.defaults.headers.common['X_REQUESTED_WITH'] ='XMLHttpRequest';$httpProvider.defaults.transformRequest=function (data) {var result =angular.isObject(data) &&String(data) !=='[object File]'?$.param(data) : data;return result; }; }]);// Seriço que executa no servidor as requisições HTTPmodule.factory('ItemService', ['$http',function ($http) {return {}; }]);// Controlador da interfacemodule.controller('ItemController', ['$scope','ItemService',function ($scope, ItemService) {$scope.data = {}; }]);})(angular);
Enfileirando Arquivo JS
Com o arquivo JS do módulo criado, é necessário que ele seja incluído na visão. Isto pode ser feito de diferentes maneiras, mas para este exemplo, faremos o enfileiramento no próprio arquivo do template part:
// layouts/parts/meu-plugin/items-list.php<?php $this->enqueueScript('app',// grupo de scripts'ng-module-item',// nome do script'js/meu-plugin/ng.module.item.js',// arquivo do script [] // dependências do script);?><div><h1>Lista de itens</h1><ul><li>Item 1-<a>remover</a></li><li>Item 2-<a>remover</a></li></ul></div>
Chamando o Módulo no Template
O AngulaJS precisa de um elemento com a diretiva ng-app que indica qual o módulo principal da página. Como utilizaremos o módulo numa nova rota que ainda não tem nenhum ng-app definido, colocaremos a propriedade na tag com a classe main-content:
// arquivo views/meu-plugin/items.php<div ng-app="module.item"class="main-content"><h1> Lista de itens </h1><?php $this->part('meu-plugin/items-list'); ?></div>
Definido o ng-app, podemos chamar o controlador no arquivo do template part colocando a diretiva ng-controller na div que envolve o ul:
Em seguida, fazemos a iteração no template utilizando a diretiva ng-repeat na tag li:
// layouts/parts/meu-plugin/items-list.php
<div ng-controller="ItemController">
<h1>Lista de itens</h1>
<ul>
<li ng-repeat="item in data.items">{{item.title}} - <a>remover</a></li>
</ul>
</div>
Para testar a listagem é necessário, nesse ponto, inserir algumas entradas na tabela item.
Recuperando Itens do Banco de Dados
Vamos implementar uma função no serviço que consulta a API de busca da entidade item:
// arquivo assets/js/meu-plugin/ng.module.item.js// Seriço que executa no servidor as requisições HTTPmodule.factory('ItemService', ['$http',function ($http) {return {find:function(keyword) {var endpoint =MapasCulturais.baseURL +'api/item/find';var params = {'@select':'*'};// para possibilitar filtro if (keyword) {params.title ='ILIKE(*'+ keyword +'*)'; }return$http.get(endpoint, {params}); } };}]);
Em seguida, removemos os itens fakes do array de itens, implementamos função no controlador que chama a função find do serviço e chamamos essa função no controller para trazer os itens da API:
// arquivo assets/js/meu-plugin/ng.module.item.js// Controlador da interfacemodule.controller('ItemController', ['$scope','ItemService',function ($scope, ItemService) {$scope.data = { items: [] };// busca itens na api$scope.findItems=function (keyword) {ItemService.find(keyword).then(function(result) {$scope.data.items =result.data; }); }// faz a busca assim que carrega a página$scope.findItems();
Inserindo Itens
No serviço, criamos uma função insert que faz uma requisição POST em /item:
// arquivo assets/js/meu-plugin/ng.module.item.js// Serviço que executa no servidor as requisições HTTPmodule.factory('ItemService', ['$http',function ($http) { . . .// faz uma requisição do tipo POST insert:function(item) {var endpoint =MapasCulturais.createUrl('item','index');return$http.post(endpoint, item); },
No controlador, adicionamos uma propriedade para guardar o título do novo item, criamos uma função que utiliza essa variável para criar um item e enviar para o serviço:
Antes da listagem, adicionamos um formulário para criação de novo item:
// layouts/parts/meu-plugin/items-list.php
<div ng-controller="ItemController">
<h1>Lista de itens</h1>
<form>
<input type="text" placeholder="Título do novo item" ng-model="data.newItemTitle">
<button ng-click="insertItem()">criar novo item</button>
</form>
Deletando Itens
No serviço, criamos uma função remove que faz uma requisição http do tipo DELETE para /item/single/{id}.
// arquivo assets/js/meu-plugin/ng.module.item.js// Serviço que executa no servidor as requisições HTTPmodule.factory('ItemService', ['$http',function ($http) {return { . . .// faz uma requisição DELETE para deletar um itemremove:function (item) {var endpoint =MapasCulturais.createUrl('item','single', [item.id]);return$http.delete(endpoint); }
No controlador, criamos uma função $scope.removeItem que chama a função remove do serviço:
No controlador, adicionamos uma nova chave no $scope.data para guardar uma cópia do item que será editado.
E, adicionamos duas funções no $scope, a primeira que faz a cópia do objeto e a segunda que salva o objeto copiado.
// arquivo assets/js/meu-plugin/ng.module.item.js module.controller('ItemController', ['$scope','ItemService',function ($scope, ItemService) {$scope.data = { newItemTitle:'', editItem:null, items: [] };// copia o item para edição$scope.editItem=function (item) {$scope.data.editItem =angular.copy(item); }// modifica um item$scope.updateItem=function(item) {ItemService.update($scope.data.editItem).then(function (result) {var index =$scope.data.items.indexOf(item);if (index >-1) {$scope.data.items[index] =result.data;$scope.data.editItem =null; } }); }
No template, modificamos o conteúdo da tag li adicionando um formulário e um link para edição que chama a função $scope.editItem. Esta, faz uma cópia do item para o $data.editItem fazendo com que a condição para exibição do formulário seja verdadeira e que assim, o formulário é exibido: