CRUD usando ExtJs e ASP NET MVC 3

Depois de falar sobre ExtJs Grid Panel e ExtJs FormPanel, hoje vou descrever como integrar os dois componentes, fazendo um CRUD usando Entity Framework para salvar as informações no banco de dados SqlServer

A idéia é criar um cadastro simples de uma entidade chamada Carro onde vamos listar os dados em uma grid e com as opções de Adicionar, Editar e Deletar, então mãos a obra

OBS: Não vou demostrar como criar o EDMX do Entity Framework, para isso veja esse artigo CRUD com Entity Framework e ASP NET MVC

Model

Primeiro de tudo vamos criar nosso model CarroModel, veja abaixo o código

[sourcecode language=”csharp”]
public class CarroModel
{
public int Id { get; set; }
public string Nome { get; set; }
public string Cor { get; set; }
public string Modelo { get; set; }
public string IdModelo { get; set; }
public decimal Valor { get; set; }
public string TipoCombustivel { get; set; }
public string IdTipoCombustivel { get; set; }
public int QtdePortas { get; set; }
}
[/sourcecode]

Controller

Agora vamos criar controller CarroController

[sourcecode language=”csharp”]
public class CarroController : Controller
{
public ActionResult Index()
{
return View();
}
}
[/sourcecode]

Vamos criar um novo método chamado GetAll, que será responsável por recuperar os carros gravados no banco

[sourcecode language=”csharp”]
public JsonResult GetAll(int start, int limit)
{
TesteExtJsEntities entities = new TesteExtJsEntities();
List lstCarros = entities.CreateObjectSet().ToList();

// transforma no Model
List retorno = lstCarros.Select(
obj => new CarroModel
{
Id = obj.id,
Nome = obj.nome,
Cor = obj.cor,
Valor = obj.valor,
QtdePortas = obj.qtde_portas,
Modelo = obj.modelo.nome,
IdModelo = obj.modelo.id_modelo.ToString(),
TipoCombustivel = obj.tipo_combustivel.nome,
IdTipoCombustivel = obj.tipo_combustivel.id_tipo_combustivel.ToString()
}).ToList();

int total = lstCarros.Count;
lstCarros = lstCarros.Skip(start).Take(limit).ToList();
return Json(new { carros = lstCarros, total = total }, JsonRequestBehavior.AllowGet);
}
[/sourcecode]

Agora vamos criar o método para recuperar todos os Modelos e Combustiveis, será usado para carregar as combos na tela de cadastro

[sourcecode language=”csharp”]
public JsonResult GetAllModelo()
{
TesteExtJsEntities entities = new TesteExtJsEntities();
List lstModelos = entities.CreateObjectSet().ToList();

// transforma no Model
List retorno = lstModelos.Select(
obj => new ModeloModel
{
Id = obj.id_modelo,
Nome = obj.nome
}).ToList();

return Json(new { modelos = retorno, total = retorno.Count }, JsonRequestBehavior.AllowGet);
}

public JsonResult GetAllCombustivel()
{
TesteExtJsEntities entities = new TesteExtJsEntities();
List lstCombustivel = entities.CreateObjectSet().ToList();

// transforma no Model
List retorno = lstCombustivel.Select(
obj => new ModeloModel
{
Id = obj.id_tipo_combustivel,
Nome = obj.nome
}).ToList();

return Json(new { combustiveis = retorno, total = retorno.Count }, JsonRequestBehavior.AllowGet);
}
[/sourcecode]

O método Save, receberá os parâmetros e vai criar ou atualizar o objeto do tipo carro e persistir no banco usando o entity framework

