# Módulo AngularJS

{% embed url="<https://www.youtube.com/watch?v=psL6M4vwKrQ>" %}

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.

```php
// arquivo Controllers/MeuPlugin.php
    function GET_itens () {
        $this->requireAuthentication();
        $this->render('items');
    }
```

```php
// arquivo views/meu-plugin/items.php
<div class="main-content">
    <?php $this->part('meu-plugin/items-list'); ?>
</div>
```

```markup
// 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.

```javascript
// 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:

```php
// 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`:

```php
// 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`:

```markup
// 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:

```javascript
// 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:

```markup
// 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:

```javascript
// 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:

```javascript
// 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`:

```javascript
// 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:

```javascript
// 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:

```markup
// 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}`.

```javascript
// 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:

```javascript
// 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*:

```markup
// 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}`:

```javascript
// 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.

```javascript
// 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:

```markup
// 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.&#x20;

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:

```javascript
// 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:

```markup
// 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mapasculturais.gitbook.io/documentacao-para-desenvolvedores/formacao-para-desenvolvedores/modulo-angularjs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
