Meu Primeiro jQuery Plugin – Change in Line
Bruno Kenj | ASP.NET, ASP.NET MVC, jQuery
Pessoal,
outro dia, precisei fazer uma edição de um conteúdo de apenas uma linha (um campo) de uma forma direta, no estilo “Web 2“, para um novo portal que estamos criando no #CFOAB. Para não duplicar código e provavelmente isso iria acontecer, acabei decidindo fazer meu primeiro plugin para o jQuery. Até então, eu vinha mexendo muito com o ExtJS e ficava só na vontade de mexer com o jQuery desde que se tornou padrão no ASP.NET.
A idéia do plugin é simples e deve ser utilizado por um único objeto DOM, utilizando os seletores do jQuery, passando pelo menos dois parâmetros obrigatórios (urlRequest e nomeParam).
Infelizmente, meu host (arght!), não deixou configurado o ASP.NET MVC, nem 1, nem 2, e então eu acabei usando o WebForms para a demonstração, mas vou postar o código utilizando o ASP.NET MVC 1 (RTM) e 2 (RC).
Antes de partir para o código, aqui vai uma DEMO do resultado aplicado em uma div e uma table.
Então vamos aos códigos CSS, Javascript, jQuery e HTML.
Código HTML
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>JQuery Plugin - Change In Line : Demo - Bruno Kenj</title>
<link href="Content/Site.css" rel="stylesheet" type="text/css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js" type="text/javascript"></script>
<!-- Microsoft AJAX CDN: http://ajax.microsoft.com/ajax/jquery/jquery-1.4.0.min.js -->
<script src="Scripts/ChangeInLine.js" type="text/javascript"></script>
<!-- Cada aplicação deve ter sua própria configuração de input, etc -->
<style type="text/css">
input { border:1px solid #e3e3e3; }
.clEdit { padding: 5px; width:300px; }
table { border:0px; width:310px; }
table tr td.texto { width:100px; }
</style>
</head>
<body>
<div id="txtEmail" class="clEdit">contato@brunokenj.net</div>
<table>
<tr>
<td class="texto">E-mail:</td>
<td id="txtEmailTable" class="clEdit">contato@brunokenj.net</td>
</tr>
</table>
<script type="text/javascript">
$(document).ready(function() {
function ChangeEmail(args) {
alert('O email ' + args.email + ' foi alterado? ' + args.sucesso);
}
var options = {
urlRequest: 'Servicos/AlterarEmail.ashx',
nomeParam: 'email',
savedChanges: ChangeEmail
};
$('#txtEmail').ChangeInLine(options);
$('#txtEmailTable').ChangeInLine(options);
});
</script>
</body>
</html>
Para fazer a chamada no javascript para o Change In Line, basta recuperarmos o objeto DOM com o jQUery e chamar a função ChangeInLine(opts) informando os parâmetros necessários.
$('#txtEmail').ChangeInLine({ urlRequest: 'Servicos/AlterarEmail.ashx', nomeParam: 'email', savedChanges: ChangeEmail });
Dois parâmetros são obrigatórios:
1. urlRequest: URL que recebe o POST com o conteúdo na variável nomeParam.
2. nomeParam: Nome da variável que vai receber o valor do campo a ser alterado.
Não obrigatórios:
1. savedChanges: function(args). Função para ação pós requisição.
2. urlImageSave: URL da imagem de Salvar.
3. urlImageCancel: URL da imagem de Cancelar.
4. urlImageLoading: URL da imagem de Loading.
Vamos ao restante agora CSS e jQuery.
CSS
/* Componente Change in Line */
.clEdit { background:#fff url(Imagens/12-em-pencil.png) no-repeat 99% 50%; cursor:pointer; padding-right:15px; }
.clEditHover { background-color:#ffffd3; }
.clActive { background-color:#ffffd3; background-image:none; }
Bem simples, apenas três classes. Fique atento ao caminho da imagem na classe clEdit.
E para finalizar, o plugin do jQuery:
Change In Line
/* Criado por Bruno Kenj (http://www.brunokenj.net) */
(function($) {
$.fn.ChangeInLine = function(options) {
var defaults = {
urlRequest: '',
nomeParam: '',
urlImageSave: 'Content/Imagens/16-circle-green-check.png',
urlImageCancel: 'Content/Imagens/16-circle-red-delete.png',
urlImageLoading: 'Content/Imagens/loading.gif',
savedChanges: function(args) { }
};
var options = $.extend(defaults, options);
var root, valorTexto, html, loading;
function setActive(obj) {
obj.data('inEdit', true);
obj.unbind('click');
}
function setInative(obj) {
obj.data('inEdit', false);
obj.bind('click', onChangeInLine);
}
function setFocus(obj) {
obj.find('input').select();
}
function onFocus() {
this.select();
}
function save() {
saveChanges(root, false);
}
function cancel() {
saveChanges(root, true);
}
function setEventInButtons(obj) {
obj.find('.clSavar').click(save);
obj.find('.clCancelar').click(cancel);
}
function setHTML(obj, valor) {
obj.html(valor);
}
function toogleActive(obj) {
obj.toggleClass("clActive");
}
function isLoading() {
return loading;
}
function setLoading(isLoading) {
loading = isLoading;
if (isLoading) {
var space = ' ';
var inputText = '<input type="text" value="' + root.find('input').val() + '" disabled="disabled" />';
var imageLoading = '<img src="' + options.urlImageLoading + '" class="clLoading" width="16" height="16" />';
setHTML(root, inputText + space + space + imageLoading);
}
}
function onChangeInLine() {
if (root.data('inEdit') == false || root.data('inEdit') == null) {
valorTexto = root.html();
var space = ' ';
var inputText = '<input type="text" value="' + valorTexto + '"/>';
var buttonOK = '<img src="' + options.urlImageSave + '" class="clSavar" width="16" height="16" />';
var buttonNO = '<img src="' + options.urlImageCancel + '" class="clCancelar" width="16" height="16" />';
html = inputText + space + space + buttonOK + space + buttonNO;
setActive(root);
setHTML(root, html);
setEventInButtons(root);
setFocus(root);
}
else {
setInative(root);
setHTML(root, valorTexto);
}
toogleActive(root);
}
var savedChangesCallBack = function(args) {
onChangeInLine();
setLoading(false);
root.removeClass('clEditHover');
root.html(eval('args.' + options.nomeParam));
options.savedChanges(args);
}
function saveChanges(obj, cancel) {
if (cancel) {
obj.click(onChangeInLine);
} else {
var objJson = eval('({ "' + options.nomeParam + '": "' + obj.find('input').val() + '" })');
setLoading(true);
jQuery.post(options.urlRequest, objJson, savedChangesCallBack, "json");
}
}
function mouseOver() {
$(this).addClass('clEditHover');
}
function mouseOut() {
$(this).removeClass('clEditHover');
}
return this.each(function() {
root = $(this);
root.click(onChangeInLine)
.mouseover(mouseOver)
.mouseout(mouseOut);
});
};
})(jQuery);
Dica: para evitar problemas com outras bibliotecas, o mínimo para fazermos o plugin deve ser:
(function($){
$.fn.ChangeInLine = function() {
return this.each(function() {
});
};
})(jQuery);
OK. Mas, ainda falta receber o valor no servidor para realizar a persistência do dado. Vou colocar o exemplo utilizando WebForms, MVC 1 e MVC 2 RC.
Obs: Nestes exemplos, eu adicionei o Thread.Sleep(600) para podermos ver melhor o efeito de loading.
WebForms (AlterarEmail.ashx – Handle HTTP)
<%@ WebHandler Language="C#" Class="AlterarEmail" %>
using System;
using System.Web;
public class AlterarEmail : IHttpHandler {
public void ProcessRequest (HttpContext context) {
System.Threading.Thread.Sleep(600);
string email = context.Request.Form["email"];
context.Response.ContentType = "text/json";
string retorno = string.Format("\"sucesso\": true, \"email\": \"{0}\"", email);
context.Response.Write("{" + retorno + "}");
}
public bool IsReusable { get { return false; } }
}
Bem simples. Reparem que a variável que vem no Request do Form é o nome (email) informado na configuração do plugin. Aqui, eu poderia ter usado alguma biblioteca para serializar o JSON mas fiz direto com string mesmo.
ASP.NET MVC 1 (ServicosController)
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult AlterarEmail(string email)
{
System.Threading.Thread.Sleep(600);
var retorno = new { sucesso = true, email };
return Json(retorno);
}
ASP.NET MVC 2 (ServicosController)
public JsonResult AlterarEmail(string email)
{
System.Threading.Thread.Sleep(600);
var retorno = new { sucesso = true, email };
return Json(retorno, JsonRequestBehavior.AllowGet);
}
Recursos utilizados e Download:
Download das Imagens
Download da Solução (WebForms)
Espero que tenham gostado do plugin. Como eu fiz ele em uma tarde, é possível que exista algum erro. Se alguém encontrar antes de mim, por favor, me informe que eu atualizo o post. Se alguém tiver alguma dúvida, pode me procurar no Twitter: @brunokenj.
Já tenho em mente uma próxima melhoria: permitir o uso de TextArea para campos de textos grandes e outros controles HTML.
abraços,