[sourcecode language=”csharp”]
public JsonResult Save(string Id, string Nome, string Cor, string Valor, string QtdePortas, string Modelo, string TipoCombustivel, string action)
{
try
{
TesteExtJsEntities entities = new TesteExtJsEntities();

if(action.Equals("insert"))
{
carro objCarro = new carro();
objCarro.nome = Nome;
objCarro.cor = Cor;
objCarro.valor = Convert.ToDecimal(Valor);
objCarro.qtde_portas = Convert.ToInt32(QtdePortas);
objCarro.id_tipo_combustivel = Convert.ToInt32(TipoCombustivel);
objCarro.id_modelo = Convert.ToInt32(Modelo);

entities.AddTocarro(objCarro);
entities.SaveChanges();
}
else if (action.Equals("update"))
{
int cod = int.Parse(Id);
carro objCarro = entities.CreateObjectSet().Where(a => a.id == cod).ToList().First();

// Altera os dados
objCarro.nome = Nome;
objCarro.cor = Cor;
objCarro.valor = Convert.ToDecimal(Valor);
objCarro.qtde_portas = Convert.ToInt32(QtdePortas);
objCarro.id_tipo_combustivel = Convert.ToInt32(TipoCombustivel);
objCarro.id_modelo = Convert.ToInt32(Modelo);

entities.ApplyCurrentValues(objCarro.GetType().Name, objCarro);
entities.SaveChanges();
}

return Json(new { success = true }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message }, JsonRequestBehavior.AllowGet);
}
}
[/sourcecode]

E por último o método para deletar um carro

[sourcecode language=”csharp”]
public JsonResult Delete(int id)
{
try
{
TesteExtJsEntities entities = new TesteExtJsEntities();
carro entity = entities.carro.Single(a => a.id == id);
entities.DeleteObject(entity);
entities.SaveChanges();
return Json(new { success = true }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message }, JsonRequestBehavior.AllowGet);
}
}
[/sourcecode]

View

Nesse exemplo vamos usar apenas um arquivo cshtml, para isso clique com o botão direito sobre a action Index do controller e selecione a opção Add View, conforme abaixo:

Primeiro de tudo temos que adicionar as referências das bibliotecas do ExtJs, veja abaixo

[sourcecode language=”html”]
<link rel="stylesheet" type="text/css" href="../../ext-3.2.1/resources/css/ext-all.css"/>
<script type="text/javascript" src="../../ext-3.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-3.2.1/ext-all.js"></script>
[/sourcecode]

Criando a Grid

Vamos criar uma grid onde será listado os registros com as opções de Adicionar / Excluir / Editar

[sourcecode language=”js”]
Ext.onReady(function () {
//titulo do formulário
var formTitle = ‘Cars ‘;

// criamos um JsonStore que vai armazenar as informações da entidade carro
var dsCarros = new Ext.data.JsonStore({
url: ‘@Url.Content("~/Carro/GetAll")’,
root: ‘carros’,
totalProperty: ‘total’,
idProperty: ‘id’,
fields: [‘Id’, ‘Nome’, ‘Cor’, ‘Valor’, ‘QtdePortas’, ‘Modelo’, ‘IdModelo’, ‘TipoCombustivel’, ‘IdTipoCombustivel’]
});
dsCarros.load({ params: { start: 0, limit: 15} });

// criando a grid que vai listar os registros da entidade carro
var grid = new Ext.grid.GridPanel({
width: 760,
height: 395,
title: ‘List Cars’,
store: dsCarros,
stripeRows: true,
// grid columns
columns: [{
id: ‘id’,
header: "Id",
dataIndex: ‘Id’,
width: 50,
sortable: true
}, {
id: ‘nome’,
header: "Nome",
dataIndex: ‘Nome’,
width: 200,
sortable: true
}, {
id: ‘cor’,
header: "Cor",
dataIndex: ‘Cor’,
width: 100,
sortable: true
}, {
id: ‘valor’,
header: "Valor",
dataIndex: ‘Valor’,
width: 100,
sortable: true
}, {
id: ‘qtde_portas’,
header: "Qtde Portas",
dataIndex: ‘QtdePortas’,
width: 100,
sortable: true
}, {
id: ‘modelo’,
header: "Modelo",
dataIndex: ‘Modelo’,
width: 100,
sortable: true
}, {
id: ‘tipo_combustivel’,
header: "Tipo Combustivel",
dataIndex: ‘TipoCombustivel’,
width: 100,
sortable: true
}],
// paging bar on the bottom
bbar: new Ext.PagingToolbar({
pageSize: 15,
store: dsCarros,
displayInfo: true,
displayMsg: ‘Displaying topics {0} – {1} of {2}’,
emptyMsg: "No topics to display"
}),
//Aqui apenas adicionamos uma barra no topo com com os botões de adicionar, editar e deletar
tbar: [{
text: ‘Adicionar’,
iconCls: ‘btn-add’,
scope: this
}, {
text: ‘Editar’,
iconCls: ‘btn-edit’,
scope: this
}, {
text: ‘Deletar’,
iconCls: ‘btn-delete’,
scope: this
}]
});

// mostramos a grid em uma div chamada div_grid
grid.render(‘div_grid’);
});
[/sourcecode]

