Representar valor sin vinculación de datos

87

En AngularJS, ¿cómo puedo representar un valor sin enlace de datos bidireccional? Es posible que desee hacer esto por razones de rendimiento, o incluso generar un valor en un momento dado.

Los siguientes ejemplos utilizan ambos enlaces de datos:

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

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

¿Cómo renderizo value sin ningún enlace de datos?

Blowsie
fuente
cuál es su entrada y salida. por favor explique
Nitish Kumar
3
Sus ejemplos son en realidad enlace de datos unidireccional (cambios de modelo -> ver actualizaciones). ng-modelle ofrece enlace de datos bidireccional: cambios de modelo -> ver actualizaciones, ver cambios -> actualizaciones de modelo.
Mark Rajcok
1
actualizado. lo siento, quise decir que no quiero ningún enlace de datos en absoluto
Blowsie
10
No creo que esta pregunta sea terrible o merezca un voto negativo. En realidad, es muy común querer deshabilitar el enlace de datos para evitar relojes innecesarios.
OverZealous
4
ACTUALIZACIÓN: cualquiera que lea este artículo probablemente encontrará este video extremadamente útil. youtube.com/watch?v=zyYpHIOrk_Y
Blowsie

Respuestas:

141

Angular 1.3+

En 1.3, Angular lo ha admitido utilizando la siguiente sintaxis.

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

Como se menciona en esta respuesta .


Angular 1.2 y menos

Esto es simple y no necesita un complemento. Mira esto.

Esta pequeña directiva logrará fácilmente lo que está tratando de lograr

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

Puedes unir una vez así

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

Puedes unir como de costumbre

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

Demostración: http://jsfiddle.net/fffnb/

Algunos de ustedes pueden estar usando batarang angular, y como se menciona en los comentarios, si usa esta directiva, el elemento todavía se muestra como vinculante cuando no lo es, estoy bastante seguro de que esto tiene algo que ver con las clases que están adjuntas al elemento, por lo que intente esto, debería funcionar (no probado) . Déjame saber en los comentarios si funcionó para ti.

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

@ x0b : Si tiene TOC y desea eliminar el classatributo vacío, haga esto

!$element.attr('class') && $element.removeAttr('class')
iConnor
fuente
Todavía tengo que probar el complemento, pero supongo que las herramientas de cromo de AngularJS no mostrarían el elemento bind-once como un enlace, como lo hace su ejemplo. Aunque es un enfoque interesante, probaré ambos enfoques pronto.
Blowsie
Vea esto: se muestran ambos como enlaces dl.dropboxusercontent.com/u/14037764/Development/stackoverflow/…
Blowsie
1
Sin lugar a dudas, eso se debe a que si la clase ng-binding que puede eliminar fácilmente
iConnor
4
Esto es genial y mucho más simple que el complemento bindonce. Agregué la capacidad de esperar una condición antes de destruir el alcance y es realmente útil. Gracias.
Yaron
1
@Connor no estoy de acuerdo. Por ejemplo, estoy recibiendo un objeto de video ($ scope.video) de una API REST y quiero un enlace único del título del video ($ scope.video.title). Incluso si resuelvo la promesa ANTES de agregarla al alcance en el controlador, todavía tengo que declarar ng-bind = "video.title" bind-once en el DOM. Ahora, antes de que se resuelva la promesa, video.title no está definido y el alcance se destruye antes de que se defina video.title. Una solución que tengo para esto es envolver los elementos en algún tipo de bandera de carga / inicio, ng-if = "someLoadingFlag", pero es un patrón deficiente.
SirTophamHatt
49

Parece que Angular 1.3 (comenzando con beta 10) tiene un enlace único integrado:

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

Encuadernación única

Una expresión que comienza con :: se considera una expresión de una sola vez. Las expresiones únicas dejarán de recalcularse una vez que sean estables, lo que ocurre después del primer resumen si el resultado de la expresión es un valor no indefinido (consulte el algoritmo de estabilización de valor a continuación).

Karen Zilles
fuente
1
Esta respuesta una y otra vez. ¡No puedo alabarte lo suficiente, Karl! Recomiendo encarecidamente el uso agresivo de esta función siempre que tenga sentido.
XDS
1
Wow, estoy muy contento de haber bajado el scroll. Le voy a pedir a Connor que haga referencia a esto en su respuesta aceptada.
JSager
Tengo una tabla / lista con 2000 líneas y al usar el operador de enlace único, mi aplicación se vuelve extremadamente lenta cuando se muestra / renderiza la lista por primera vez. ¡Tan lento, que el navegador me pregunta dos o tres veces si quiero dejar de ejecutar el script!
Billy G
@ billy-g ¿Puedes publicar un jsfiddle o plunker que ilustre el problema?
James Daily
@James Daily: Aquí está el caso "normal" plnkr.co/edit/rCRP0T5fSgNIllx7F27y y aquí el caso "expresión de una sola vez" plnkr.co/edit/Rd5VBVjkcX3sTJYGypUr pero ... no puedo reproducirlo allí. De todos modos, no es más rápido con la "expresión única" y tengo que investigar más para encontrar por qué sucede en mi entorno (uso 1.3 beta 18 de angularjs)
Billy G
20

Utilice el módulo bindonce . Deberá incluir el archivo JS y agregarlo como una dependencia al módulo de su aplicación:

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

Esta biblioteca le permite renderizar elementos que están enlazados solo una vez, cuando se inicializan por primera vez. Se ignorará cualquier actualización adicional de esos valores. Es una excelente manera de reducir la cantidad de reproducciones en la página para las cosas que no cambiarán después de su procesamiento.

Ejemplo de uso:

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

Cuando se usa así, la propiedad debajo valuese establecerá una vez que esté disponible, pero luego el reloj se desactivará.

Demasiado entusiasta
fuente
1
Estaba a punto de escribir una respuesta "escribe tu propia directiva ...", pero parece que alguien ya lo ha hecho por nosotros, bueno.
Mark Rajcok
3
Bindonce es lo suficientemente útil como para incluirlo como una biblioteca opcional incorporada, como $resource.
OverZealous
6
esto es lo que estaba buscando, sin embargo, ¡esperaba que algo como esto se construyera en angular!
Blowsie
7

Comparación entre @OverZealous y @Connor responde:

Con el ngRepeat tradicional de angular: 15s para 2000 filas y 420mo de RAM ( Plunker )

Con ngRepeat y el módulo de @OverZealous: 7s para 2000 filas y 240mo de RAM ( Plunker )

Con ngRepeat y la directiva de @Connor: 8s para 2000 filas y 500mo de RAM ( Plunker )

Hice mis pruebas con Google Chrome 32.

Gabriel
fuente
1
Sería bueno haberlo angular-oncecomparado también . Gracias.
alecxe
@alecxe: Planeé hacer las pruebas cuando se publique una compilación estable de AngularJS 1.3.
Gabriel
Gracias, no olvides incluir el angular-oncepaquete (lo he publicado como una opción alternativa aquí).
alecxe
5

Como alternativa, hay angular-oncepaquete:

Si usa AngularJS, tiene problemas de rendimiento y necesita mostrar muchos datos de solo lectura, ¡este proyecto es para usted!

angular-oncese inspiró en realidad bindoncey proporciona once-*atributos similares :

<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
fuente