He creado una directiva con un enlace mediante «ámbito de aplicación». En algunos casos, quiero enlazar un objeto constante. Por ejemplo, con HTML:

<div ng-controller="Ctrl">
    <greeting person="{firstName: 'Bob', lastName: 'Jones'}"></greeting>
</div>

y JavaScript:

var app = angular.module('myApp', []);

app.controller("Ctrl", function($scope) {

});

app.directive("greeting", function () {
    return {
        restrict: "E",
        replace: true,
        scope: {
            person: "="
        },
        template:
        '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
    };
});

Aunque esto funciona, también provoca un error de JavaScript:

Error: 10 $digest() iterations reached. Aborting!

(Violín demostrar el problema)

¿Cuál es la forma correcta para enlazar un objeto de constante sin causar el error?

OriginalEl autor Michael Williamson | 2013-06-05

6 Comentarios

  1. 11

    Aquí está la solución que se me ocurrió, con base en @sh0ber la respuesta:

    Implementar una costumbre link función. Si el atributo es válido JSON, entonces es un valor constante, por lo que sólo evaluar una vez. De lo contrario, ver y actualizar el valor normal (en otras palabras, trate de comportarse como un = de unión). scope necesita ser configurada para true para asegurarse de que el valor asignado sólo afecta a esta instancia de la directiva.

    (Ejemplo en jsFiddle)

    HTML:

    <div ng-controller="Ctrl">
        <greeting person='{"firstName": "Bob", "lastName": "Jones"}'></greeting>
        <greeting person="jim"></greeting>
    </div>

    JavaScript:

    var app = angular.module('myApp', []);
    
    app.controller("Ctrl", function($scope) {
        $scope.jim = {firstName: 'Jim', lastName: "Bloggs"};
    });
    
    app.directive("greeting", function () {
        return {
            restrict: "E",
            replace: true,
            scope: true,
            link: function(scope, elements, attrs) {
                try {
                    scope.person = JSON.parse(attrs.person);
                } catch (e) {
                    scope.$watch(function() {
                        return scope.$parent.$eval(attrs.person);
                    }, function(newValue, oldValue) {
                        scope.person = newValue;
                    });
                }   
            },
            template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
        };
    });

    OriginalEl autor Michael Williamson

  2. 7

    Usted está consiguiendo que el error porque Angular es la evaluación de la expresión cada vez. ‘=’ es para los nombres de variable.

    Aquí son dos formas alternativas para lograr el mismo pensar sin el error.

    Primera Solución:

    app.controller("Ctrl", function($scope) {
        $scope.person = {firstName: 'Bob', lastName: 'Jones'};
    });
    
    app.directive("greeting", function () {
        return {
            restrict: "E",
            replace: true,
            scope: {
                person: "="
            },
            template:
            '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
        };
    });
    
    <greeting person="person"></greeting>

    Segunda Solución:

    app.directive("greeting2", function () {
        return {
            restrict: "E",
            replace: true,
            scope: {
                firstName: "@",
                lastName: "@"
            },
            template:
            '<p>Hello {{firstName}} {{lastName}}</p>'
        };
    });
    
    <greeting2 first-name="Bob" last-Name="Jones"></greeting2>

    http://jsfiddle.net/7bNAd/82/

    Gracias por la respuesta. Por desgracia, la segunda solución no es posible ya que los datos reales estoy usando es profundamente anidadas. El primer caso es posible, pero un poco desordenado, ya que hay muchas instancias de la directiva se utiliza con valores constantes (que son generados en el lado del servidor).

    OriginalEl autor martinpaulucci

  3. 4

    Otra opción:

    app.directive("greeting", function () {
        return {
            restrict: "E",
            link: function(scope,element,attrs){
                scope.person = scope.$eval(attrs.person);
            },
            template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>'
        };
    });

    OriginalEl autor Dan

  4. 2

    Esto es porque si usted utiliza el = tipo de ámbito vínculo de campo, el valor del atributo que se observa para los cambios, pero las pruebas para la igualdad de referencia (con !==) más que probado profundamente por la igualdad. Especificando literal de objeto en línea hará que angulares para crear el nuevo objeto cuando el atributo se accede para obtener su valor — así pues, cuando angular hace sucio de comprobación, comparando el valor anterior a la actual, siempre indica el cambio.

    Una manera de superar esa sería modificar angular de la fuente como se describe aquí:

    https://github.com/mgonto/angular.js/commit/09d19353a2ba0de8edcf625aa7a21464be830f02.

    De lo contrario, se podría crear un objeto en el controlador y hacer referencia a él por su nombre en el elemento del atributo:

    HTML

    <div ng-controller="Ctrl">
        <greeting person="personObj"></greeting>
    </div>

    JS

    app.controller("Ctrl", function($scope)
    {
        $scope.personObj = { firstName : 'Bob', lastName : 'Jones' };
    });

    Otra manera es crear el objeto en el elemento principal del ng-init directiva y posteriormente hacer referencia a él por su nombre (pero esta es menos legible):

    <div ng-controller="Ctrl" ng-init="personObj = { firstName : 'Bob', lastName : 'Jones' }">
        <greeting person="personObj"></greeting>
    </div>

    OriginalEl autor mirrormx

  5. 0

    No me gusta el uso de eval(), pero si usted realmente desea conseguir que esto funcione con el código HTML que proporciona:

    app.directive("greeting", function() {
        return {
            restrict: "E",
            compile: function(element, attrs) {
                eval("var person = " + attrs.person);
                var htmlText = '<p>Hello ' + person.firstName + ' ' + person.lastName + '</p>';
                element.replaceWith(htmlText);
            }
        };
    });

    OriginalEl autor Mark Rajcok

  6. 0

    Tuve el mismo problema, lo resuelto por el análisis de las json en el paso de compilación:

    angular.module('foo', []).
    directive('myDirective', function () {
        return {
            scope: {
                myData: '@'
            },
            controller: function ($scope, $timeout) {
                $timeout(function () {
                    console.log($scope.myData);
                });
            },
            template: "{{myData | json}} a is  {{myData.a}} b is {{myData.b}}",
            compile: function (element, attrs) {
                attrs['myData'] = angular.fromJson(attrs['myData']);
            }
        };
    });

    El único inconveniente es que el $scope no se llena inicialmente cuando el controlador se ejecuta en primer lugar.

    Aquí un JSFiddle con este código.

    OriginalEl autor Peter Kovacs

Dejar respuesta

Please enter your comment!
Please enter your name here