Execute o projeto e veja o resultado!

Criando o formulário Adicionar

Agora temos que criar o formulário que será responsável por Adicionar e Alterar os registros, primeiramente criamos um objeto do tipo Form e depois um objeto do tipo Window que conterá o formulário, veja abaixo

[sourcecode language=”js”]
//Criamos o formulário
var formulario = new Ext.form.FormPanel({
//Tiramos a borda azul
border: false,
// deixa o form azul
frame: true,
//Definimos a largura dos labels
labelWidth: 70,
items: [{
//Tipo do campo
xtype: ‘textfield’,
//Nome a ser enviado pro server e para ser carregado do store
name: ‘Id’,
//Id para recuperar o campo
id: ‘txtId’,
//Largura do campo
width: 30,
// campo invisivel
hidden: true
}, {
xtype: ‘textfield’,
name: ‘Nome’,
id: ‘txtNome’,
//Nome visível ao usuário
fieldLabel: ‘Nome’,
width: 300,
//Não permite campo em branco
allowBlank: false
}, {
xtype: ‘textfield’,
name: ‘Cor’,
id: ‘txtCor’,
fieldLabel: ‘Cor’,
width: 100,
allowBlank: false
}, {
xtype: ‘textfield’,
name: ‘Valor’,
id: ‘txtValor’,
fieldLabel: ‘Valor’,
width: 50,
allowBlank: false
}, {
xtype: ‘textfield’,
name: ‘QtdePortas’,
id: ‘txtQtdePortas’,
fieldLabel: ‘Qtde Portas’,
width: 50,
allowBlank: false
}, {
xtype: ‘combo’,
hiddenName: ‘Modelo’,
name: ‘cmbModelo’,
id: ‘cmbModelo’,
fieldLabel: ‘Modelo’,
width: 150,
allowBlank: false,
col: true,
emptyText: ‘Selecione…’
}, {
xtype: ‘combo’,
hiddenName: ‘TipoCombustivel’,
id: ‘cmbCombustivel’,
name: ‘cmbCombustivel’,
fieldLabel: ‘Combustivel’,
width: 150,
allowBlank: false,
col: true,
emptyText: ‘Selecione…’
}]
});

//Aqui criamos a janela que conterá nosso form
var winForm = new Ext.Window({
title: formTitle,
height: 250,
width: 430,
//Bloqueia o resto da aplicação
modal: true,
//Quando fechada a janela é apenas escondida para não precisar ser criada novamente
closeAction: ‘hide’,
//Aqui definimos que o filho desta tela irá ocupar toda a área disponivel na janela
layout: ‘fit’,
// adicionamos o formulário na janela
items: formulario,
//Alinhamos os botões no meio
buttonAlign: ‘center’,
buttons: [{
text: ‘Salvar’,
iconCls: ‘btn-save’,
scope: this
}, {
text: ‘Cancelar’,
iconCls: ‘btn-cancel’,
scope: this
}]
});
[/sourcecode]

