Renderizar valor sem vinculação de dados

87

No AngularJS, como posso renderizar um valor sem vinculação de dados bidirecional? Pode-se desejar fazer isso por motivos de desempenho ou até mesmo renderizando um valor em um determinado momento.

Os exemplos a seguir usam vinculação de dados:

<div>{{value}}</div>

<div data-ng-bind="value"></div>

Como faço para renderizar value sem qualquer vinculação de dados?

Blowsie
fonte
qual é a sua entrada e saída. Por favor, explique
Nitish Kumar
3
Seus exemplos são, na verdade, vinculação de dados unilateral (alterações de modelo -> atualizações de visualização). ng-modelfornece ligação de dados bidirecional: alterações de modelo -> atualizações de visualização, alterações de visualização -> atualizações de modelo.
Mark Rajcok
1
Atualizada. desculpe, eu quis dizer que não quero vinculação de dados
Blowsie
10
Não acho que essa pergunta seja terrível ou merecesse um voto negativo. Na verdade, é muito comum querer desabilitar a vinculação de dados para evitar relógios desnecessários.
OverZealous
4
ATUALIZAÇÃO: quem lê este artigo provavelmente achará este vídeo extremamente útil. youtube.com/watch?v=zyYpHIOrk_Y
Blowsie

Respostas:

141

Angular 1.3+

No 1.3, o Angular oferece suporte para isso usando a seguinte sintaxe.

<div>{{::message}}</div>

Conforme mencionado nesta resposta .


Angular 1.2 e abaixo

Isso é simples e não precisa de um plugin. Veja isso.

Esta pequena diretriz realizará facilmente o que você está tentando alcançar

app.directive('bindOnce', function() {
    return {
        scope: true,
        link: function( $scope ) {
            setTimeout(function() {
                $scope.$destroy();
            }, 0);
        }
    }
});

Você pode ligar uma vez assim

<div bind-once>I bind once - {{message}}</div>

Você pode ligar normalmente

<div ng-bind="message" bind-once></div>

Demo: http://jsfiddle.net/fffnb/

Alguns de vocês podem estar usando o batarang angular e, conforme mencionado nos comentários, se você usar esta diretiva, o elemento ainda mostra como vinculativo quando não é, tenho certeza que isso tem algo a ver com as classes que estão anexadas ao elemento. tente fazer isso, deve funcionar (não testado) . Deixe-me saber nos comentários se funcionou para você.

app.directive('bindOnce', function() {
    return {
        scope: true,
        link: function( $scope, $element ) {
            setTimeout(function() {
                $scope.$destroy();
                $element.removeClass('ng-binding ng-scope');
            }, 0);
        }
    }
});

@ x0b : Se você tem TOC e deseja remover o classatributo vazio, faça isso

!$element.attr('class') && $element.removeAttr('class')
iConnor
fonte
Ainda estou para testar o plug-in, mas presumo que as ferramentas de cromo do AngularJS não mostrariam o elemento bind-once como um vínculo, como mostra seu exemplo. Abordagem interessante embora, testarei ambas as abordagens em breve.
Blowsie
Veja isto - mostra ambos como ligações dl.dropboxusercontent.com/u/14037764/Development/stackoverflow/…
Blowsie
1
Sem dúvida, é porque se a classe ng-binding que você pode remover facilmente
iConnor
4
Isso é ótimo e muito mais simples do que o plug-in bindonce. Eu adicionei a capacidade de esperar por uma condição antes de destruir o escopo e é muito útil. obrigado.
Yaron
1
@Connor discordo. Por exemplo, estou recebendo um objeto de vídeo ($ scope.video) de uma API REST e quero vinculação única do título de vídeo ($ scope.video.title). Mesmo se eu resolver a promessa ANTES de adicioná-la ao escopo no controlador, ainda tenho que declarar ng-bind = "video.title" bind-uma vez no DOM. Agora, antes que a promessa seja resolvida, video.title é indefinido e o escopo é destruído antes que video.title seja definido. Uma solução que tenho para isso é envolver os elementos em algum tipo de flag de carregamento / init, ng-if = "someLoadingFlag", mas é um padrão pobre.
SirTophamHatt
49

