Ember.js - Usando um auxiliar de Handlebars para detectar que uma subvisualização foi renderizada

Existem inúmeras perguntas que perguntam de uma forma ou de outra: "Como faço alguma coisa depois que alguma parte de uma exibição é renderizada?" (Aqui, AquieAqui apenas para dar alguns). A resposta é geralmente:

usardidInsertElement para executar código quando uma exibição éinicialmente renderizado.usarEmber.run.next(...) para executar seu códigodepois de as alterações de exibição são liberadas, se você precisar acessar os elementos DOM criados.use um observador emisLoaded ou uma propriedade semelhante para fazer algo após odados você precisa está carregado.

O que é irritante nisso é que isso leva a coisas muito desajeitadas como esta:

didInsertElement: function(){
    content.on('didLoad', function(){
        Ember.run.next(function(){
            // now finally do my stuff
        });
    });
}

E isso nem sempre funciona necessariamente quando você está usando dados de brasão porqueisLoaded pode já ser verdade (se o registro já foi carregado antes e não é solicitado novamente do servidor). Então, obter o seqüenciamento correto é difícil.

Além disso, você provavelmente já está assistindo isLoaded no seu modelo de visualização da seguinte forma:

{{#if content.isLoaded}}
    <input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
    <div>Loading data...</div>
{{/if}}

e fazê-lo novamente no seu controlador parece duplicação.

Eu criei uma solução um pouco nova, mas ela precisa de trabalho ou é realmente uma má ideia ... caso contrário, pode ser verdade:

Eu escrevi um pequeno ajudante de guiador chamado{{fire}} que disparará um evento com um nome personalizado quando o modelo de guiador de contenção for executado (ou seja, deve ser sempre que a subvisualização for renderizada novamente, certo?).

Aqui está o meumuito tentativa precoce:

Ember.Handlebars.registerHelper('fire', function (evtName, options) {
    if (typeof this[evtName] == 'function') {
        var context = this;
        Ember.run.next(function () {
            context[evtName].apply(context, options);
        });
    }
});

que é usado assim:

{{#if content.isLoaded}}
    {{fire typeaheadHostDidRender}}
    <input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
    <div>Loading data...</div>
{{/if}}

Isso essencialmente funciona como está, mas tem algumas falhas que eu já conheço:

Ele chama o método no controlador ... provavelmente seria melhor pelo menos sercapaz para enviar o "evento" para o objeto de visão ancestral em vez disso, talvez até para fazer com que o comportamento padrão. eu tentei{{fire typeaheadHostDidRender target="view"}} e isso não funcionou. Ainda não vejo como obter a visão "atual" do que é passado para o assistente, mas obviamente{{view}} O ajudante pode fazer isso.Eu estou supondo que há uma maneira mais formal de acionar um evento personalizado do que o que estou fazendo aqui, mas ainda não aprendi isso. jQuery's.trigger() parece não funcionar nos objetos do controlador, embora possa funcionar nas visualizações. Existe uma maneira "Ember" para fazer isso?Pode haver coisas que eu não entendo, como um caso em que esse evento seria acionado, mas a exibição não seria, de fato, adicionada ao DOM ...?

Como você pode imaginar, estou usando o controle Typeahead do Bootstrap e preciso conectá-lo após o<input> é processado, o que na verdade só acontece depois de vários aninhados{{#if}} blocos são avaliados como verdadeiros no meu modelo. Eu também uso o jqPlot, então eu me deparo com a necessidade desse padrão muito. Esta parece ser uma ferramenta viável e útil, mas pode ser que eu esteja perdendo algo grande que torna essa abordagem burra. Ou talvez haja outra maneira de fazer isso que não tenha aparecido nas minhas pesquisas?

Alguém pode melhorar essa abordagem para mim ou me dizer por que é uma má ideia?

ATUALIZAR

Eu percebi alguns dos bits fora:

Eu posso obter o primeiro "real" contendo vista comoptions.data.view.get('parentView')... óbvio, talvez, mas não achei que seria assim tão simples.Você realmente pode fazer um estilo jQueryobj.trigger(evtName) em qualquer objeto arbitrário ... mas o objeto deve estender oEmber.Evented mixin! Então, suponho que seja a maneira correta de fazer esse tipo de evento enviando em Ember. Apenas certifique-se de que o alvo pretendido se estendeEmber.Evented (visualizações já fazem).

Aqui está a versão melhorada até agora:

Ember.Handlebars.registerHelper('fire', function (evtName, options) {
    var view = options.data.view;
    if (view.get('parentView')) view = view.get('parentView');

    var context = this;
    var target = null;
    if (typeof view[evtName] == 'function') {
        target = view;
    } else if (typeof context[evtName] == 'function') {
        target = context;
    } else if (view.get('controller') && typeof view.get('controller')[evtName] == 'function') {
        target = view.get('controller');
    }

    if (target) {
        Ember.run.next(function () {
            target.trigger(evtName);
        });
    }
});

Agora, quase tudo que eu sinto falta é descobrir como passar no alvo pretendido (por exemplo, o controlador ou a visualização - o código acima tenta adivinhar). Ou descobrir se há algum comportamento inesperado que quebra todo o conceito.

Alguma outra entrada?

questionAnswers(1)

yourAnswerToTheQuestion