Cómo obtener atributos evaluados dentro de una directiva personalizada

Resuelto Shlomi Schwartz asked hace 12 años • 5 respuestas

Estoy intentando que me evalúen. de mi directiva personalizada, pero no encuentro la forma correcta de hacerlo.

He creado este jsFiddle para elaborarlo.

<div ng-controller="MyCtrl">
    <input my-directive value="123">
    <input my-directive value="{{1+1}}">
</div>

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+attr.value);
    }
});

¿Qué me estoy perdiendo?

Shlomi Schwartz avatar Sep 11 '12 20:09 Shlomi Schwartz
Aceptado

Aviso: actualizo esta respuesta a medida que encuentro mejores soluciones. También guardo las respuestas antiguas para referencia futura siempre que sigan relacionadas. La última y mejor respuesta es lo primero.

Mejor respuesta:

Las directivas en angularjs son muy poderosas, pero lleva tiempo comprender qué procesos se esconden detrás de ellas.

Al crear directivas, angularjs le permite crear un alcance aislado con algunos enlaces al alcance principal. Estos enlaces se especifican mediante el atributo que adjunta al elemento en DOM y cómo define la propiedad de alcance en el objeto de definición de directiva .

Hay 3 tipos de opciones de enlace que puede definir en el alcance y escribirlas como atributos relacionados con prefijos.

angular.module("myApp", []).directive("myDirective", function () {
    return {
        restrict: "A",
        scope: {
            text: "@myText",
            twoWayBind: "=myTwoWayBind",
            oneWayBind: "&myOneWayBind"
        }
    };
}).controller("myController", function ($scope) {
    $scope.foo = {name: "Umur"};
    $scope.bar = "qwe";
});

HTML

<div ng-controller="myController">
    <div my-directive my-text="hello {{ bar }}" my-two-way-bind="foo" my-one-way-bind="bar">
    </div>
</div>

En ese caso, en el alcance de la directiva (ya sea en la función de enlace o en el controlador), podemos acceder a estas propiedades de esta manera:

/* Directive scope */

in: $scope.text
out: "hello qwe"
// this would automatically update the changes of value in digest
// this is always string as dom attributes values are always strings

in: $scope.twoWayBind
out: {name:"Umur"}
// this would automatically update the changes of value in digest
// changes in this will be reflected in parent scope

// in directive's scope
in: $scope.twoWayBind.name = "John"

//in parent scope
in: $scope.foo.name
out: "John"


in: $scope.oneWayBind() // notice the function call, this binding is read only
out: "qwe"
// any changes here will not reflect in parent, as this only a getter .

"Aún está bien" Respuesta:

Dado que esta respuesta fue aceptada, pero tiene algunos problemas, la actualizaré a una mejor. Aparentemente, $parsees un servicio que no se encuentra en las propiedades del alcance actual, lo que significa que solo toma expresiones angulares y no puede alcanzar el alcance. {{, }}las expresiones se compilan mientras se inicia angularjs, lo que significa que cuando intentamos acceder a ellas en nuestro postlinkmétodo de directivas, ya están compiladas. ( ya {{1+1}}está 2en la directiva).

Así es como le gustaría usar:

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

myApp.directive('myDirective', function ($parse) {
    return function (scope, element, attr) {
        element.val("value=" + $parse(attr.myDirective)(scope));
    };
});

function MyCtrl($scope) {
    $scope.aaa = 3432;
}​

.

<div ng-controller="MyCtrl">
    <input my-directive="123">
    <input my-directive="1+1">
    <input my-directive="'1+1'">
    <input my-directive="aaa">
</div>​​​​​​​​

Una cosa que debe tener en cuenta aquí es que, si desea establecer la cadena de valor, debe colocarla entre comillas. (Ver 3ra entrada)

Aquí está el violín para jugar: http://jsfiddle.net/neuTA/6/

Antigua respuesta:

No voy a eliminar esto para las personas que pueden ser engañadas como yo, tenga en cuenta que usar $evalestá perfectamente bien la forma correcta de hacerlo, pero $parsetiene un comportamiento diferente, probablemente no necesitará esto en la mayoría de los casos.

La forma de hacerlo es, una vez más, utilizando scope.$eval. No solo compila la expresión angular, sino que también tiene acceso a las propiedades del alcance actual.

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

myApp.directive('myDirective', function () {
    return function (scope, element, attr) {
        element.val("value = "+ scope.$eval(attr.value));
    }
});

function MyCtrl($scope) {
   
}​

Lo que te faltaba era $eval.

http://docs.angularjs.org/api/ng.$rootScope.Scope#$eval

Ejecuta la expresión en el ámbito actual y devuelve el resultado. Cualquier excepción en la expresión se propaga (no se detecta). Esto es útil al evaluar expresiones angulares.

Umur Kontacı avatar Sep 11 '2012 14:09 Umur Kontacı

Para un valor de atributo que debe interpolarse en una directiva que no utiliza un alcance aislado, por ejemplo,

<input my-directive value="{{1+1}}">

utilizar el método de atributos $observe:

myApp.directive('myDirective', function () {
  return function (scope, element, attr) {
    attr.$observe('value', function(actual_value) {
      element.val("value = "+ actual_value);
    })
 }
});

Desde la página de directiva ,

observar atributos interpolados: Úselo $observepara observar los cambios de valor de los atributos que contienen interpolación (p. ej. src="{{bar}}"). Esto no solo es muy eficiente, sino que también es la única forma de obtener fácilmente el valor real porque durante la fase de vinculación la interpolación aún no se ha evaluado y, por lo tanto, el valor en este momento está establecido en undefined.

Si el valor del atributo es solo una constante, por ejemplo,

<input my-directive value="123">

puedes usar $eval si el valor es un número o booleano y quieres el tipo correcto:

return function (scope, element, attr) {
   var number = scope.$eval(attr.value);
   console.log(number, number + 1);
});

Si el valor del atributo es una constante de cadena, o desea que el valor sea de tipo cadena en su directiva, puede acceder a él directamente:

return function (scope, element, attr) {
   var str = attr.value;
   console.log(str, str + " more");
});

Sin embargo, en su caso, dado que desea admitir valores y constantes interpolados, utilice $observe.

Mark Rajcok avatar Sep 12 '2012 19:09 Mark Rajcok