Anda di halaman 1dari 47

29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .

NET Coders

Início

Acesso A Dados Arquitetura Automação Banco De Dados Controle De Versão Desenvolvimento

Infraestrutura Metodologias Mobile Serviços Testes Web Windows

Você Está Em : Home » ASP.NET MVC 5 » SPA Utilizando ASP.NET MVC 5 – Parte 2.0

SPA Utilizando ASP.NET MVC 5 – Parte Encontre Um Artigo

2.0 Pesquisar
 Erick Wendell Gomes da Silva        24/03/2015      ASP.NET MVC 5      11 Comentários

Últimos Artigos
Recapitulando…

Js Advice – Analisador de código JavaScript


Vimos no artigo anterior à criação da estrutura do projeto, utilizando o pattern de camadas.
Exploratory Testing (Preview) – Extensão
Caso ainda não tenha visto o artigo anterior acesse.
Google Chrome

Neste artigo veremos os seguintes temas: Executando .NET Core Apps no Docker

Criando Web.Conᒈg Seguros com


Review do ASP.NET MVC ProtectedConᒈgurationProvider

Consumo de Dados em JSON com JQuery Ajax. Debugging ASP.NET MVC Com Glimpse

Utilização do plugin POST MAN do Google Chrome. Testes de performance com ApacheBench

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 1/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Observações: Padronizando as Respostas JSON no ASP.NET


MVC

Assim como no artigo anterior, utilizaremos o Visual Studio 2013 SP4. Faremos todo o projeto com Novidades do Visual Studio “15”: abertura de
métodos “na unha”. Para ᒈxar o uso de sintaxe e semântica das ferramentas utilizadas. pastas
Vamos agora iniciar a criação da parte web do nosso projeto. Clique com o botão direito do mouse, Angular 2 x React Js
em Controllers, selecione Add > Controller > MVC5 Controller Empty > Add.
Novidades do C# 7 – Local Functions

Imagem 1 – Criação da AppController Entenda o que é Data Parallelism & Task


Parallelism.

Novidades do C# 7 – Ref Returns

Implementando o Design Pattern Repository


e Unit Of Work com Entity Framework

Criando a primeira aplicação ASP.NET CORE


1.0 no Linux Ubuntu – Parte 2

Utilizando o ASP.NET Core RC2 no Visual


Studio 2015

Tags

.NET Core .NET Native AForge.NET Android SDK

Angular AOP ApacheBench apps hibridos


Arquitetura de serviços Asp.net ASP.NET
5 ASP.NET Core ASP.NET Core RC2 ASP.NET
MVC ASP.NET MVC 5 ASP.NET
MVC 6 ASP.NET Web API
ASP.NET WEB API 2.2 Automação Azure SQL
Imagem 1 – Escolha o Nome para a controler, digite AppController e clique em Add.
Backup BAckup Hyper-V BACPAC BDD Binary Literals
Bot Bot Framework Bower BPM BPMN BSON
Imagem 2 – AppController
Business Intelligence C# C# 5.0 C#
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 6.0 2/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

6.0 C# 7 C# 7.0 Cache Captcha Chrome Chrome


DevTools Client-side Code Analysis Code
Cracker Code First Code Snippets Cordova CSS
Data-tier Application Data Binding DDD
dependency inversion principle design
patterns Digit Separators dip DNVM DNX
Docker Entity Framework 6 F# Ferramentas
Free Firebug Foreign Key Front-End GitHub Google
Play Gulp Hyper-V ICommand

INotifyPropertyChanged ionic framework


JavaScript JSON Json.NET Linguagem
Funcional Linux Load Test Local Functions Log4Net

Microsoft Test Manager Migração Mocking


MongoDB Mono MonoDevelop Moq MVVM

NDepend NoSQL npm Nuget NUnit OCR Parallel


Pattern Matching PostSharp Primary Key ReactJs
Refactoring Ref Returns Reporting Services
Imagem 2 – No exemplo temos a AppController com a Action padrão. A Controller será quem REST RESTful Restore VM RestSharp Rx.NET
trabalhará com o Javascript/Ajax para transitar os dados e a o retorno ActionResult será  o retorno de Segurança Selenium Serviços SOA SOAP solid
nossos dados. Neste ponto criaremos o primeiro método chamado Cadastrar, receberá uma
SPA SpecFlow SqlPackage SQL Server SQL
PessoaModel como parâmetro.
Server 2012 SQL Server 2014 SQL Server
Database Engine Tuning Advisor SQL Server Proᒈler
IMPORTANTE: Veremos nos passos a seguir que não trataremos os possíveis erros, os nossos
Stress Test Tag Helpers Task TDD Tesseract
try/catch foram colocados a ᒈm de mostrar que você pode tratar seus erros, mas neste artigo não
Testes Testes de performance Thread
será abordado sobre a correção dos mesmos.
Transact-SQL Try/Catch Typescript
Listagem 1 – Criação de Método de Cadastro em AppController Ubuntu Universal Apps UWP Visual Studio

Visual Studio "15" Visual


1
2
//importamos tambem as namespaces para manipular as classes
using JqueryAjax.AspNetMVC.CRUD.Models.Model; Studio 2015 WCF Web API
3 using JqueryAjax.AspNetMVC.CRUD.Models.Negocio; Windows 10 Windows Backup Windows SDK
4 using System;
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ xamarin Xamarin3/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

