Ciao a tutti,
ho bisogno di un aiuto per capire come il codice javascript si integri
in un'app Rails, ovvero quali sono le best practices al proposito. In
rete non ho trovato risposte soddisfacenti alle mie perplessità.
Prima dell'avvento di UJS il codice js era inserito all'interno delle
viste, ora viene collocato in app/assets/javascript/. Suppongo di avere
una semplice azione invocata dal click su un elemento grafico (uso
codice coffeescript per semplicità):
$("#dropdown").change( ->
...
)
Questo frammento viene salvato in books.js.coffee che viene caricato
assieme a tutti i file specificati in application.js. Tuttavia l'azione
riportata è visibile e disponibile a tutta l'applicazione e non, come
succede con gli helper, alle sole viste di books, e questa incoerenza a
me non piace molto. Inoltre porta a rischi di collisioni tra nomi (devo
fare attenzione ai selettori che uso).
Procedo con un esempio. Suppongo di avere due liste dropdown: il valore
della prima lista determina il caricamento della seconda lista (es.
regioni e provincie italiane).
Ogni volta che l'utente seleziona un elemento diverso dalla prima lista,
viene invocata una funzione js 'region_changed' che effettua una
chiamata ajax che provvede a popolare gli elementi della seconda lista
dropdown:
$("#regione_id").change(function() {
region_changed();
})
Definisco la mia funzione in pippo.js.coffee. Questa funzione dovrà
essere chiamata anche al completamento del caricamento della pagina web.
Che faccio? Inserisco un bel pezzo di codice 'obtrusive' tipo
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
region_changed();
})
</script>
nella mia vista?
E se uso haml per le viste come faccio?
Inoltre: la mia funzione va definita globale (@region_changed) per
essere chiamata dalla vista?
E se voglio usare un helper di routing dentro la funzione per l'URL
della chiama Ajax? Come faccio?
Mi spiego meglio, nel vecchio mondo obtrusive avrei scritto nella vista
(dentro la funzione 'region_changed'):
$.get('<%= province_of_region_path %>', { region_id: id },
function(data) {
...
Ora che la funzione va messa in app/assets/javascript come si inietta
l'URL giusto?
Troppe domande? Troppa confusione mia?
Sono ben accolti suggerimenti di lettura di articoli o blog...
Grazie mille per ogni spiegazione o suggerimento,
ciao
iwan
on 2012-10-18 00:15
on 2012-10-18 04:14
Parto dicendo che non sono un esperto di client side scripting :)
Pero' sul progetto che lavoro ora:
1) evitare che lo script sia usato in tutte le pagine -- noi definiamo
il comportamento del tuo js in funzioni, poi dentro la view
interessata abbiamo uno script tag che le triggera.
Dato un Game che has_one Display, dentro un un form partial:
app/views/name_space/admin/games/display/_form.html.erb
noi abbiamo:
<script>
$(function() {
var displayForm = new
NameSpace.Admin.Games.Display.Form('#display_properties',
'#validateDisplay', '#result');
});
</script>
quella funzione e' dentro
app/assets/name_space/admin/games/display/form.js perche' e' una
comportamento specifico che e' solo associata a quella view.
Potenzialmente gli dai un nome generico e riusi la chiamata in altre
view, ma sempre tramite uno script tag (diretto o spingendo in <head>
via content_for :head).
1.1) certe librerie che non vogliamo proprio fare impacchettare non
sono incluse nella lista della asset pipeline ed appaiono come
javascript_tag in una content_for head solo nelle view.
<% content_for :head do %>
<%= javascript_include_tag 'name_space/v1_0/our_library_name' %>
<script type="text/javascript">
Questo approccio (ed altri) li abbiamo usati dopo aver visto:
http://railsapps.github.com/rails-javascript-inclu...
2) mixare lo script con codice Ruby -- praticamente quello che viene
via asset pipeline puo' esserre erb code. Quindi puoi fare robe tipo:
# Rename countdown_event_name.js → countdown_event_name.js.erb
var max = <%= Event::NAME_MAX_LENGTH %>
Preso pari pari da
http://robots.thoughtbot.com/post/32392361476/dont...
HTH,
Enrico
2012/10/18 Iwan B. <iwan.buetti@mac.com>:
> $("#dropdown").change( ->
> Procedo con un esempio. Suppongo di avere due liste dropdown: il valore
>
> nella mia vista?
> function(data) {
> ciao
>
> iwan
>
> --
> Posted via http://www.ruby-forum.com/.
> _______________________________________________
> Ml mailing list
> Ml@lists.ruby-it.org
> http://lists.ruby-it.org/mailman/listinfo/ml
--
Enrico Teotti
Software development and web design
currently working @ http://abc.com.au
Sydney, NSW, Australia
enrico.teotti@gmail.com
mobile (AU) +00610416748450
http://teotti.com
on 2012-10-18 09:51
2012/10/18 Enrico Teotti <enrico.teotti@gmail.com> [cut] > 1) evitare che lo script sia usato in tutte le pagine -- noi definiamo > il comportamento del tuo js in funzioni, poi dentro la view > interessata abbiamo uno script tag che le triggera. > Non sono tanto d'accordo con questo approccio. Ci sono alcuni casi in cui il caricamento eterogeneo di script ti permette di ottenere un guadagno prestazionale e in quei casi, vai gi di content_for come suggerisci. Caricare in ogni pagina tutti gli script non per cos assurdo: - maggiore semplicit gestionale (eviti di dover specificare i content_for) - il complessivo js *compresso* viene caricato e cachato con la prima richiesta e reso disponibile per le successive - l'overhead computazionale pu non essere rilevante - eviti il name clashing semplicemente cambiando la mentalit di gestione del DOM Premessa: scopo del JS aggiungere *behavior* al contenuto delle pagine. $(selector).click -> doSomething() ha senso se *ogni elemento* che corrisponde a selector necessita del behavior "on click do something". HTML e JS dovrebbero essere scritti in un'ottica modulare. Nel caso del JS, potresti creare una classe CoffeeScript deputata alla gestione dei country/region/province/etc, sempre caricata nell'asset pipeline definita pi o meno cos: /app/assets/javascripts/geo_selection.js.coffee: class GeoSelection constructor: -> # do something setup: -> # on some items do .change -> ... $ -> if pageNeedGeoSelection new GeoSelection() In questo modo l'unico overhead computazionale introdotto da questo modulo, per ogni pagina, consiste nel semplice check di una condizione (verosimilmente la presenza o meno di alcuni elementi nel DOM). Un buon approccio per evitare il name clashing consiste nel basarsi sull'uso dei data-*: <select id="region_id" data-country-region="true"> in soldoni inserisci l'attributo data-country-region in ogni sezione a cui vuoi applicare il comportamento di selezione della localit e nel modulo CoffeeScript la condizione di applicazione del comportamento sar: $ -> collection = $('[data-country-region]') if collection.size() > 0 new GeoSelection(collection) Maurizio -- My profile <https://plus.google.com/100973969013103507046/about>
on 2012-10-18 10:18
> Ciao a tutti, > ho bisogno di un aiuto per capire come il codice javascript si integri > in un'app Rails, ovvero quali sono le best practices al proposito. In > rete non ho trovato risposte soddisfacenti alle mie perplessit. Neanche io, a dire la verita`. E` un'ottima domanda. Hai ragione, a mio avviso che il pacchettone 'include tutto' non e` bellissimo, perche` ti costringe a scrivere codice ragionando sul codice in tutte le altre view. Quello che faccio io e` questo: <%= page_specific_javascript -%> in views/layouts/application.html.erb def page_specific_javascript controller = params[:controller] action = params[:action] if File.exist?(Rails.root + "public/javascripts/#{controller}/#{action}.js") javascript_include_tag "/javascripts/#{controller}/#{action}.js" end end in helpers/application_helper.rb Non mi sembra una soluzione perfetta, ma almeno mi permette di divedere bene il codice, che per me e` piu` importante di ottimizzare i tempi di caricamento al 100%. -- David N. Welton http://www.welton.it/davidw/ http://www.dedasys.com/
on 2012-10-18 10:24
2012/10/18 David Welton <davidnwelton@gmail.com> > Consiglio anche a te l'approccio data-* driven ;-) Maurizio -- My profile <https://plus.google.com/100973969013103507046/about>
on 2012-10-18 11:21
Ciao, io di solito per organizzare il javascript uso un approccio simile a questo: http://blog.jerodsanto.net/2012/02/a-simple-patter...
on 2012-10-19 09:45
Con Rails 3.1 e` stata introdotta la asset pipeline. CSS e JS vengono
compilati in un unico file, viene minified (non son sicuro di come si
dica in italiano) e gli viene applicato anche un digest alla fine tipo
?3343343 per forzare il browser quando il file vengono cambiati. Con la
asset pipeline la maggior parte dei problemi dello sviluppo CSS e JS
viene rimosso. Mettere JS nelle view e helper non mi sembra molto 'the
Rails Way', almeno dalla versione 3.1 in poi.
Purtroppo Rails e` un server-side web framework, ed usarlo per il
front-end, implica utilizzare dei compromessi. Quindi il modo di default
al momento e` avere un file pages.js.coffee per il controller pages,
quindi mappare il controller/view con un file coffeescript. Poi tutti i
file verranno messi insieme, 'minificati', applicato un digest e verra`
servito solamente application.js.
Io cerco si unire il codice per le funzioni che fornisce. Tipo
valiables.js.coffee per delle variabili, tipo il formato dell'ora, poi
metto il codice specifico al controller nel suo corrispettivo file
coffee.
JQuery aggiunge delle callback agli elementi del DOM, se non trova
l'elemento si vede che sei nel controller images piuttosto che pages, ma
tanto non accade nessun errore JS. Quindi avere la partet front-end in
un file application.js va piu` che bene.
Se poi l'applicazione diventa molto basata sul JS, allora e` meglio
usare un JS framework tipo Backbone, Spine o Ember ed usare Rails
solamente per fornire i dati via REST. Questo scenario lo trovo ostico e
complesso. A questo punto preferisco Meteor Framework, dove
l'applicazione viene scritta server e client side con il JavaScript ed
il codice client side o server side e` separato in questo modo:
// On server startup, if the database is empty, create some initial
data.
if (Meteor.isServer) {
Meteor.startup(function () {
if (Rooms.find().count() === 0) {
Rooms.insert({name: "Initial room"});
}
});
}
e Meteor.isClient per la parte client.
In un mondo ideale, dove con Ruby si potrebbe fare tutto, la parte
client sarebbe scritta in Ruby e compilata in JavaSCript, come avviene
per CoffeeScript e la parte server utilizzerebbe semplicemente Ruby.
Quindi una risposta definitiva all tua risposta credo che non ci sia. Se
hai poco JavaScript usalo come prescrive la asset pipeline, se ne hai
tanto passa a Ember.js oppure a Meteor e lasci Rails. Oppure scrivi un
equivalente di meteor con Ruby, incluso la compilazione di JavaSCript da
codice Ruby
on 2012-10-19 10:41
> Ciao, > io di solito per organizzare il javascript uso un approccio simile a > questo: > http://blog.jerodsanto.net/2012/02/a-simple-patter... Questa mi sembra la risposta migliore e piu` pulita di quelle che ho visto finora. Quello che non mi piace di "data-blah" e` che se inizi ad avere diverse pagine con codice diverso per ogni pagina inizia a molteplicarsi il lavoro: pagina X ha del codice associato che cerca il suo data-foobar, ma lo stesso codice cerchera` data-foobar su pagine Y e Z, mentre il codice per quelle pagine cerchera` data-y e data-z nelle altre dove non c'e`, e cosi` via. O sbaglio? Forse non ha un costo alto, ma comunque non mi sembra bellissimo. La cosa strana di questa situazione e` che normalmente quelli di Rails sono molto bravi a suggerire "the best way to do it", ma in questo caso non c'e` molto nella documentazione. -- David N. Welton http://www.welton.it/davidw/ http://www.dedasys.com/
on 2012-10-19 11:06
On 19/ott/2012, at 10:41, David Welton wrote: >> Ciao, >> io di solito per organizzare il javascript uso un approccio simile a >> questo: >> http://blog.jerodsanto.net/2012/02/a-simple-patter... > > Questa mi sembra la risposta migliore e piu` pulita di quelle che ho > visto finora. Sono pienamente d'accordo, mi sembra un approccio molto elegante e pulito, ed quello che sto provando :-)
on 2012-10-19 12:06
2012/10/19 David Welton <davidnwelton@gmail.com> > diverse pagine con codice diverso per ogni pagina inizia a > molteplicarsi il lavoro: pagina X ha del codice associato che cerca il > suo data-foobar, ma lo stesso codice cerchera` data-foobar su pagine Y > e Z, mentre il codice per quelle pagine cerchera` data-y e data-z > nelle altre dove non c'e`, e cosi` via. O sbaglio? Forse non ha un > costo alto, ma comunque non mi sembra bellissimo. > E' vero che al document ready verranno effettuati n controlli di esistenza di elementi contenenti data-X, ma nella maggior parte dei casi mi paiono prestazionalmente irrilevanti. D'altro canto hai ottenuto il vantaggio di indebolire l'accoppiamento tra sorgenti ruby e sorgenti js. Nel caso del link postato, l'associazione Controller <-> Componente JS pu avere senso in qualche use case ma non va nella direzione "corretta" (non parlo di Rails way, parlo di "buon senso" :-)). In genere dovremmo ragionare per blocchi funzionali e far interagire solo i blocchi funzionali affini tra di loro, per i soliti concetti di riutilizzo, blah blah blah :-) Maurizio -- My profile <https://plus.google.com/100973969013103507046/about>
on 2012-10-19 12:47
Maurizio De magnis wrote in post #1080430: > > Nel caso del link postato, l'associazione Controller <-> Componente JS > pu > avere senso in qualche use case ma non va nella direzione "corretta" > (non > parlo di Rails way, parlo di "buon senso" :-)). > > In genere dovremmo ragionare per blocchi funzionali e far interagire > solo > i blocchi funzionali affini tra di loro, per i soliti concetti di > riutilizzo, blah blah blah :-) > Beh, niente ti impedisce di creare oggetti specifici per alcune funzioni dell'applicazione e richiamarli all'interno delle varie azioni legate ai "controller" javascript. Di solito faccio così. Ad esempio il BlahBlahController potrebbe avere un metodo new con new -> App.TooltipManager.addTooltip('myElement', "ciao, sono un tooltip"); App.DateManager.myDateConstraint('dateElement') // o (new App.DateManager('dateElement')).myDateConstraint() se preferite gli oggetti :-) Le cose belle di quest'approccio sono, secondo me: il fatto di sapere immediatamente dove si trova il javascript relativo ad una pagina; il fatto di isolare il javascript, evitando che per errore partano funzioni che interferiscano con il corretto funzionamento dell'applicazione.
on 2012-10-19 16:15
2012/10/19 Marco S. <marco.sormani@gmail.com> > > solo > App.DateManager.myDateConstraint('dateElement') > // o (new App.DateManager('dateElement')).myDateConstraint() se > preferite gli oggetti :-) > > Le cose belle di quest'approccio sono, secondo me: il fatto di sapere > immediatamente dove si trova il javascript relativo ad una pagina; il > fatto di isolare il javascript, evitando che per errore partano funzioni > che interferiscano con il corretto funzionamento dell'applicazione. Ok, stai limitando gli "svantaggi" dell'associazione controller <-> js, limitando la responsabilit del js bindato con il controller al mero instradamento :-) Rimane comunque la "noia" dell'update di questo mapping nell'eventualit che un componente JS venga usato in pi controller o qualora dovesse sorgere la necessit di spostare l'html "bersaglio" al di fuori del raggio d'azione del controller. E' un'associazione a mio parere pericolosa in casi di siti molto grossi. Ma sottolineo che se lo use case composto da pochi controller, questa associazione resta uno dei compromessi migliori :-) Maurizio -- My profile <https://plus.google.com/100973969013103507046/about>
on 2012-10-19 16:42
Maurizio De magnis wrote in post #1080455: > 2012/10/19 Marco S. <marco.sormani@gmail.com> > >> > solo >> App.DateManager.myDateConstraint('dateElement') >> // o (new App.DateManager('dateElement')).myDateConstraint() se >> preferite gli oggetti :-) >> >> Le cose belle di quest'approccio sono, secondo me: il fatto di sapere >> immediatamente dove si trova il javascript relativo ad una pagina; il >> fatto di isolare il javascript, evitando che per errore partano funzioni >> che interferiscano con il corretto funzionamento dell'applicazione. > > > Ok, stai limitando gli "svantaggi" dell'associazione controller <-> js, > limitando la responsabilit del js bindato con il controller al mero > instradamento :-) > Rimane comunque la "noia" dell'update di questo mapping nell'eventualit > che un componente JS venga usato in pi controller o qualora dovesse > sorgere la necessit di spostare l'html "bersaglio" al di fuori del > raggio > d'azione del controller. > E' un'associazione a mio parere pericolosa in casi di siti molto grossi. > Secondo me e` comodo soprattutto quando hai molto codice JS, quindi se hai siti grossi od una app con molto JS. Trovo soprattutto importante l'utilizzo della variabile App, che contiene tutto il codice JS del sito per evitare collisioni con altro codice, ma anche per il suo approccio OOP. Io lo vedo come i controller di Rails, quindi serve ad organizzare il codice. Delle volte mi e` successo di scrivere un matcher jQuery per una pagina, poi mi son reso conto che lo stesso codice faceva il matching con un elemento del DOM in un'altra pagina, ed in quella pagina invece non era una funzionalita` desiderata. Quindi mi torna il discorso "il fatto di isolare il javascript, evitando che per errore partano funzioni che interferiscano con il corretto funzionamento dell'applicazione." > Ma sottolineo che se lo use case composto da pochi controller, questa > associazione resta uno dei compromessi migliori :-) > > Maurizio > -- > My profile <https://plus.google.com/100973969013103507046/about>
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.