Usando jQuery com Rails

04/06/08

Pode me chamar de velha guarda, mas prefiro escrever meu próprio Javascript. E depois que comecei a trabalhar com Ruby on Rails e jQuery, minha vida ficou infinitamente mais simples.

E dessa simplicidade, surgiu o rails.js, um dispatcher de eventos feito para ser usado com jQuery, no Rails.

A idéia é que, baseado no controller e action que está sendo renderizado, seja chamado o método correto do Javascript para aquele contexto.

Para ver como funciona na prática, crie um novo projeto com o comando abaixo.

rails pages

Você precisará de um controller chamado "pages", com algumas actions.

script/generate controller index show new create edit update

Crie também um arquivo de layout em "app/views/layouts/application.html.erb" com o conteúdo abaixo.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-type" content="text/html; charset=utf-8">
        <title>Pages</title>
        <%= javascript_include_tag "jquery", "rails", "pages" %>
 
        <meta name="rails-controller" content="<%= controller.controller_name %>" />
        <meta name="rails-action" content="<%= controller.action_name %>" />
    </head>
<body>
    <%= yield %>
</body>
</html>

O segredo está nesses dois campos meta, com os nomes do controller e action. É baseado neles que o rails.js irá chamar o método correto.

Agora vem o passo mais importante de todos: remova os arquivos do Prototype. Afinal, eles não servirão para nada! ;)