4 using System; Windows Server 2012R2 wpf xamarin Xamarin


5 using System.Collections.Generic;
6 using System.Linq; Forms Xaml XML XUnit xUnit.net
7 using System.Web;
8 using System.Web.Mvc;
9  
10         //Informamos no Atributo que será uma requisição do tipo Post Redes Sociais
11  
12         [HttpPost] História do Grupo
13  
14         public void Cadastrar(PessoaModel pessoa) Palestras e Mini Cursos
15         { Linkedin
16             try
17             { Facebook
18                 new PessoaNeg().Cadastrar(pessoa);
19             }
20             catch (Exception)
21             { Patrocinadores
22                 throw;
23             }
24         }

Listagem 2  – Criação de View Index

1         //action padrao da AppController
2         public ActionResult Index()
3         {
4             return View();
5         }

Clicando com o botão direito em cima de View() cliente em Add View.

Imagem 3 – Criação de View Index

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 4/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Parceiros

Feito isso clique em Add e aguarde o Scaඬolding (Template gerado pelo Visual Studio com todos itens
padrões (pastas, arquivos e etc)).

Imagem 4 – View Index


Vídeo Aulas

ComunidadeNetCoders
8 vídeos

Inscrever­se 654

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 5/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

eXcript
578 vídeos

Inscrever­se 999+

Curta Nossa Página

NET Coders
25.981 curtidas

Curtir Página Cadastre­se

Seja o primeiro de seus amigos a curtir isso.

Imagem 4 – Repare que em Solution Explorer  foram adicionadas pastas como:

Content - Onde ᒈcarão seus arquivos CSS, por padrão já adicionou o bootstrap para nós.

Fonts - Fontes padrão do seu site.

Scripts – Por padrão, adicionou os arquivos do e Bootstrap.

Views>App – onde adicionaremos todo nosso SPA.

Views>Shared – todo conteúdo que será compartilhado entre as outras páginas é adicionado
nesta pasta, como LayoutPadrao, e páginas reutilizáveis e etc.

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 6/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

