Encaminhamento baseado em DOM estendido de Paul Irish para CoffeeScript

VocĂŞ já ouviu falar sobre a “execução DOM-ready abrangente e discreta baseada em marcação” de Paul Irish ?
Ele foi escrito na Ă©poca em que o MVC do lado do cliente nem existia – mais de um ano antes de Jeremy Ashkenas enviar o primeiro commit para Backbone.js , e ninguĂ©m sabia o que Ă© Ember.js.
No entanto, todo mundo já estava reclamando sobre o JavaScript ser uma linguagem que incentiva a escrever código espaguete com estrutura livre. Para aqueles de vocês que pensam que aplicativos MVC de uma página do lado do cliente não são uma panacéia, há algo aqui para tornar sua vida mais fácil.

Algo assim parece familiar:

$(document).ready(function(){
var processMagic = function(){
// do stuff
}

if($("#specific-page-container").length > 0){
$
('body').on("click", ".secreet-link", processMagic)
} else if($(".another-page-container").length > 0){
// doo stuff
}
})

Que nojo. NĂŁo parece certo, Ă© difĂ­cil mantĂŞ-lo e precisa ser resolvido. O “roteamento baseado em DOM” de Paul e uma “visĂŁo de Jason Graber sobre isso” aprimorada oferecem uma solução elegante para aplicativos de várias páginas que nĂŁo precisam de uma estrutura MVC sofisticada do lado do cliente. Jason Graber esclarece:

O método é notável porque reforça um senso de organização em seu JavaScript e em seu HTML. Para aplicativos da web suficientemente grandes, usar um sistema como o de Paul pode tornar a integração do JavaScript muito rápida. O benefício adicional de ter um objeto estruturado contendo todas as funcionalidades do seu aplicativo é a cereja do bolo.

Mas há algo mais que impõe um senso de organização, JavaScript orientado a objetos e impede que você escreva JavaScript incorreto: CoffeeScript ! Aqui na AlphaSights , colocamos a segunda camada de cobertura com sabor de café no bolo do Graber e concordamos unanimemente que é uma grande vitória em comparação com as outras abordagens que tentamos antes.

É assim que fizemos em um de nossos aplicativos Rails3.

Em primeiro lugar, vamos dar uma olhada na estrutura de arquivos dos arquivos js no mĂłdulo de aplicativo “PĂşblico”.

app/assets/javascripts/public
├── public
│   ├── controllers
│   │   ├── common_controller.js.coffee
│   │   ├── users_controller.js.coffee
│   │   └── events_controller.js.coffee
│   └── models
│   ├── auto_toggle.js.coffee
│   ├── user.js.coffee
│   ├── data_store.js.coffee
│   ├── hider.js.coffee
│   ├── logger.js.coffee
│   ├── notification.js.coffee
│   ├── event_modal.js.coffee
│   └── scroller.js.coffee
└── public.js.coffee

A classe de roteamento principal para este módulo é public.js.coffee. Certifique-se de que ele seja carregado pelo pipeline de ativos (ou qualquer outra técnica que você use para o seu .coffee exige)

#  public.js.coffee file
#= require jquery
#= require_self
#= require_tree ./public

window
.Public ||= {}; # defining a namespace

Public.initiatedClasses = {} # a variable for caching loaded controller objects

Public.UTIL =
exec: (controller, action = 'all_actions') ->
if Public[controller] # try to find a controller
# create a controller object or re-use it if already present
klass
= Public.initiatedClasses[controller] ||= new Public[controller]

# check if both a controller and an action are usable
if typeof klass is "object" and typeof klass[action] is "function"
klass
[action]() # call the function

init
: ->
body
= $("body") # this is where your data-router-class and data-router-action attributes live
controller
= body.data("router-class")
action
= body.data("router-action")

# CommonController#all_actions is executed on every page, useful for generic stuff
this.exec "CommonController"
this.exec controller
this.exec controller, action


# this is the only place in the whole application module where we bind $(document).ready
# may as well be substituted with pjax or any other events you want
$
(document).ready -> External.UTIL.init()

Tudo o que vocĂŞ precisa fazer para invocar suas classes CoffeeScript Ă© adicionar os seguintes atributos ao seu elemento body:

<body data-router-class='User' data-router-action='index'>

# Public.UTIL will call the following functions if they are defined:

# Public.CommonController#all_actions

# Public.UsersController#all_actions

# Public.UsersController#index

É assim que fazemos em Rails e HAML .

Vamos dar uma olhada no User.js.coffee agora:

class Public.UsersController
constructor: () ->
@base = $("#main_container")
@data_sore = new Public.DataStore("users")

index
: ()->
@hide_stuff()
@base.on "click", ".show_more", (e) => @show_more_info(e)

show_more_info
: (e) ->
// process event
@data_sore.remember(user_id)
// show stuff

hide_stuff
: ->
for user in @data_sore.hidden_users()
$
(user).hide()
// cuddle rainbows

Observe que não temos que lidar com eventos $ (document) .ready nem verificar manualmente se o código é executado na página em que queremos que seja executado. Arrumado.

A classe Public.DataStore referenciada reside em public / modules. Não tem nada a ver com DOM, é reutilizável e fácil de testar. Claro, você também pode escrever o mesmo código em JavaScript, mas parece tão natural e fácil com CoffeeScript.

Em suma, os benefĂ­cios de usar esta abordagem sĂŁo

  • Melhorias de organização de cĂłdigo de alto nĂ­vel.
  • Melhorias na estrutura do cĂłdigo no arquivo.
  • CoffeeScript incentiva o estilo de codificação OO, portanto, uma mudança mental menor necessária ao alternar entre back-ends e front ends.
  • Convenções semelhantes a Rails para invocar mĂ©todos de ação de controlador.
  • NĂŁo há necessidade de lidar repetidamente com $ (document) .ready e $ (selector) .length
  • É independente do framework (alĂ©m da Ăşnica chamada $ (document) .ready especĂ­fica do jQuery)

DiscussĂŁo sobre Hacker News

Meu twitter @tadas_t