Olen koodaillut pitkästä aikaa erästä projektia Ember-kirjastolla, joka vaikuttaa kehittyneen hiljalleen eteenpäin. Emberistä puuttuu vielä kunnollinen tietomallikerros, minkä vuoksi pidän yhä Backbonesta, mutta sellainenkin näyttää olevan tulossa.

Tällä hetkellä olen sitä mieltä, että Backbone sopii yleispäteväksi MVC-apuvälineeksi tavallisten webbisivujen frontendiin, jossa näkymiä voidaan päivittää vähän huolettomammin renderöimällä isoja alueita kerralla uudelleen. Emberiä taas kannattaa harkita silloin, kun kyse on enemmän webapp-tyyppisestä sovelluksesta, jossa ruudulla esitetään paljon jatkuvasti muuttuvaa dynaamista dataa, ja kunkin yksittäisen pienen tietokentän tai luettelon halutaan päivittyvän itsenäisesti. Spine taas on ollut viime aikoina sen verran näkymätön projekti, etten tiedä onko sillä suurta tulevaisuutta.

Käytin äsken paljon aikaa erään asian ihmettelyyn Emberissä. Yritin nimittäin selvittää, miten saisin ajettua muutaman jQuery-funktion automaattisesti sen jälkeen, kun Ember on renderöinyt näkymän ruudulle. Tarkemmin sanoen halusin alustaa muutaman input-kentän käyttämään Select2-kirjastoa.

Vähän aikaa mietittyäni ja didInsertElement- ja rerender-metodeita kokeiltuani tajusin, että eihän Ember itse asiassa välttämättä renderöikään koko näkymää koskaan uudelleen, koska se käyttää HTML:ään upotettuja automaattisia sidoksia. DOM-puuta vain muokataan hieman silloin, kun näkymään sidotun Ember.ArrayControllerin sisältö muuttuu.

Toisin sanoen jQuery-alustukset pitäisi saada ajettua aina siinä vaiheessa, kun data on muuttunut ja muutokset on päivitetty DOMiin. Päädyin tämän tyyppiseen ratkaisuun:

<script type="text/x-handlebars" data-template-name="myTemplate">
  {{#each myData}}
    <input type="hidden" class="tag-select">
  {{/each}}
</script>

MyApp = Ember.Application.create
  myView: Ember.View.create
    templateName: 'myTemplate'
    myData: Ember.ArrayController.create
        content: [1, 2, 3]
    setupSelect: ( -> Ember.run.next this, -> Ember.run.next this, ->
      $('input.tag-select').select2
        tags: ['hello', 'world']
      ).observes 'myData.@each' # Setup select2 after data changes
    didInsertElement: ->
      # Setup select2 after first render
      this.setupSelect()

Mielenkiintoista ylläolevassa koodissa on se, että jouduin kutsumaan Ember.run.next()-metodia kahteen kertaan sisäkkäin. Muussa tapauksessa jQuery-koodi käynnistyi liian aikaisin, sillä Handlebars-templatessa oleva {{#each}}-alinäkymä ei ollut vielä välttämättä ehtinyt renderöityä. Oma koodi kannattaa testata huolella, sillä jos dataa ladataan ajaxilla palvelimelta, syntyy helposti race condition, jossa kaikki toimii välillä ihan hyvin ja välillä taas renderöinti tapahtuu liian aikaisin.

Jos joku on keksinyt siistimmän tavan tehdä sama asia, olisi mielenkiintoista kuulla siitä.

Published 18.6.2012