A primeira coisa a se fazer, será em Views > Shared > _Layout e adicionar as seções conforme a
Imagem 5 e 6.  Deᒈniremos que os trechos com aquela seção serão renderizados na ordem
deᒈnida. É boa prática usá-lo sempre em suas aplicações, para evitar scripts que não são utilizados
em outras paginas sejam carregados ou fazer do uso de Bundles (este não faremos uma analogia
neste artigo, para saber mais, acesse http://goo.gl/w72WQ0 e veja os detalhes deste assunto).
Seguindo o RenderSecton, o primeiro parâmetro será o nome da seção, e o segundo informa se
todas as páginas devem conter uma seção com este nome.

Imagem 5 – RenderSection(“styles”, false) – Para arquivos CSS

Imagem 6 – RenderSection(“scripts”, false) – Para arquivos Javascript

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 7/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Em Views > App > Index, iremos abrir novamente nosso arquivo Index.cshtml, faremos um formulário
com as mesmas propriedades deᒈnidas em PessoaModel

Listagem 3 – Index.cshtml – Cadastrar

1 @{
2     ViewBag.Title = "Index";
3 }
4  
5 <!‐‐ Seção scripts definida como RenderSection("scripts", false) em ~/Views/Shared/_Layout.cshtml ‐‐>
6 @section scripts{
7     <!‐‐Ainda nao criamos este script, criaremos na proxima etapa ‐‐>
8     <script src="~/Scripts/App/CrudApp.js"></script>
9 }
10  
11 @section styles{
12     <style>
13         /*usado apenas para abaixar a borda em 50px*/
14         body {
15             margin: 50px;
16         }
17     </style>
18 }
19  
20 <section id="conteudo" class="container">
21     <!‐‐ Usando as classes css do Bootstrap ‐‐>
22     <div class="form‐group">
23         <section id="formulario">
24  
25            <!‐‐ Formulário responsável por adicionar e atualizar pessoas ‐‐>
26             <form action="/" method="post" id="formDados">
27  
28                <!‐‐ usamos este tipo hidden para armazenar nosso Id ‐‐>
29                 <input type="hidden" name="Id" id="idPessoa" />
30                 <div class="form‐group">
31                     <label for="nome">Nome</label>
32                     <input type="text" id="nome" class="form‐control" name="nome"
33                 <div class="form‐group">
34                     <label for="DataNascimento">Data de Nascimento</label>
35                     <input type="date" id="DataNascimento" class="form‐control"
36                 <div class="form‐group">
37                     <label for="Email">Email</label>
38                     <input type="Email" id="Email" class="form‐control" name="Email"
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 8/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

38                     <input type="Email" id="Email" class="form‐control" name="Email"
39                 <div class="form‐group">
40  
41                     <!‐‐ função onclick virá do nosso arquivo javascript na proxima etapa ‐‐>
42                     <input type="button" id="salvar" value="Salvar" class="btn btn‐success"
43                 </div>
44           </form>
45         </section>
46    </div>
47 </section>

Listagem 4 – Observe que na seção scripts estamos referenciando um arquivo Javascript que ainda
não existe, então vamos criar este arquivo. No botão Salvar, temos a Função Cadastrar no atributo
onclick, que será criada neste arquivo.

Antes de criar o nosso arquivo, compile e execute seu projeto, pressionando o F5 do teclado.

Imagem 7 – Projeto Rodando no Chrome

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 9/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Imagem 7 – Caso sua aplicação não for para nossa tela do formulário, na URL, digite após o
localhost:PORTA/ coloque App/Index. Onde App é nossa Controller e Index nossa Action.

Podemos conᒈgurar este caminho de URL, para evitar este trabalho manual toda vez que executar
sua aplicação. Na raiz do nosso projeto, abra a pasta AppStart > RouteConඬg.cs conforme a Imagem
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 10/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

8.

Imagem 8 – RouteConᒈg.cs

linha 19 – troque “Home” por “App” , conforme a Listagem 5.

Listagem 5 – Conᒈguração das Rotas

1 defaults: new { controller = "App", action = "Index", id = UrlParameter.Optional }

Neste momento, estamos apontando como padrão a nossa AppController , toda vez que executar o
play no projeto irá para esta Controller como padrão.

Agora, iremos criar uma nova pasta ( New Folder) em Scripts chamada App, clicando com o botão
direito do mouse, vamos a Add > JavascriptFile, com o nome de CrudApp.js.

Imagem 9 – Criação do Arquivo Javascript

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 11/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Antes de começar a codiᒈcação do nosso Javascript, vamos conhecer a ferramenta POSTMAN , uma
extensão do Google Chrome. É muito utilizado quando estamos trabalhando com Ajax ou testar
algum serviço, com ele, faremos testes para saber se estamos chamando a URL certa, para o Debug
em nossa aplicação.

Imagem 10 – POSTMAN – http://goo.gl/QQzu7O

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 12/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Imagem 10 – Aqui veremos o POSTMAN após a instalação no navegador Google Chrome, vamos
colocar nossa Url de LocalHost:PORTA/App/Index. Estes dados você consegue na URL do seu
navegador quando executa a aplicação no navegador, copie e cole no campo “Enter request URL
here”.

Imagem 11 – Url Colada em “Enter request URL here”

De volta à nossa AppController.cs em nossa aplicação, clique na chave da Action Index e pressione F9
do teclado, para adicionar um BreakPoint.
Imagem 12 – AppController.cs
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 13/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Dê o play em seu projeto caso não esteja rodando, vá ao Postman e clique em Send.

Feito isso, ele irá disparar e ir até nosso BreakPoint .

Imagem 13 – BreakPoint Ativo

Com isso, veriᒈcamos que nossa URL está correta e nosso projeto pronto para trabalhar com
requisições Ajax. Para ter certeza que funcionou, pressione F5 para aplicação continuar, volte ao
POSTMAN e veriᒈque se está com o Status 200 Ok.

Imagem 14 – Status OK Postman.

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 14/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Voltando ao projeto, em Scripts> App > CrudApp.js  iremos inserir o dois Métodos auxiliares.

Listagem 6 – Métodos Auxiliares

1 function LimparFormulario() {
2     //Limpar formulario
3     //pega todos os itens do form e limpa os campos
4     $('#formDados').each(function () {
5         this.reset();
6     });
7 }
8  
9 //Recebe a classe css para o tipo, seja sucesso ou erro.
10 // passamos como parâmetro SUCCESS(para sucesso) ou DANGER(para erro),
11 //conforme o bootstrap
12 function Mensagem(stringCss, mensagem) {
13     //caso exista uma mensagem, ele remove
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 15/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

13     //caso exista uma mensagem, ele remove
14     $("#mensagem").remove();
15  
16     //serve para limitar um tempo para aparecer a nova mensagem
17     setTimeout(function () {
18          //serve este passo pode não ser a melhor abortagem,
19         //fazemos assim, para que tenha uma familharidade maior com o JQuery.
20         $('#formDados').append("<div class='alert alert‐" + stringCss + "' id=mensagem role=alert>"
21     }, 10);
22 }

No mesmo arquivo, criaremos o método Cadastrar, seguindo a Listagem 7.

Listagem 7 – Criação do Método de Cadastrar com JQuery

1 function Cadastrar() {
2     var dadosSerializados = $('#formDados').serialize();
3     $.ajax({
4         type: "POST",
5         url: "/App/Cadastrar",
6         data: dadosSerializados,
7         success: function () {
8             Mensagem("success", "Cadastrado com Sucesso!");
9         },
10         error: function () {
11              Mensagem("danger", "Erro ao cadastrar!");
12         }
13     });
14 }

Linha 2 – Pegamos o id do nosso formulário via Jquery, usamos a função serialize(), que
transforma nosso formulário em uma série de strings de acordo com o atributo name dos
respectivos Inputs do HTML – para mais informações acesse http://api.jquery.com/serialize/

Linha 7 – Inicializamos a função $.ajax() do Jquery, passando os valores dos seguintes


atributos:

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 16/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

type: Informamos o tipo de requisição, podendo ser: GET, POST, PUT ou DELETE. Em nossos
exemplos usaremos somente GET e POST , para melhor aprendizado.

url: Nosso caminho /Controller/Action criada em nosso NET MVC, para onde enviaremos os
dados do formulário.

data: Serão os dados que enviaremos por parâmetro para a Action,  mandaremos os dados
do nosso formulário.

Observação:  Em AppController no servidor, ela espera como parâmetro uma PessoaModel, então
nosso formulário deve possuir os mesmos atributos encontrados nesta classe, ele será identiᒈcado
no momento da requisição pelos atributos name dos inputs no formulário, caso esteja deᒈnido com
name diferente do deᒈnido na classe PessoaModel o mesmo não será localizado e na Controller,
receberá como valor null.

success: Após sua requisição chegar ao servidor e o mesmo devolver os dados e ou realizar
todas as operações sem nenhuma Exception executará as funções deᒈnidas neste ponto.

error: Caso surgir algum imprevisto, algum erro que não foi tratado, alguma Exception
“Explodiu”, ele entrará neste parâmetro e executará os métodos.

Agora, iremos testar se nossa aplicação está cadastrando nossa “Pessoa”.


Imagem 15 – Cadastrar.cshtml

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 17/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Imagem 16 – Debug AppController

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 18/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Imagem 17 – Mensagem de Sucesso.

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 19/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Caso tudo ocorreu como planejado, vamos a próxima parte de nossa Single Page Application.
Agora que cadastramos os clientes, em nossa lista estática, temos que listar esses dados em uma
tabela, para nosso exemplo, iremos fazer um pouco mais “na unha”, faremos nosso código HTML
ser renderizado a partir do JQuery, criar as listas e adicioná-las à tabela, sem o uso de outra
ferramenta Js. Antes de codiᒈcar o $.ajax(), será uma requisição do tipo GET para recuperar a lista de
todos os usuários cadastrados em nossa aplicação.

Vamos inserir o snippet de código de listagem de pessoas em AppController.cs.

Listagem 8 – Action Listar em AppController.cs

1         [HttpGet]
2         public ActionResult Listar()
3         {
4             var listaPessoas = new PessoaNeg().Listar();
5             return Json(listaPessoas, JsonRequestBehavior.AllowGet);
6         }
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 20/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

6         }

Por padrão as requisições serão do tipo GET, para o exemplo, deᒈnimos o Atributo [HttpGet] acima
da assinatura do método, apenas para você ter um melhor aproveitamento da sintaxe C#, mas caso
fosse uma requisição do tipo POST, seríamos obrigados a colocar o atributo [HttpPost]. Retornamos
um JSON, ou seja, uma lista de objetos Javascript, para o nosso Ajax, liberando acesso para
requisição GET. Sempre que trabalhamos com Ajax e ASP.MVC usamos o retorno JSON para que o
Javascript consiga interpretar nossos objetos

Repare que estamos pedindo ao nosso back-end, um IEnumerable<PessoaModel>, ou seja, uma lista
genérica somente leitura da nossa classe de pessoa convertida para uma lista de objetos Javascript,
para trabalhar em nosso HTML.
Com a extensão do Chrome, PostMan, antes de utilizar nosso javascript, vamos testar se a controller
nos devolve os dados corretos da Lista,  seguindo sua url: localhost:PORTA/App/Listar.

Detalhe importante, caso Esteja deᒈnido POST em frente ao campo URL, clique e mude para GET.

Imagem 18 – Postman retornando a Lista de Pessoas

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 21/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Feito isso, sabemos que está retornando os arquivos que queremos, então vamos ao nosso
javascript manipular estes dados.
Vamos ao nosso CrudApp.js codiᒈcar o código de listagem dos itens na Listagem 9.

Listagem 9 – Código de Listagem em CrudApp.js

1 function Listar() {
2     //chamamos nosso método auxiliar para limpar os campos
3     LimparFormulario();
4     $.ajax({
5         type: "GET",
6         url: "/App/Listar",
7         success: function (dadosPessoa) {
8             if (!dadosPessoa.length == 0) {
9                 $('#tbody').children().remove();
10                 $(dadosPessoa).each(function (i) {
11                     var dataMiliSegundos = dadosPessoa[i].DataNascimento.replace(
12                     var dataNascimento = new Date(parseInt(dataMiliSegundos)).toLocaleDateString();
13                     var tbody = $('#tbody');
14                     var tr = "<tr>";
15                     tr += "<td>" + dadosPessoa[i].Id;
16                     tr += "<td>" + dadosPessoa[i].Nome;
17                     tr += "<td>" + dataNascimento;
18                     tr += "<td>" + dadosPessoa[i].Email;
19                     tr += "<td>" + "<button class='btn btn‐info' onclick=Editar("
20                     tr += "<td>" + "<button class='btn btn‐danger' onclick=Deletar("
21                     tbody.append(tr);
22                 });
23             }
24         }
25     });
26 }

Listagem 10 – Inicializamos nosso $.ajax, como visto anteriormente na Listagem 7, temos algumas
variações para este método:

Não temos o atributo data, pois não enviaremos parâmetros para a Action

url: referenciará a Action Listar, de onde virão nossos dados.

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 22/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

success: aqui, recebemos a resposta do servidor, que no caso é a lista de pessoas que estão
cadastradas em nosso sistema.

$(‘#tbody’).children().remove() – Aqui deᒈnimos que toda vez que chamar o método de


listagem, ele irá remover todos os itens que está dentro da tr para não replicar os dados em
nossa tabela. Esta abordagem pode não ser a melhor prática, está sendo utilizada para ᒈns
de estudo e melhor entendimento do assunto, para entender mais sobre a função children,
acesse https://api.jquery.com/children/ ;

$(dadosPessoa).each – Aqui pegamos os dados (nossa lista), e vamos usar a função do JQuery
each. Ela nos permite trabalhar com cada objeto dentro de uma lista, passando como atributo
o index(este index, será utilizado como as posições de um for, para cada posição desta lista,
ele incrementa este valor e pegamos objeto daquela posição).
dataMiliSegundos – quando este objeto vir do C# ele vem em formato de Milissegundos, junto à
uma string. Removemos esta string e mantemos somente os números, para assim, na linha
debaixo criar uma nova data, passando estes milissegundos.
Nas próximas linhas, iremos adicionar à tbody da nossa tabela existente no HTML (cshtml)
manualmente as linhas dela. Concatenamos os dados que virão do objeto daquela posição, os
botões deᒈnidos com funções para Edição e Deleção (nossos próximos passos) , passando como
parâmetro o Id daquela PessoaModel.
Após isso, com a função append ( http://api.jquery.com/append ), concatenamos toda estrutura à
nossa tabela atual. A cada posição da lista fará esta concatenação e adição de itens.

Vamos colocar um objeto de debugger em nosso código JS dentro da função success para veriᒈcar
em modo debug se os dados estão aparecendo como previsto.

Listagem 11 – Adicionando o debugger em Listar()

1 success: function (dadosPessoa) {
2             if (!dadosPessoa.length ==0) {
3                 debugger;

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 23/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Adicionaremos um breakpoint no método Listar e Cadastrar em nossa Controller

Imagem 19 – Debug Cadastrar em AppController

Imagem 20 – Debug Listar em AppController

Neste ponto, iremos no método Cadastrar em nosso JavaScript, e chamar o Método Listar(), após o
cadastro do mesmo, na função success.

Listagem 12 – Função Cadastrar() adicionar a chamada para Listar no atributo success;

1         success: function () {
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 24/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

1         success: function () {
2             Listar();
3             Mensagem("success", "Cadastrado com Sucesso!");
4         },

Dê um play no projeto ou F5, cadastre uma nova pessoa em seu SPA, veriᒈque se após isso irá
chamar o breakpoint em nossa AppController e depois o debugger no JavaScript. Caso não apareça,
abra as ferramentas do desenvolvedor no Chrome (pressionando F12) e veriᒈque se possui algum
erro.

Imagem 21 – Após Cadastrar no HTML, recebe os dados na AppController > Cadastrar

Imagem 21 – Veriᒈque se os dados que voce digitou no HTML estão sendo recebidos na controller,
aproximando o mouse em em pessoa > apareceram todas as propriedades, o Id não será populado
neste momento. Seguindo com o Botão F11, vá pressionando e navegando nos dados até a adição
desse elemento em nossa lista genérica.

Feito o Cadastro, invocará a Action Listar() conforme  a imagem 22

Imagem 22 – Listar() em AppController

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 25/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Imagem 22  – Após o cadastro, navegue também pressionando F11 veriᒈcando se retorna os dados
corretos.
Usando o Google Chrome, abrirá a Imagem 23.

Imagem 23 – Navegador Chrome com Debugger Javascript

Vá pressionando F10 e veja o comportamento dos métodos.


A pergunta é, porque meus dados não foram populados na tabela HTML?

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 26/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Porque ainda não escrevemos o snippet de código com nossa tabela no HTML!
Voltando à Index.cshtml vamos inserir o seguinte código

Listagem 13 – Criação da tabela

1 <section id="listagem">
2 @* Repare que passamos a classe Hidden do bootstrap,
3  isso acontece para escondermos a tabela enquanto ela não tem dados *@
4   <table class="table hidden" id="tabela">
5     <thead>
6       <tr>
7         <th>Id</th>
8         <th>Nome</th>
9         <th>Data de Nascimento</th>
10         <th>Email</th>
11         <th></th>
12       </tr>
13     </thead>
14     @* Nosso tbody será preenchido em tempo de execução via JQUERY *@
15     <tbody id="tbody"></tbody>
16   </table>
17  </section>

Imagem 24 – Tabela HTML

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 27/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Vamos alterar em nosso Javascript, para na chamada da Listar() ele mostrar o conteúdo da tabela.

Listagem 14 – Método para mostrar a tabela novamente

1 success: function (dadosPessoa) {
2             if (dadosPessoa.length == 0) {
3                 $('table').addClass('hidden');
4             } else {
5                 $('table').removeClass('hidden');
6 ...

Imagem 25 – Listar nossos dados em tabela.

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 28/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Imagem 25 – Repare que só exibimos os dados na tela se nossa Appcontroller retornar um ou mais
itens (função length do Javascript).
Vamos adicionar no Load da página, nosso método de Listar para caso exista algum dado
cadastrado anteriormente, seja exibido.

Imagem 26 – Adicionando Listar após o carregamento da página

Listagem 15 – Implantação de Listagem no carregamento da página


http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 29/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

1 $(function () {
2     Listar();
3 });

Deᒈnimos os métodos que serão chamados após o carregamento da página, então, inserimos
dentro da função nosso método Listar.
Repare que na Imagem 24, em tbody temos apenas a assinatura, isso acontece, pois, vamos inserir
as linhas manualmente via JQuery. Vamos Rodar nosso projeto mais uma vez, cadastrar uma nova
pessoa e veriᒈcar se os dados virão corretamente.

Imagem 27 – Após reiniciar o projeto e adicionar uma nova Pessoa

Nosso cadastro e listagem de pessoas estão funcionando corretamente, agora, precisamos


programar os botões Editar e Deletar. Iniciaremos pelo menos completo, o botão Deletar.
Quando o usuário clicar em Deletar, precisará obrigatoriamente uma conᒈrmação, se ele deseja
realmente apagar aquele dado, por segurança, após a conᒈrmação removeremos o item daquela
linha.

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 30/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Observação. Quando criamos a linha da tabela junto aos botões (Listagem 9), criamos os métodos
mesmo sem utilizar naquele ponto, faremos a implementação destes métodos neste ponto.
Voltando á CrudApp.js iremos criar nossa função Deletar();

Listagem 16 – Criação do Método Deletar

1 function Deletar(idPessoa) {
2     var confirmar = confirm("Deseja Realmente Apagar ?");
3     if (confirmar) {
4             $.ajax({
5                 type: 'POST',
6                 url: "/App/Deletar",
7                 data: { id: idPessoa },
8                 success: function () {
9                     Listar();
10                     Mensagem("success", "Deletado com sucesso!");
11                 },
12                 error: function () {
13                     Mensagem("danger", "Erro ao Deletar!");
14                 }
15             });
16           }
17 }

Listagem 16 – Deᒈnição

Ao click do botão, irá acionar nossa função Deletar, passando o Id daquela pessoa conforme a
Listagem 9  o código Javascript abaixo
Irá exibir um alerta pedindo uma conᒈrmação se realmente deseja deletar, caso clique em OK,
adicionará à Action Deletar enviando o Id daquela pessoa, de uma determinada Linha.
Repare que em nosso Ajax, deᒈnimos:
data: { id: idPessoa }, neste caso, nossa Action recebe somente um parâmetro do tipo Inteiro, temos
que informar a chave id (mesmo nome encontrado no parâmetro na controller MVC) e o valor o Id
recebido por parâmetro.

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 31/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Código Implantado, dê um stop em seu projeto, pressionando Shift F5. Em sua AppController, crie um
método de nome Deletar.

Listagem 17 – Método Deletar

1         public void Deletar(int id)
2         {
3             try
4             {
5                 new PessoaNeg().Deletar(id);
6             }
7             catch (Exception)
8             {
9                 throw;
10             }
11         }

Rode novamente sua aplicação, cadastre uma nova pessoa, tente remover um registro.
Imagem 28 – Conᒈrmação de  Remoção

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 32/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 33/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Imagem 29 – Deletado com Sucesso

Vamos a ultima parte do nosso projeto, editar um registro existente. Voltando à AppController.
Criaremos a Action Editar.

Listagem 18 – AppController.cs – Criação do método de edição de registro.

1         public ActionResult Editar(int id)
2         {
3             try
4             {
5                 //recebemos o id da pessoa selecionada,
6                 //com este id, pedimos ao nosso banco fantasia,
7                 // que retorne a pessoa que possuir aquele id.
8                 var pessoa = new PessoaNeg().GetById(id);
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 34/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

8                 var pessoa = new PessoaNeg().GetById(id);
9  
10                 //para retornar ao ajax, temos que enviar nosso objeto em
11                 //formato JSON, e LIBERA‐LO
12                 //Para a requisicao GET como dito anteriormente
13                 return Json(pessoa, JsonRequestBehavior.AllowGet);
14             }
15             catch (Exception)
16             {
17                 throw;
18             }
19         }

Vamos neste ponto ao arquivo CrudApp.js e adicionaremos a função Editar.Obs. Este método é
chamado pelo botão editar, no atributo onclick do HTML. Adicionamos ele em tempo de execução na
função Listar implementada anteriormente. Vamos codiᒈcar este método que até então não existia.

Listagem 19 – Criação da Função Editar.

1     function Editar(idPessoa) {
2             $.ajax({
3                 type: 'GET',
4                 url: '/App/Editar',
5  
6                 //informamos que nossa controler possui um parametro com nome 'id'
7                 // e enviamos o nosso id que pegamos no botao editar.
8                 data: { id: idPessoa },
9                 success: function (dados) {
10                     // faz a formatacao novamente da data que vem do C# em formado JSON
11                     var dataMiliSegundos = dados.DataNascimento.replace('/Date('
12  
13                     //converte para a data em formato local
14                     var dataFormatoLocal = new Date(parseInt(dataMiliSegundos)).toLocaleDateString();
15  
16                     //como nosso atributo TYPE DATE do HTML
17                     //suporta o somente o formato yyyy‐mm‐dd (ano‐mes‐dia)
18                     //faremos a formatacao do mesmo.
19                     var dataFormatada = "";
20  
21                     //com o metodo SUBSTRING do JAVASCRIPT, pegamos os Indexes da string. no exemplo
22                     //10‐01‐12 ‐ a substring inicia‐se em 0 entao,
23                     //se queremos pegar o valor apenas do dia, será de
24                     //0 a 2 pois no primeiro parametro informamos o indice e no segundo a posicao do valor
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 35/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

24                     //0 a 2 pois no primeiro parametro informamos o indice e no segundo a posicao do valor
25  
26                     dataFormatada += dataFormatoLocal.substring(6, 10) + '‐';
27                     dataFormatada += dataFormatoLocal.substring(3, 5) + '‐';
28                     dataFormatada += dataFormatoLocal.substring(0, 2);
29  
30                     //após voltar os dados do nosso backend, ele insere estes valores
31                     //no input de cada item
32                     $('#idPessoa').val(dados.Id);
33                     $('#nome').val(dados.Nome);
34                     $('#DataNascimento').val(dataFormatada);
35                     $('#Email').val(dados.Email);
36  
37                     //como queremos EDITAR um determinado contato, ocultaremos o botao SALVAR
38                     //e mostraremos o botao ATUALIZAR usando a funcao do JQUERY
39                     $("#salvar").addClass("hidden");
40                     $("#atualizar").removeClass("hidden");
41                 }
42             });
43     }

Repare que não estamos fazendo validações, usando muitos padrões etc etc etc. Estamos fazendo
do modo “na unha”, ou seja, tudo aqui foi pensado na melhor forma de exercitar seu conhecimento
e mostrar como a coisa funciona “por trás dos panos”.
Agora que temos a função editar pronta, coloque um breakpoint em nossa  Action Editar. Rode o
projeto, e vamos aos testes.

Imagem 30 – BreakPoint Editar

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 36/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Veriᒈcamos que o id veio corretamente, caso você não chegou a este resultado, abra o navegador
(em nossos exemplos estamos usando o Google Chrome), pressionando o F12 do teclado, abra o
console e veriᒈque se há algum erro.
Neste ponto, em nossa Controller, pressionando F11 do teclado, veriᒈque o andamento da execução
do seu programa. Quando terminar, volte ao navegador e veriᒈque sua SPA se está conforme a
imagem a seguir.

Imagem 31 – SPA com dados em Editar

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 37/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Repare que neste ponto nosso botão de Salvar desapareceu isto acontece porque na função Editar
o ocultamos, em seguida mostraramos o botão atualizar. Vamos ao nosso arquivo HTML, e adicionar
um botão oculto para atualizar nossos dados.

Imagem 32 – Inserir o Botão atualizar

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 38/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Repare que referenciamos a classe hidden do bootstrap para deixar este botão oculto. No atributo
onclick inserimos a função Atualizar (criaremos esta função no próximo passo) do nosso CrudApp.js.
Caso você rodar o projeto, veriᒈcará que o botão está aparecendo normalmente, quando clicar no
botão editar, ele ocultará o Salvar. Esta foi uma dinâmica que criamos apenas para simpliᒈcar ao
máximo nosso projeto.

Voltando ao CrudApp.js adicionaremos o método de Atualizar.

Listagem 20 – Criacao da função de Atualizar

1 function Atualizar() {
2     //recebemos novamente nosso formulário
3     var dadosSerializados = $('#formDados').serialize();
4     $.ajax({
5         type: "POST",
6         url: "/App/Atualizar",
7  
8         //repare, nosso método de cadastrar é parecido até aqui.
9         //nossa controller receberá os dados de acordo com o atributo name
10         //de cada propriedade
11         data: dadosSerializados,
12         success: function () {
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 39/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders
11         data: dadosSerializados,
12         success: function () {
13             //depois que ocorrer toda a operação com sucesso
14             //ocultaremos o botao atualizar e mostraremos novamente o de salvar
15             $("#salvar").removeClass("hidden");
16             $("#atualizar").addClass("hidden");
17  
18             //por fim, listamos os arquivos novamente.
19             Listar();
20         },
21  
22         error: function myfunction() {
23             alert("Erro!");
24         }
25     });
26 }

Volte à sua controller, escreva o seguinte snippet de codigo

1 [HttpPost]
2  public void Atualizar(PessoaModel pessoa)
3  {
4    try
5    {
6      new PessoaNeg().Atualizar(pessoa);
7    }
8    catch (Exception)
9    {
10      throw;
11    }
12 }

Salve seu projeto, rode novamente a aplicação e faça os devidos testes.

Conclusão

Vimos que neste conjunto de artigos, abordamos o básico de uma aplicação Web MVC.
Demonstrando alguns itens importantes para a manipulação destas ferramentas.
Este projeto foi pensado na melhor forma de aprendizado possível, portanto, não utilizamos de

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 40/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

alguns padrões para a construção do mesmo. Podemos nos próximos artigos, mostrar alguns
padrões para melhorar a produtividade de seus projetos.

Duvidas ou sugestões, utilize a área de comentários, não deixem de compartilhar, isto ajuda em
todo o trabalho que ᒈzemos até aqui.

Download desta solução

Erick Wendel Gomes da Silva


Microsoft Student Partner, MSDN Tech Advisor e Microsoft Partner Network.
Instrutor, Palestrante e Vice Presidente do grupo .NET Coders. Fundador e
organizador da comunidade .NET Coders Ladies, formando em Análise e
Desenvolvimento de Sistemas. Também é MCP e Especialista em Web
Development, atualmente desenvolvendo e aventurando-se em C#, Python,
NodeJS e TypeScript.

         

Compartilhe isso:

 Facebook 2  Imprimir  Email  Reddit  Twitter  LinkedIn 21  Google

Curtir isso:

 Gosto
Um blogger gosta disto

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 41/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Relacionado

SPA Utilizando ASP.NET MVC 5 – Novidades do ASP.NET 5: Utilizando Windows


Parte 1.0 utilizando a tag cache em Views Authentication em projetos
Em "ASP.NET MVC 5" do MVC 6 ASP.NET MVC
Em "ASP.NET 5" Em "ASP.NET MVC 5"

Comentários
8 comentarios

5 comentários Classificar por  Mais antigos

Adicionar um comentário...

Ronaldo Robledo Mendes DE Souza · Analista de sistemas em Máxima Sistemas
Cara muito bom mesmo ta de parabens era justamente o que eu estava procurando, vai ter
continuação.
Curtir · Responder ·  2 · 25 de março de 2015 22:46

Erick Wendel · Vice­presidente em NET Coders
Olá Ronaldo, este projeto encerramos por aqui, mas pretendo postar mais artigos, tem
algum assunto que voce possui preferencia? de repente posso preparar algo;
Curtir · Responder · 26 de março de 2015 16:16

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 42/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Ronaldo Robledo Mendes DE Souza · Analista de sistemas em Máxima Sistemas
Olá Erick Wendel gostaria de saber se tem como postar algo pratico assim como fez neste
post, utilizando Entity Framework com transação, tipo abrir um bloco de transação
atualizar tabelas diversas e efetuar commit, já verifiquei vários posts ate mesmo utilizando
padrão Unit for Work mais nada que demonstrasse essa ideia. 

Grande abraço
Curtir · Responder · 6 de maio de 2015 22:04

Erick Wendel · Vice­presidente em NET Coders
Opa, Ronaldo Robledo Mendes DE Souza desculpe a demora, o problema hoje é a falta
de tempo rs, assim que sobrar um tempinho, eu escrevo algo sobre isso, para ser o mais
pratico possível
Curtir · Responder · 15 de maio de 2015 10:24

Vinicius Moret
Muito bom!!
Muito obrigado cara, espero ver mais posts como esse!
Curtir · Responder ·  1 · 12 de junho de 2015 11:08

Claudevan José Silva · Analista Programador em Capta Tecnologia
Cara muito bacana o post, bem simples e direto. Exatamente o que estava procurando.
Curtir · Responder ·  1 · 13 de julho de 2015 00:03

Douglas Vinicius · IF Muzambinho
Otimo post! ajudou demais. Abraços
Curtir · Responder · 14 de outubro de 2015 15:34

Rafael Baptista · Analista de Sistemas em Foxconn
Sensacional!!

No related posts.

 ASP.NET MVC 5, SPA

Related Posts
http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 43/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Related Posts

Utilizando Windows
Authentication em projetos
ASP.NET MVC

SignalR e MVC – Pedidos para a SPA Utilizando ASP.NET MVC 5 –


cozinha em tempo real Parte 1.0

11 Comments Already
Subscribe to comments feed

sergio durval - abril 15th, 2015 at 14:19


Ótimo artigo deu pra pegar uma noção bacana de spa , parabéns.

Erick Wendell Gomes da Silva - abril 15th, 2015 at 15:37


Muito obrigado Sergio !

Iotar Dias Júnior - agosto 4th, 2015 at 23:05


http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 44/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Parabéns pelo artigo, muito bom. Estou realizando alguns testes com base no artigo
proposto e estou me deparando com uma situação. É a seguinte….. criei minha rota e
o ng-view. Até ai funcionou certinho. Porem, no templete que é “carregado” dentro da
ng-view não funciona os scripts. Ou seja, como faço para carregar scripts que serão
utilizados nas paginas de dentro do ng-view? Obrigado pela atenção e mais uma vez,
parabéns….

Erick Wendell Gomes da Silva - agosto 5th, 2015 at 11:13


Cara tive um mesmo problema no Angular. por padrao ele nao carrega os scripts nas
views parciais. Não sei se é a melhor solução mas resolvi com essa diretiva.
http://codepen.io/ErickWendel/pen/qdgmoo

Iotar Dias Júnior - agosto 5th, 2015 at 22:44

Show de bola…. Utilizando esta solução, é possível utilizar a importação do arquivo?


Ou seja, algo do tipo: .

Valeu.

Amarildo - novembro 24th, 2015 at 08:11


Parabens, Erick, tenho aprendido muito com suas dicas e orientações,obrgão

ridersetGil Bastos - novembro 27th, 2015 at 15:39

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 45/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

Cara… Eu reᒈz essa sua aplicação… Funcionou perfeitamente no Chrome. Mas,


realmente não funcionou no IE11. Debugando. Percebi que “dadosPessoa” (método
“Listar”), no CrudApp.js, não funciona quando renderizado no IE11… Sabe o que pode
ser?

Deixe uma resposta


Insira seu comentário aqui...

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 46/47
29/07/2016 SPA Utilizando ASP.NET MVC 5 – Parte 2.0 | .NET Coders

« Análise de código: uma visão geral da ferramenta Novos recursos do Visual Studio 2015: Smart Unit
NDepend Tests »

Copyright © 2016. .NET Coders

http://netcoders.com.br/blog/spa­asp­net­mvc­5­parte­2­0/ 47/47

Anda mungkin juga menyukai