Neste artigo eu vou mostrar como criar uma aplicação ASP .NET Single Page Application usando os recursos do AngularJS com Web API: criando um serviço.
Na quinta parte do artigo criamos o serviço definido em filmeService de forma que encapsulasse as funcionalidades da nossa Web API representada pelo controller FilmeController.
Neste artigo, vamos implementar as funcionalidades para deletar, editar e criar filmes em nossa aplicação SPA.
Recursos usados:
- Visual Studio 2015 Community
- AngularJS
- Web API
- Entity Framework
Nota: Baixe e use a versão Community 2015 do VS . Ela é grátis e é equivalente a versão profissional.
Deletando filmes
Agora vamos permitir que o usuário possa deletar um filme em nossa aplicação. Para isso, abra a solução Filmoteca, criada no artigo anterior no VS Community 2015 (Open Project). Depois abra o arquivo lista.html e inclua a linha de código para incluir um botão de comando, conforme o código a seguir:
<div ng-controller="listaController"> <table class="table"> <tr> <th>Título</th> </tr> <tr ng-repeat="filme in filmes"> <td>{{filme.Titulo}}</td> <td> <a class="btn btn-primary" href="#/detalhes/{{filme.Id}}">Detalhes</a> <button class="btn btn-danger" ng-click="deleta(filme)">Deletar</button> </td> </tr> </table> </div>
Nossa view lista.html está usando alguns recursos do bootstrap para definir estilos para o button Deletar e para o link Detalhes.
Nesta view, incluímos um button HTML onde temos uma diretiva ng-click que aciona a function deleta(filme), passando o filme associado.
Precisamos, então, alterar o código do controller listaController incluindo a function deleta(filme) conforme mostra o código a seguir:
(function (app) { var listaController = function ($scope, filmeService) { filmeService .getFilmes() .success(function (data) { $scope.filmes = data; }); $scope.deleta = function (filme) { filmeService.deletar(filme) .success(function () { removerFilmePorId(filme.Id); }); }; var removerFilmePorId = function (id) { for (var i = 0; i < $scope.filmes.length; i++) { if ($scope.filmes[i].Id == id) { $scope.filmes.splice(i, 1); break; } } }; }; app.controller("listaController", listaController) }(angular.module("filmoteca")));
Temos agora duas novas functions no controller listaController. A primeira é a function deleta anexado ao objeto $scope e, por isso mesmo, este método é chamado pela diretiva ng-click que usa o serviço filmeService para deletar o filme.
Quando a chamada é feita com sucesso, é feito uma chamada à function removerFilmePorId(), que não está associada ao objeto $scope e, por isso, é uma implementação privada dentro do controlador. Esta function localiza e deleta o filme pelo seu id no modelo e o remove do array dos filmes.
Abaixo vemos a aparência da view lista.html:
Editando e criando filmes
Vamos agora implementar a edição e a criação de um novo filme. Podemos fazer isso de diversas formas e neste exemplo vamos adotar a seguinte abordagem:
- No arquivo lista.html, que exibe a lista de filmes e o botões – Detalhes e Deletar – vamos incluir um novo botão – Novo Filme – e vincular a esse botão uma diretiva ng-click que chama uma function criar();
- Vamos criar, então, um novo arquivo HTML, chamado edita.html, que vai funcionar como um formulário para criar um novo filme e para editar um filme existente. Este arquivo funciona como uma partial view e deverá ser incluído na view lista.html e também na viewdetalhes.html usando a diretiva ng-include.
Dessa forma, a view edita.html vai compartilhar a funcionalidade de incluir um filme e editar um filme.
Clique com o botão direito do mouse na pasta Html e, a seguir, em Add -> New Item e selecione o template HTML page e informe o nomeedita.html. A seguir, inclua o código abaixo nesta view:
<div ng-controller="editaController"> <form ng-show="isEditavel()"> <fieldset> <div class="form-group"> <label for="titulo"> Titulo </label> <input id="titulo" type="text" ng-model="editar.filme.Titulo" required class="form-control" /> </div> <div class="form-group"> <label for="anoLancamento"> Ano de Lançamento </label> <input id="anoLancamento" type="number" ng-model="editar.filme.AnoLancamento" required min="1900" max="2050" class="form-control" /> </div> <div class="form-group"> <label for="duracao"> Duração </label> <input id="duracao" type="number" ng-model="editar.filme.Duracao" required min="10" max="500" class="form-control" /> </div> <button class="btn btn-primary" ng-click="salvar()">Salvar</button> <button class="btn btn-danger" ng-click="cancelar()">Cancelar</button> </fieldset> </form> </div>
É muito importante entender esta view :
- Nesta view, usamos a diretiva ng-controller=”editaController”, que indica que o controlador editaController é responsável pelas functions usadas nesta view. Como este controlador ainda não existe, vamos ter que criá-lo posteriormente;
- A diretiva ng-show é usada para ocultar ou mostrar uma seção do DOM baseado na expressão usada: ng-show=”isEditavel()” que deve retornar true ou false; Neste caso, o elemento form somente será exibido quando a function isEditavel() retornar true a partir do model; Estamos usando esta diretiva para ocultar o formulário, exibindo-o somente para criar ou editar um novo filme;
- A diretiva ng-model define o two-way-binding entre o model e os elementos do formulário (textarea, select, input, etc); No formulário, definimos as seguintes diretivas ng-model:
- ng-model=”editar.filme.Titulo”
- ng-model=”editar.filme.AnoLancamento”
- ng-model=”editar.filme.Duracao”
Fazendo assim, criamos essas propriedades no objeto $scope e permitimos que elas sejam acessadas pelo controlador editaController que iremos criar.
Perceba que o objetivo das diretivas é fazer a vinculação entre o model e a view. O model (ou controller) nunca manipula diretamente os elementos DOM; quem faz isso são as diretivas. Fazendo uma alteração no model ela se reflete na view.
Vamos, então, alterar a view lista.html incluindo o botão para criar um novo filme e usar a diretiva ng-include para incluir a view edita.html:
<div ng-controller="listaController"> <table class="table"> <tr> <th>Título</th> </tr> <tr ng-repeat="filme in filmes"> <td>{{filme.Titulo}}</td> <td> <a class="btn btn-primary" href="#/detalhes/{{filme.Id}}">Detalhes</a> <button class="btn btn-danger" ng-click="deleta(filme)">Deletar</button> </td> </tr> </table> <button class="btn btn-success" ng-click="criar()">Novo Filme</button> <div ng-include="'/Cliente/Html/edita.html'"></div> </div>
Agora a view lista.html pode invocar o método criar() que será definido no model.
A diretiva ng-include faz a inclusão da view edita.html nesta view, exibindo o formulário para o usuário digitar as informações do novo livro.
Observe que usamos aspas simples(”) no interior da diretiva para chamar a página edita.html, assim asseguramos que o caminho informado será reconhecido como um literal string.
A function criar() definida deverá ser criada no controlador listaController, que é responsável por esta view. Altere o arquivolistaController.js incluindo a function criar():
(function (app) { var listaController = function ($scope, filmeService) { filmeService .getFilmes() .success(function (data) { $scope.filmes = data; }); $scope.criar = function () { $scope.editar = { filme: { Titulo: "", Duracao: 0, AnoLancamento: new Date().getFullYear() } }; }; $scope.deleta = function (filme) { filmeService.deletar(filme) .success(function () { removerFilmePorId(filme.Id); }); }; var removerFilmePorId = function (id) { for (var i = 0; i < $scope.filmes.length; i++) { if ($scope.filmes[i].Id == id) { $scope.filmes.splice(i, 1); break; } } }; }; app.controller("listaController", listaController) }(angular.module("filmoteca")));
Da mesma forma, vamos alterar a view detalhes.html para incluir um botão de comando vinculado a diretiva ng-click para editar os dados e outro para retornar para a lista. Vamos usar também a diretiva ng-include para incluir a view edita.html na view detalhes para exibir o formulário para editar os dados. Abaixo temos o código da view detalhes.html alterado:
<div ng-controller="detalhesController"> <h2> Filme : {{filme.Titulo}} </h2> <div> <h3> Lançamento em : {{filme.AnoLancamento}} </h3> </div> <div> <h3>{{filme.Duracao}} minutos de duração.</h3> </div> <a class="btn btn-primary" ng-click="editar()">Editar</> <a class="btn btn-success" href="#/">Retorna</a> <div ng-include="'/Cliente/Html/edita.html'"></div> </div>
Para concluir esta parte do artigo, vamos definir a function editar() no controlador detalhesController, conforme mostra o código a seguir:
(function (app) { var detalhesController = function ($scope, $routeParams, filmeService) { var id = $routeParams.id; filmeService .getFilmePorId(id) .success(function (data) { $scope.filme = data; }); $scope.editar = function () { $scope.editar.filme = angular.copy($scope.filme); }; }; app.controller("detalhesController", detalhesController); }(angular.module("filmoteca")));
A função editar() apenas faz uma cópia do filme selecionado no escopo para a propriedade filme, atualizando a view e exibindo os dados do filme selecionado para edição.
Executando o projeto agora, teremos o seguinte resultado:
1. A página inicial exibindo a view lista.html:
2. A exibição da view detalhes.html quando o usuário clica no botão Detalhes:
3. A exibição do formulário representado pela view edita.html, exibindo os dados do filme selecionado quando o usuário clica no botão Editar:
4. A exibição do formulário representado pela view edita.html quando o usuário clica no botão Novo Filme:
Na próxima parte do artigo, vamos implementar as funcionalidades do controlador editaController e definir as functions para criar um novo filme e alterar dados do filme selecionado.