Depois de criado o formulário, temos que adicionar o evento no botão Adicionar para abrir a nossa janela, para isso vamos na tool bar da grid no botão Adcionar e vamos adicionar um Handler, veja abaixo:

[sourcecode language=”js”]

tbar: [{
text: ‘Adicionar’,
iconCls: ‘btn-add’,
scope: this,
handler: adicionar
}, {
text: ‘Editar’,
iconCls: ‘btn-edit’,
scope: this
}, {
text: ‘Deletar’,
iconCls: ‘btn-delete’,
scope: this
}]

//Função que será chamada ao clicar no botão de adicionar da janela principal
var adicionar = function () {
//Defino uma propriedade na janela do form pra indicar que estamos abrindo-a
//para editar um registro e assim poder definir o que fazer na hora de salvar
winForm.update = false;

//Aqui altero o titulo pegando aquele titulo salvo acima e adicionando um texto
//indicando que ação estamos executando
winForm.setTitle(formTitle + ‘[Inserindo]’);

//Mostramos a janela
winForm.show();

//Limpo o formulário para iniciar a inserção de um novo registro
formulario.getForm().reset();
};
[/sourcecode]

Execute o projeto e clique no botão Adicionar, a tela abaixo deverá aparecer

Veja que as combos ainda estão vazias, precisamos criar um novo JsonStore para cada combo, veja abaixo:

[sourcecode language=”js”]
// criamos um JsonStore que vai armazenar as informações da entidade modelo
var dsModelo = new Ext.data.JsonStore({
url: ‘@Url.Content("~/Carro/GetAllModelo")’,
root: ‘modelos’,
totalProperty: ‘total’,
idProperty: ‘id’,
fields: [‘Id’, ‘Nome’]
});

// criamos um JsonStore que vai armazenar as informações da entidade combustivel
var dsCombustivel = new Ext.data.JsonStore({
url: ‘@Url.Content("~/Carro/GetAllCombustivel")’,
root: ‘combustiveis’,
totalProperty: ‘total’,
idProperty: ‘id’,
fields: [‘Id’, ‘Nome’]
});
[/sourcecode]

Agora no formulário temos que setar o store com as informações

[sourcecode language=”js”]

{
xtype: ‘combo’,
hiddenName: ‘Modelo’,
name: ‘cmbModelo’,
id: ‘cmbModelo’,
fieldLabel: ‘Modelo’,
width: 150,
allowBlank: false,
col: true,
//Store de onde o combo pegara sua lista de dados
store: dsModelo,
//Campo que será usado como valor
valueField: ‘Id’,
//Campo que será mostrado na lista
displayField: ‘Nome’,
//necessário para o combo buscar os dados do store
triggerAction: ‘all’,
emptyText: ‘Selecione…’
}, {
xtype: ‘combo’,
hiddenName: ‘TipoCombustivel’,
id: ‘cmbCombustivel’,
name: ‘cmbCombustivel’,
fieldLabel: ‘Combustivel’,
width: 150,
allowBlank: false,
col: true,
//Store de onde o combo pegara sua lista de dados
store: dsCombustivel,
//Campo que será usado como valor
valueField: ‘Id’,
//Campo que será mostrado na lista
displayField: ‘Nome’,
//necessário para o combo buscar os dados do store
triggerAction: ‘all’,
emptyText: ‘Selecione…’
}

[/sourcecode]

Veja que a nossa tela de Adicionar ja preenche as combos

Pronto! nosso formulário está pronto, agora vamos implementear as ações dos botões Salvar e Cancelar, novamente temos que adicionar um handler para cada botão conforme abaixo:

[sourcecode language=”js”]

buttons: [{
text: ‘Salvar’,
iconCls: ‘btn-save’,
scope: this,
handler: salvar
}, {
text: ‘Cancelar’,
iconCls: ‘btn-cancel’,
scope: this,
handler: cancelar
}]