Parece que o Angular 1.3 (começando com beta 10) tem uma vinculação única integrada:

https://docs.angularjs.org/guide/expression#one-time-binding

Encadernação única

Uma expressão que começa com :: é considerada uma expressão única. As expressões ocasionais deixarão de recalcular quando estiverem estáveis, o que acontece após o primeiro resumo se o resultado da expressão for um valor não indefinido (consulte o algoritmo de estabilização de valor abaixo).

Karen Zilles
fonte
1
Esta resposta novamente e novamente. Eu não posso te elogiar o suficiente, Karl! Eu recomendo fortemente o uso agressivo desse recurso sempre que fizer sentido.
XDS
1
Uau, estou realmente feliz por ter rolado para baixo. Vou pedir a Connor para fazer referência a isso em sua resposta aceita.
JSager
Eu tenho uma tabela / lista com 2.000 linhas e usando o operador de ligação única, meu aplicativo fica extremamente lento ao mostrar / renderizar a lista pela primeira vez. Tão lento que o navegador me pergunta duas ou três vezes se quero parar de executar o script!
Billy G
@ billy-g Você pode postar um jsfiddle ou plunker ilustrando o problema?
James Daily
@James Daily: Aqui está o caso "normal" plnkr.co/edit/rCRP0T5fSgNIllx7F27y e aqui o caso "expressão única" plnkr.co/edit/Rd5VBVjkcX3sTJYGypUr, mas ... não posso reproduzi-lo lá. De qualquer forma, não é mais rápido com a "expressão única" e tenho que fazer mais pesquisas para descobrir por que isso acontece no meu ambiente (eu uso 1,3 beta 18 do angularjs)
Billy G
20

Use o módulo bindonce . Você precisará incluir o arquivo JS e adicioná-lo como uma dependência ao módulo do aplicativo:

var myApp = angular.module("myApp", ['pasvaz.bindonce']);

Esta biblioteca permite renderizar itens que são vinculados apenas uma vez - quando são inicializados pela primeira vez. Quaisquer outras atualizações desses valores serão ignoradas. É uma ótima maneira de reduzir o número de relógios na página para coisas que não mudarão depois de serem renderizadas.

Exemplo de uso:

<div bo-text="value"></div>

Quando usado dessa forma, a propriedade em valueserá definida assim que estiver disponível, mas o relógio será desabilitado.

Excesso de zelo
fonte
1
Eu estava prestes a escrever uma resposta "escreva sua própria diretiva ...", mas parece que alguém já fez isso por nós, ótimo.
Mark Rajcok
3
O Bindonce é útil o suficiente para ser incluído como uma biblioteca opcional embutida, como $resource.
OverZealous
6
isso é o que eu estava procurando, mas esperava algo assim para ser integrado ao angular!
Blowsie
7

Comparação entre as respostas @OverZealous e @Connor:

Com o ngRepeat tradicional de angular: 15s para 2.000 linhas e 420mo de RAM ( Plunker )

Com ngRepeat e o módulo de @OverZealous: 7s para 2.000 linhas e 240mo de RAM ( Plunker )

Com ngRepeat e a diretiva de @Connor: 8s para 2.000 linhas e 500mo de RAM ( Plunker )

Fiz meus testes com o Google Chrome 32.

Gabriel
fonte
1
Seria bom também ter angular-oncecomparado. Obrigado.
alecxe de
@alecxe: Eu planejei fazer os testes quando uma versão estável do AngularJS 1.3 fosse publicada.
Gabriel,
Obrigado, não se esqueça de incluir o angular-oncepacote (eu postei como uma opção alternativa aqui).
alecxe
5

Como alternativa, existe o angular-oncepacote:

Se você usa AngularJS, tem problemas de desempenho e precisa exibir muitos dados somente leitura, este projeto é para você!

angular-oncefoi realmente inspirado bindoncee fornece once-*atributos semelhantes :

<ul>
    <li ng-repeat="user in users">
      <a once-href="user.profileUrl" once-text="user.name"></a>
        <a once-href="user.profileUrl"><img once-src="user.avatarUrl"></a>
        <div once-class="{'formatted': user.description}" once-bind="user.description"></div>
    </li>
</ul>
Alecxe
fonte