Eu tenho três maneiras diferentes de inicializar e renderizar uma exibição e suas subvisões, e cada uma delas tem problemas diferentes. Estou curioso para saber se existe uma maneira melhor de resolver todos os problemas:
Cenário Um:
Inicialize os filhos na função de inicialização dos pais. Dessa forma, nem tudo fica preso na renderização para que haja menos bloqueio na renderização.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.render().appendTo(this.$('.container-placeholder');
}
Os problemas:
O maior problema é que a chamada de renderização no pai pela segunda vez removerá todas as ligações de eventos do filho. (Isso ocorre por causa do funcionamento do jQuery
$.html()
.) Isso pode ser atenuado pela chamadathis.child.delegateEvents().render().appendTo(this.$el);
, mas no primeiro caso, e na maioria das vezes, você está fazendo mais trabalho desnecessariamente.Ao anexar os filhos, você força a função de renderização a ter conhecimento da estrutura DOM dos pais para obter a ordem desejada. O que significa que a alteração de um modelo pode exigir a atualização da função de renderização de uma exibição.
Cenário Dois:
Inicialize os filhos no pai initialize()
ainda, mas em vez de anexar, use setElement().delegateEvents()
para definir o filho como um elemento no modelo dos pais.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}
Problemas:
- Isso torna o
delegateEvents()
necessário agora, o que é um pouco negativo, pois só é necessário em chamadas subsequentes no primeiro cenário.
Cenário Três:
Inicialize os filhos no render()
método dos pais .
initialize : function () {
//parent init stuff
},
render : function () {
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
Problemas:
Isso significa que agora a função de renderização também deve ser associada a toda a lógica de inicialização.
Se eu editar o estado de uma das visualizações filho e, em seguida, chamar render no pai, um filho completamente novo será criado e todo o seu estado atual será perdido. O que também parece que pode ficar arriscado por vazamentos de memória.
Realmente curioso para entender o que os seus caras fazem. Qual cenário você usaria? ou existe um quarto mágico que resolve todos esses problemas?
Você já acompanhou um estado renderizado para uma View? Diga uma renderedBefore
bandeira? Parece realmente irregular.
fonte
delegateEvents()
após osetElement()
? Conforme docs: "... e mova os eventos delegados da exibição do elemento antigo para o novo", osetElement
próprio método deve lidar com a re-delegação de eventos.Respostas:
Esta é uma grande pergunta. O backbone é ótimo por causa da falta de suposições, mas significa que você precisa (decidir como) implementar coisas como essa. Depois de analisar minhas próprias coisas, acho que (meio que) uso uma mistura dos cenários 1 e 2. Não acho que exista um quarto cenário mágico porque, simplesmente, tudo o que você faz nos cenários 1 e 2 deve ser feito.
Eu acho que seria mais fácil explicar como eu gosto de lidar com isso com um exemplo. Digamos que esta página simples seja dividida nas visualizações especificadas:
Digamos que o HTML seja, depois de renderizado, algo como isto:
Espero que seja bastante óbvio como o HTML corresponde ao diagrama.
Ele
ParentView
possui 2 visualizações filhoInfoView
ePhoneListView
algumas divs extras, uma das quais#name
precisa ser definida em algum momento.PhoneListView
possui visualizações filho próprias, uma matriz dePhoneView
entradas.Então, vamos à sua pergunta real. Lido com a inicialização e a renderização de maneira diferente, com base no tipo de exibição. Eu divido minhas visões em dois tipos,
Parent
visões eChild
visões.A diferença entre eles é simples: as
Parent
visualizações mantêm as visualizações filho, enquanto asChild
visualizações não. Então, no meu exemplo,ParentView
ePhoneListView
sãoParent
visualizações, enquantoInfoView
e asPhoneView
entradas sãoChild
visualizações.Como mencionei antes, a maior diferença entre essas duas categorias é quando elas podem ser renderizadas. Em um mundo perfeito, quero que as
Parent
visualizações sejam renderizadas apenas uma vez. Cabe às exibições filhas deles lidar com qualquer nova renderização quando os modelos forem alterados.Child
visualizações, por outro lado, permito reproduzir novamente a qualquer momento, pois elas não têm outras visualizações.Em um pouco mais detalhadamente, para
Parent
visualizações, gosto de minhasinitialize
funções para fazer algumas coisas:InfoView
seria atribuído#info
).O passo 1 é bastante auto-explicativo.
A etapa 2, a renderização, é feita para que todos os elementos que os filhos exibem já existam antes que eu tente atribuí-los. Ao fazer isso, eu conheço todas as crianças
events
serão definidas corretamente e posso renderizar novamente seus blocos quantas vezes quiser, sem me preocupar em ter que delegar novamente nada. Na verdade, não tenhorender
nenhuma visão infantil aqui, deixo que eles façam isso sozinhosinitialization
.As etapas 3 e 4 são realmente tratadas ao mesmo tempo em que passo
el
ao criar a exibição filho. Gosto de passar um elemento aqui, pois acho que o pai deve determinar onde, em sua própria visão, o filho pode colocar seu conteúdo.Para renderização, tento mantê-lo bastante simples para
Parent
visualizações. Quero que arender
função não faça nada além de renderizar a exibição pai. Nenhuma delegação de evento, nenhuma renderização de visualizações filho, nada. Apenas uma renderização simples.Às vezes, isso nem sempre funciona. Por exemplo, no meu exemplo acima, o
#name
elemento precisará ser atualizado sempre que o nome no modelo for alterado. No entanto, esse bloco faz parte doParentView
modelo e não é tratado por umaChild
exibição dedicada , portanto, resolvo isso. Vou criar algum tipo desubRender
função que substitui apenas o conteúdo do#name
elemento e não precisa descartar o#parent
elemento inteiro . Isso pode parecer um hack, mas eu realmente achei que funciona melhor do que ter que me preocupar em renderizar novamente todo o DOM e reconectar elementos. Se eu realmente quisesse torná-lo limpo, criaria uma novaChild
exibição (semelhante àInfoView
) que lidaria com o#name
bloco.Agora, para
Child
visualizações, elainitialization
é bastante semelhante àsParent
visualizações, apenas sem a criação de outrasChild
visualizações. Assim:Child
ver renderização também é muito simples, basta renderizar e definir o conteúdo do meuel
. Novamente, não mexa com delegação ou algo assim.Aqui está um exemplo de código de como minha
ParentView
aparência pode ser:Você pode ver minha implementação
subRender
aqui. Por ter alterações vinculadas a, esubRender
nãorender
, não preciso me preocupar em explodir e reconstruir todo o bloco.Aqui está um código de exemplo para o
InfoView
bloco:As ligações são a parte importante aqui. Ao vincular meu modelo, nunca preciso me preocupar em chamar manualmente
render
me . Se o modelo for alterado, esse bloco será renderizado novamente sem afetar outras visualizações.O
PhoneListView
será semelhante aoParentView
, você só precisa de um pouco mais lógica em ambos os seusinitialization
erender
funções para coleções punho. A decisão de como você lida com a coleção é realmente sua, mas pelo menos você precisará ouvir os eventos da coleção e decidir como deseja renderizar (acrescentar / remover ou apenas renderizar novamente o bloco inteiro). Pessoalmente, gosto de acrescentar novas exibições e remover as antigas, e não renderizar novamente a exibição inteira.O
PhoneView
será quase idêntico aoInfoView
, apenas ouvindo as mudanças de modelo com as quais se preocupa.Espero que isso tenha ajudado um pouco, por favor, deixe-me saber se algo é confuso ou não é detalhado o suficiente.
fonte
render
dentro doinitialize
método é uma prática ruim, porque impede que você seja mais eficiente nos casos em que não deseja render imediatamente. O que você pensa sobre isso?PhoneListView
?Não tenho certeza se isso responde diretamente à sua pergunta, mas acho que é relevante:
http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
O contexto em que eu configurei este artigo é diferente, é claro, mas acho que as duas soluções que ofereço, juntamente com os prós e os contras de cada uma delas, devem fazer com que você se mova na direção certa.
fonte
Para mim, não parece a pior idéia do mundo diferenciar entre a configuração inicial e as configurações subseqüentes de suas visualizações por meio de algum tipo de sinalizador. Para tornar isso fácil e limpo, o sinalizador deve ser adicionado à sua própria exibição, que deve estender a exibição Backbone (Base).
Assim como Derick, não tenho certeza absoluta de que isso responda diretamente à sua pergunta, mas acho que talvez valha a pena mencionar nesse contexto.
fonte
Kevin Peel dá uma ótima resposta - aqui está a minha versão tl; dr:
fonte
Estou tentando evitar o acoplamento entre visualizações como essas. Normalmente, existem duas maneiras de fazer isso:
Use um roteador
Basicamente, você permite que a função do roteador inicialize a exibição pai e filho. Portanto, a exibição não tem conhecimento um do outro, mas o roteador lida com tudo.
Passando o mesmo el para as duas visualizações
Ambos têm conhecimento do mesmo DOM e você pode solicitá-los da maneira que desejar.
fonte
O que faço é dar a cada criança uma identidade (que o Backbone já fez por você: cid)
Quando o Container faz a renderização, o uso de 'cid' e 'tagName' gera um espaço reservado para cada filho, portanto, no modelo, os filhos não têm idéia de onde serão colocados pelo Container.
do que você pode usar
nenhum espaço reservado especificado é necessário e o Container gera apenas o espaço reservado, em vez da estrutura DOM dos filhos. Cotainer e Children ainda estão gerando elementos DOM próprios e apenas uma vez.
fonte
Aqui está uma combinação leve para criar e renderizar sub-visualizações, que eu acho que resolve todos os problemas deste segmento:
https://github.com/rotundasoftware/backbone.subviews
A abordagem adotada por este plug-in é criar e renderizar subvisões após a primeira vez que a visualização pai é renderizada. Em seguida, nas renderizações subsequentes da visualização pai, $ .detalhe os elementos da subvisão, renderize novamente o pai, insira os elementos da subvisão nos locais apropriados e os renderize novamente. Dessa maneira, os subviews objetos são reutilizados nas renderizações subseqüentes e não há necessidade de delegar novamente os eventos.
Observe que o caso de uma exibição de coleção (onde cada modelo da coleção é representado com uma subvisão) é bem diferente e merece sua própria discussão / solução, eu acho. A melhor solução geral que conheço nesse caso é o CollectionView in Marionette .
EDIT: Para o caso de exibição de coleção, você também pode conferir esta implementação mais focada na interface do usuário , se precisar de seleção de modelos com base em cliques e / ou arrastar e soltar para reordenar.
fonte