[/sourcecode]

Agora vamos implementar as funções salvar e cancelar conforme abaixo

[sourcecode language=”js”]
//Função chamada ao clicar no botão de cancelar do formulário
var cancelar = function () {
formulario.getForm().reset();
//Apenas fechamos a janela
winForm.hide();
};

//Função a ser chamada quando clicar no botão salvar do formulário
var salvar = function () {
//Verificamos se o formulário está valido de acordo com cada campo
if (formulario.getForm().isValid()) {
//Se sim colocamos uma mascara de "Salvando" na janela do form
//Impedindo que o usuário fique funçando na tela
winForm.el.mask(‘Salvando’, ‘x-mask-loading’);

//Usamos a função do form para salvar os dados, submit
//os dados vão por AJAX em POST
formulario.getForm().submit({
//Arquivo que faz a interação com o banco
url: ‘@Url.Content("~/Carro/Save")’,
params: {
//Aqui temos if compacto para verificar qual a ação que estava
//sendo executada no form
action: winForm.update ? ‘update’ : ‘insert’
},
//Função chamada se retornado success:true
success: function (form, action) {
if (action.result.success) {
//Então se tudo ok retiramos a mascara de ‘Salvando’
winForm.el.unmask();
//E fechamos a janela
winForm.hide();
//Recarregamos o grid para visualizarmos as mudanças
dsCarros.reload();
}
},
//Função chamada se retornado success:false
failure: function (form, action) {
//Se tivemos problemas tiramos a mascara
winForm.el.unmask();
//E mostramos uma mensagem ao usuário informando o erro vindo do servidor
Ext.Msg.alert(‘Erro’, action.result.message);
},
scope: this
})
} else {
//Se temos algum campo inválido avisamos ao usuário
Ext.Msg.alert(‘Atenção’, ‘Exixtem campos inválidos’);
}
};
[/sourcecode]

Veja o formulário preenchido abaixo

Criando o formulário Editar

Para criar a opção de Editar vamos usar o mesmo formulário, apenas recuperando os valores do banco e preenchendo os campos, primeiro temos que criar um handler para o Editar, veja abaixo

[sourcecode language=”js”]

tbar: [{
text: ‘Adicionar’,
iconCls: ‘btn-add’,
handler: adicionar
}, {
text: ‘Editar’,
iconCls: ‘btn-edit’,
scope: this,
handler: editar
}, {
text: ‘Deletar’,
iconCls: ‘btn-delete’,
scope: this
}]

[/sourcecode]

Agora a função editar que vai preencher o formulário

[sourcecode language=”js”]
//Função a ser chamada quando clicar no botão de editar da janela principal
var editar = function () {
//Para editar precisamos que o usuário tenha selecionado um registro então
//verificamos se existe alguama seleção no nosso grid.
if (grid.getSelectionModel().hasSelection()) {
//Mudamos nossa propriedade para indicar que a janela está em modo de atualização
winForm.update = true;
//Mudamos o titulo para indicar ao usuário a ação que está sendo executada
winForm.setTitle(formTitle + ‘[Alterando]’);
//mostramos a janela
winForm.show();

// recupero o registro selecionado na grid e preencho os valores
// dessa forma não preciso ir até o banco novamente
var record = grid.getSelectionModel().getSelected();
Ext.getCmp("txtId").setValue(record.data.Id);
Ext.getCmp("txtNome").setValue(record.data.Nome);
Ext.getCmp("txtCor").setValue(record.data.Cor);
Ext.getCmp("txtValor").setValue(record.data.Valor);
Ext.getCmp("txtQtdePortas").setValue(record.data.QtdePortas);
Ext.getCmp("cmbModelo").setValue(record.data.IdModelo);
Ext.getCmp("cmbCombustivel").setValue(record.data.IdTipoCombustivel);
Ext.getCmp("cmbModelo").setRawValue(record.data.Modelo);
Ext.getCmp("cmbCombustivel").setRawValue(record.data.TipoCombustivel);
} else {
//Caso não tenhamos nenhuma linha selecionada avisamos ao usuário
Ext.Msg.alert(‘Atenção’, ‘Selecione um registro’);
}
};
[/sourcecode]

