Módulo AngularJS
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.
// arquivo Controllers/MeuPlugin.php
function GET_itens () {
$this->requireAuthentication();
$this->render('items');
}
// arquivo views/meu-plugin/items.php
<div class="main-content">
<?php $this->part('meu-plugin/items-list'); ?>
</div>
// layouts/parts/meu-plugin/items-list.php
<div>
<h1>Lista de itens</h1>
<ul>
<li>Item 1 - <a>remover</a></li>
<li>Item 2 - <a>remover</a></li>
</ul>
</div>
Esqueleto do Módulo
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 Slim
module.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 HTTP
module.factory('ItemService', ['$http', function ($http) {
return {};
}]);
// Controlador da interface
module.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
:
// arquivo layouts/parts/meu-plugin/items-list.php
<div ng-controlle="ItemController">
<h1>Lista de itens</h1>
<ul>
<li>Item 1 - <a>remover</a></li>
<li>Item 2 - <a>remover</a></li>
</ul>
</div>
Listando Itens
Vamos primeiro colocar no controller alguns itens fakes no objeto $scope.data
para utilizarmos na listagem:
// Controlador da interface
module.controller('ItemController', ['$scope', 'ItemService', function ($scope, ItemService) {
$scope.data = {
items: [
{id: 1, title: 'Título 1'},
{id: 2, title: 'Título 2'}
]
};
}]);
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 HTTP
module.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 interface
module.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 HTTP
module.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:
// arquivo assets/js/meu-plugin/ng.module.item.js
// Controlador da interface
module.controller('ItemController', ['$scope', 'ItemService', function ($scope, ItemService) {
$scope.data = {
newItemTitle: '',
items: []
};
$scope.insertItem = function () {
if($scope.data.newItemTitle) {
var item = { title: $scope.data.newItemTitle };
ItemService.insert(item).then(function (result) {
$scope.data.items.push(result.data);
$scope.data.newItemTitle = '';
});
}
}
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 HTTP
module.factory('ItemService', ['$http', function ($http) {
return {
.
.
.
// faz uma requisição DELETE para deletar um item
remove: 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:
// arquivo assets/js/meu-plugin/ng.module.item.js
module.controller('ItemController', ['$scope', 'ItemService', function ($scope, ItemService) {
.
.
.
// deleta um item
$scope.removeItem = function(item) {
ItemService.remove(item).then(function () {
var index = $scope.data.items.indexOf(item);
if (index > -1) {
$scope.data.items.splice(index, 1);
}
});
}
E, chamamos ela no click do link remover:
// 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 ng-click="removeItem(item)">remover</a></li>
</ul>
</div>
Modificando Itens
No serviço, criamos uma função update
que faz uma requisição http do tipo PUT para /item/single/{id}
:
// arquivo assets/js/meu-plugin/ng.module.item.js
module.controller('ItemController', ['$scope', 'ItemService', function ($scope, ItemService) {
.
.
.
// atualiza um item
update: function(item) {
var endpoint = MapasCulturais.createUrl('item', 'single', [item.id]);
return $http.put(endpoint, item);
},
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:
// layouts/parts/meu-plugin/items-list.php
<li ng-repeat="item in data.items">
<p ng-if="data.editItem.id != item.id">
{{item.title}}
- <a ng-click="editItem(item)">editar</a>
- <a ng-click="removeItem(item)">remover</a>
</p>
<form ng-if="data.editItem.id == item.id">
<input type="text" ng-model="data.editItem.title" />
<button ng-click="updateItem(item)">salvar</button>
<button ng-click="data.editItem=null">cancelar</button>
</form>
</li>
Adicionando Campo de Busca
Como já havíamos possibilitado as buscas no método find do serviço, só precisamos modificar o controlador e o template.
No controlador adicionamos uma nova chave no objeto $scope.data
para guardar o termo buscado, além de uma função que efetua a busca:
// arquivo assets/js/meu-plugin/ng.module.item.js
// Controlador da interface
module.controller('ItemController', ['$scope', 'ItemService', function ($scope, ItemService) {
$scope.data = {
newItemTitle: '',
editItem: null,
items: [],
search: ''
};
$scope.searchItems = function () {
$scope.findItems($scope.data.search);
}
No template colocamos um formulário para a busca:
// layouts/parts/meu-plugin/items-list.php
<form>
<input type="text" ng-model="data.search"/>
<button ng-click="searchItems()">buscar</button>
</form>
Incluindo Módulo em Outras Páginas
@todo
Last updated
Was this helpful?