rm public/javascripts/*

Você precisará do jQuery e do rails.js. Copie-os para o diretório "public/javascripts". Crie também um arquivo em "public/javascripts/pages.js".

Agora, vem a parte legal! Por exemplo, imagine que você queira executar algum Javascript para as actions "index" e "new". Você pode criar algo como isso.

Rails.pages = {
    'index': function() {
        alert('calling js for index');
    },
 
    'new': function() {
        alert('calling js for new');
    }
}

Se você acessar o endereço http://localhost:3000/pages/, o método Rails.pages.index() será chamado.

E o que acontece se eu acessar a action "create", que normalmente é o fallback do método "new"? Quando você acessar a action "create", o método "new" será chamado no lugar, assim como a action "update" chamará o método "edit". Para ver como isso funciona, experimente acessar os endereços http://localhost:3000/pages/new e http://localhost:3000/pages/create.

Eu tenho uma opinião bastante forte a respeito do Rails gerar Javascript. É conveniente? Claro que é! Mas ainda sim prefiro escrever meu próprio Javascript.

Você pode fazer o download deste exemplo aqui.

Usando jQuery na prática

29/03/08

O jQuery, sem sombra de dúvidas, revolucionou a maneira como escrevemos Javascript. Baseado no atalho $ para document.getElementById, o jQuery estendeu-o muito além do que se podia imaginar, misturando-o com a função document.getElementsBySelector.

Além do evidente aumento de produtividade, o tamanho da biblioteca impressiona: a versão 1.2.3 tem apenas 29kb para fazer tudo o que você precisa: requisições AJAX, iteração e criação de elementos DOM, tratamento de eventos… resumindo, é uma biblioteca completa, sem deixar a simplicidade de lado.

Uma coisa que deve ficar muito clara quando você começa a trabalhar com jQuery são os seletores disponíveis, baseados em CSS 1-3 e alguns adicionados pelo próprio jQuery. Sem eles, você não tem por onde começar. Então, aqui vai uma lista super-detalhada dos seletores disponíveis no jQuery.

Seletores

Os exemplos que mostrarei são baseados no seguinte markup:

<body>
    <h1>jQuery Selectors</h1>
    <h2>This is a visible H2</h2>
    <h2 style="display: none;">This is a hidden H2</h2>
    <p>This is just a dummy text.</p>
    <p>This is just a dummy text too.</p>
 
    <ul id="list" class="list-class">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
    </ul>
 
    <p>
        <strong>Sample:</strong> Visit our 
        <a href="#" target="_blank" id="sample">sample page</a>!
    </p>
 
    <div id="empty-div"></div>
    <div>
        <p>
            <span>
                <em>Inside a span</em>
            </span>
        </p>
        <p>
            <em>Inside a paragraph</em>
        </p>
    </div>
 
    <p>
        <input type="button" disabled="disabled" value="Disabled button" />
        <input type="button" value="Click me!" />
        <button>Click me too!</button>
    </p>
 
    <p>
        <input type="checkbox" id="checkbox" checked="checked" />
        <label for="checkbox">I'm checked</label>
    </p>
 
    <p>
        <select>
            <option value="1">Option 1</option>
            <option value="2">Option 2</option>
            <option value="3" selected="selected">Option 3</option>
        </select>
    </p>
 
    <dl>
        <dt>Colors</dt>
        <dd class="color">Blue</dd>
 
        <dt>Shapes</dt>
        <dd class="shape">Circle</dd>
 
        <dt>Words</dt>
        <dd class="word">Power</dd>
    </dl>
</body>

CSS 1-3

  • *: qualquer elemento.
    $('p.comment-meta *'); // get all elements inside p.comment-meta
  • E: pega qualquer elemento com o nome E.
    $('div'); // get all div tags
  • E:nth-child(n): em um elemento E, pega o elemento n de seu pai.
    $('li:nth-child(2)'); // get <li>Item 2</li>
  • E:first-child: em um elemento E, pega o primeiro filho de seu pai.
    $('li:first-child'); // get <li>Item 1</li>
  • E:empty: um elemento E que não possui filhos.
    $('div:empty'); // get <div id="empty-div"></div>
  • E:enabled: um elemento E que está ativo.
    $('input:enabled'); // get <input type="button" value="Click me!" />
  • E:disabled: um elemento E que está inativo.
    $('input:disabled'); // get <input type="button" disabled="disabled" value="Disabled button" />
  • E:checked: um elemento E (radio ou checkbox) que está selecionado.
    $('input:checked'); // get <input type="checkbox" checked="checked" />
  • E:selected: um elemento E (option) que está selecionado.
    $('option:selected'); // get <option value="3">Option 3</option>
  • E F: um elemento E que tenha um filho F.
    $('p strong'); // get <strong>Sample:</strong>
  • E > F: um elemento F dentro de E.
    $('p > em'); // get <em>Inside a paragraph</em> but not <span><em>Inside a span</em></span>
  • E + F: elemento F imediatamente precedido por um elemento E.
    $('h1 + p'); // get <p>This is just a dummy text.</p>
  • E ~ F: elementos F precedido por um elemento E.
    $('h1 ~ ul'); // get <ul id="list" class="list-class">
  • E,F,G: elementos E, F e G.
    $('h1,ul'); // get <h1> and <ul>
  • E[attr]: elementos E que possuam o atributo attr.
    $(':checkbox[checked]'); // get <input type="checkbox" id="checkbox" />
  • E[attr^=value]: elementos E que possuam um atributo attr cujo valor inicie-se por value.
    $('[id^=empty]'); // get <div id="empty-div"></div>
  • E[attr$=value]: elementos E que possuam um atributo attr cujo valor termine com value.
    $('[id$=div]'); // get <div id="empty-div"></div>
  • E[attr*=value]: elementos E que possuam um atributo attr cujo valor contenha value.
    $('dd[class*=o]'); // get <dd class="color"> and <dd class="word">
  • E[attr=value]: elementos cujo atributo attr tenham valor igual a value.
    $('dd[class=word]'); // get <dd class="word">

Adicionados pelo jQuery

  • :even: todos os elementos de índice par. Os índices se iniciam em zero.
    $('option:even'); // get <option value="1"> and <option value="3">
  • :odd: todos os elementos de índice ímpar.
    $('option:odd'); // get <option value="2">
  • :eq(N) e :nth(N): seleciona o elemento de índice N.
    $('option:eq(0)'); // get <option value="1">
    $('option:nth(1)'; // get <option value="2">
  • :gt(N): seleciona os elementos cujo índice sejam maior que N.
    $('li:gt(1)'); // get <li>Item 3</li>
  • :lt(N): seleciona os elementos cujo índice seja menores que N.
    $('li:lt(1)'); // get <li>Item 1</li>
  • :first: equivalente a :eq(0).
    $('li:first'); // get <li>Item 1</li>
  • :last: seleciona o último elemento.
    $('li:last'); // get <li>Item 3</li>
  • :parent: seleciona os elementos que têm filhos (incluindo textos).
    $('h1:parent'); // get <h1>jQuery Selectors</h1>
  • :contains('text'): seleciona os elementos que têm o texto especificado.
    $('li:contains("Item 1")'); // get <li>Item 1</li>
  • :visible: seleciona todos os elementos visíveis (não inclui elementos de formulário hidden).
    $('h2:visible'); // get <h2>This is a visible H2</h2>
  • :hidden: seleciona campos de formulário hidden e elementos não-visíveis.
    $('h2:hidden'); // get <h2 style="display: none;">This is a hidden H2</h2>

Formulários

O jQuery fornece alguns atalhos para se trabalhar com campos de formulário, evitando que você tenha que usar um seletor de atributo (input[type=radio], por exemplo). Veja como é simples:

  • :input: seleciona qualquer tipo de campo de formulário (input, button, textarea, select).
  • :text: seleciona campos com type="text".
  • :password: seleciona campos com type="password".
  • :radio: seleciona campos com type="radio".
  • :checkbox: seleciona campos com type="checkbox".
  • :file: seleciona campos com type="file".
  • :submit: seleciona campos com type="submit".
  • :image: seleciona campos com type="image".
  • :reset: seleciona campos com type="reset".
  • :button: seleciona campos com type="button" ou <button></button>.

Exemplos práticos

Agora você conhece os seletores do jQuery, veja alguns exemplos mostrando como usar este framework.

O jQuery possui uma série de outras funcionalidades que não foram citadas aqui. Para mais informações, você deve acessar a documentação do projeto. Ela também está disponível em um formato muito fácil de vsualizar em Visual jQuery. Vale a pena conferir!

E se você tem dúvidas em usar ou não o jQuery em seu próximo projeto, não se preocupe. Em relação ao Javascript, será a melhor escolha que você poderia ter feito!

Lidando com checkbox no JQuery

25/11/06

Se você precisa lidar com checkboxes e está usando JQuery, veja este plugin que criei.

/*
Author: Nando Vieira
License: Public Domain
*/
jQuery.fn.check = function(mode)
{
    mode = mode || 'on';
 
    return this.each(function(){
        if (this.type == 'checkbox') {
            jQuery.fn._setCheckboxState(this, mode);
        } else {
            $(this).field('checkbox').each(function(){
                jQuery.fn._setCheckboxState(this, mode);
            });
        }
    });
}
 