Veja na imagem abaixo o resultado, veja q estamos editando o registro selecionado na grid

Criando o formulário Deletar

E finalmente vamos criar a opção para deletar um registro, novamente adicione um handler para o botão cancelar conforme abaixo

[sourcecode language=”js”]

tbar: [{
text: ‘Adicionar’,
iconCls: ‘btn-add’,
scope: this,
handler: adicionar
}, {
text: ‘Editar’,
iconCls: ‘btn-edit’,
scope: this,
handler: editar
}, {
text: ‘Deletar’,
iconCls: ‘btn-delete’,
scope: this,
handler: deletar
}]

[/sourcecode]

E agora a função deletar

[sourcecode language=”js”]
//Função a ser chamada quando clicar no botão de deletar da tela principal
var deletar = function () {
//Novamente verificamos se o usuário selecionou alguma linha
if (grid.getSelectionModel().hasSelection()) {
//Separamos o registro selecionado para uma variavel para evitar de
//chamar estas funções com frequencia ja que usarei este registro mais de uma vez abaixo
var record = grid.getSelectionModel().getSelected();

//Perguntamos ao usuário se ele realmente deseja excluir o registro
Ext.Msg.confirm(‘Atenção’, ‘Você está prestes a excluir o carro <strong>’ + record.data.Nome + ‘</strong>. Deseja continuar?’, function (btn) {
//Testamos qual botão ele clicou
if (btn == ‘yes’) {
//Se ele aceitou blz, criamos um AJAX passando o registro que queremos deletar
Ext.Ajax.request({
//Aqui o arquivo php que interage com nosso banco
url: ‘@Url.Content("~/Carro/Delete")’,
//Paramentros que passaremos por POST
params: {
//E passamos o login do cara que queremos deletar, pq só o login?
//Pq o login é nossa chave primária, só preciamos dela pra fazer um delete
id: record.data.Id
},
//Função chamada quando não houver nenhum erro de pagina como 404, 500
success: function (r) {
//Se tudo OK, pegamos a resposta que é um JSON e decodificamos para um objeto
var obj = Ext.decode(r.responseText);
//Verificamos se obtivemos sucesso na ação
if (obj.success) {
//Se sim efetuar um reload
dsCarros.reload();
} else {
//Caso tenha acontecido um erro mostra uma mensagem ao usuário com um texto vindo do servidor
Ext.Msg.alert(‘Erro’, obj.msg);
}
},
//Função executada se tivermos um erro de arquivo n encontrado ou coisa do tipo, 404, 500, etc
failure: function () {
//Mostramos uma mensagem ao usuário pedindo para contatar o administrador
Ext.Msg.alert(‘Erro’, ‘Ocorreu um erro ao se comunicar com o servidor, tente novamente. Se o erro persistir entre em contato com o adiministrador do sistema’)
},
scope: this
})
}
}, this)
} else {
//Se não tivermos uma linha selecionada no grid avisa ao usuário
Ext.Msg.alert(‘Atenção’, ‘Selecione um registro’);
}
};
[/sourcecode]

Veja o resultado

Conclusão

Mais ua vez podemos ver que o ExtJs realmente tem um resultado super bom, esse exemplo pode ainde sofre melhorias mas ja da para ter uma idéia de como podemos criar um CRUD usando ExtJs

Esse exemplo foi baseado em outro exemplo em PHP que pode ser visto aqui

Download do projeto

Qualquer dúvida, opinião, reclamação mande seu comentário!

Aquele Abraço!

Sobre Leandro Prado

Leandro Silveira Prado é Premier Field Engineer na Microsoft especializado em Application Lifecycle Management.