jQuery.fn._setCheckboxState = function(checkbox, mode)
{
    switch (mode) {
        case 'toggle':
            checkbox.checked = !checkbox.checked;
            break;
        
        case 'off':
            checkbox.checked = false;
            break;
        
        case 'on':
            checkbox.checked = true;
            break;
    }
}
 
jQuery.fn.field = function(type, filter)
{
    var rule;
    filter = filter || '';
    
    if(type == 'all' || !type) {
        rule = 'input|textarea|select';
    } else {
        if (type != 'select' && type != 'textarea') {
            rule = 'input[@type=' + type + ']';
        } else {
            rule = type;
        }
    }
    
    return jQuery(rule, this).not(filter);
}

Se preferir, pode passar o próprio elemento:

$('form fieldset#preferences input[@type=checkbox]').check();

De quebra, você leva uma função que retorna todos os elementos de formulário identificados pelo tipo. Por exemplo, para retornar todos os campos "password", você pode usar:

$('form').field('password').each(function(){
    alert($(this).name());
});

Você também pode filtrar quais campos quer exibir. O exemplo abaixo pega todos os campos do formulário, com exceção de "password":

$('form').field('all', '[@type=password]').each(function(){
    alert($(this).name() + ' = ' + $(this).val());
});

Campos do tipo "select" e "textarea" também entram na lista de "input".

Download: jquery-checkbox.js
Tamanho: 912 bytes

Bug Fix

  • A regra "all" retornava todos os "selects" e "textareas" da página e não apenas do elemento.