Configuración de variables de alcance dinámico en AngularJs - scope. <some_string>

97

Tengo una cadena que obtuve de un routeParamatributo de directiva o lo que sea, y quiero crear una variable en el alcance basada en esto. Entonces:

$scope.<the_string> = "something".

Sin embargo, si la cadena contiene uno o más puntos, quiero dividirla y "profundizar" en el alcance. Así 'foo.bar'debería ser $scope.foo.bar. ¡Esto significa que la versión simple no funcionará!

// This will not work as assigning variables like this will not "drill down"
// It will assign to a variables named the exact string, dots and all.
var the_string = 'life.meaning';
$scope[the_string] = 42;
console.log($scope.life.meaning);  // <-- Nope! This is undefined.
console.log($scope['life.meaning']);  // <-- It is in here instead!

Al leer una variable basada en una cadena, puede obtener este comportamiento al hacerlo $scope.$eval(the_string), pero ¿cómo hacerlo al asignar un valor?

Erik Honn
fuente

Respuestas:

174

La solución que he encontrado es usar $ parse .

"Convierte la expresión angular en una función".

Si alguien tiene una mejor, agregue una nueva respuesta a la pregunta.

Aquí está el ejemplo:

var the_string = 'life.meaning';

// Get the model
var model = $parse(the_string);

// Assigns a value to it
model.assign($scope, 42);

// Apply it to the scope
// $scope.$apply(); <- According to comments, this is no longer needed

console.log($scope.life.meaning);  // logs 42
Erik Honn
fuente
1
¡Así que esto es hermoso! Pero (y casi siempre hay uno) no puedo obtener el $ scope. $ Apply () para propagar mis datos. Puedo verlo en un registro de la consola, pero mi vista no muestra los datos recién agregados (tengo una barra de pie de página donde muestro el $ scope) - ¿Alguna idea?
william e schroeder
Es difícil saberlo sin ver el código, pero mi primera suposición sería que el pie de página no está en el mismo ámbito. Si el pie de página está fuera del elemento con ng-controller = "YourController", entonces no tendrá acceso a sus variables.
Erik Honn
3
Gracias por la respuesta. Me gustaría señalar que la asignación también funciona con objetos complejos, no solo con tipos primitivos.
Patrick Salami
1
¿Por qué es necesario $ scope. $ Apply?
sinθ
En realidad, no lo recuerdo bien. Creo que fue porque model.assign no activa el enlace de datos, por lo que debe aplicar manualmente una vez que haya hecho todo lo que debe hacer. Pruébelo y vea si es posible eliminarlo o no (es posible que también haya cambiado en las versiones más recientes de Angular, creo que esta fue una o dos versiones atrás).
Erik Honn
20

Usando la respuesta de Erik, como punto de partida. Encontré una solución más simple que funcionó para mí.

En mi función ng-click tengo:

var the_string = 'lifeMeaning';
if ($scope[the_string] === undefined) {
   //Valid in my application for first usage
   $scope[the_string] = true;
} else {
   $scope[the_string] = !$scope[the_string];
}
//$scope.$apply

Lo he probado con y sin $ scope. $ Apply. ¡Funciona correctamente sin él!

Andrew Gray
fuente
13
Tenga en cuenta que esto es algo completamente diferente de lo que trata la pregunta. El objetivo de la pregunta es crear una variable de alcance dinámico con más de 1 profundidad basada en una cadena. Así que para crear "scope.life.meaning" y no "scope.lifeMeaning". Usar $ scope [the_string] no hará eso. Y para su caso específico, recuerde que! Undefined es realmente verdadero en javascrip, por lo que podría omitir toda la declaración if y simplemente hacer $ scope [the_string] =! $ Scope [the_string] ;. Sin embargo, podría confundir un poco el código, por lo que no estoy seguro de si realmente es algo que desearía.
Erik Honn
@ErikHonn, de hecho, ¡$ scope [significado de vida] = verdadero funcionó para mí!
Jesse P Francis
1
"Funciona", pero a menos que algo cambie en 1.6 no hace lo mismo que pide la pregunta. Si hace $ scope [life.meaning] = true, el nombre de la propiedad será "life.meaning", y eso no es lo que queremos. Queremos una propiedad llamada vida que tenga una propiedad infantil llamada significado.
Erik Honn
13

Cree variables angulares dinámicas a partir de los resultados

angular.forEach(results, function (value, key) {          
  if (key != null) {                       
    $parse(key).assign($scope, value);                                
  }          
});

PD. no olvide pasar el atributo $ parse a la función de su controlador

Demodave
fuente
5

Si está de acuerdo con el uso de Lodash, puede hacer lo que quería en una línea usando _.set ():

_.set (objeto, ruta, valor) Establece el valor de propiedad de la ruta en el objeto. Si una parte de la ruta no existe, se crea.

https://lodash.com/docs#set

Entonces tu ejemplo sería simplemente: _.set($scope, the_string, something);

Estrella del Norte
fuente
4

Solo para agregar a las respuestas ya dadas, lo siguiente funcionó para mí:

HTML:

<div id="div{{$index+1}}" data-ng-show="val{{$index}}">

¿Dónde $indexestá el índice de bucle?

Javascript (donde valueestá el parámetro pasado a la función y será el valor de $index, índice de ciclo actual):

var variable = "val"+value;
if ($scope[variable] === undefined)
{
    $scope[variable] = true;
}else {
    $scope[variable] = !$scope[variable];
}
Riaz
fuente
Esto no tiene nada que ver con la pregunta, manténgase en el tema.
Erik Honn
Lo siento, pensé que podría ayudar a crear variables dinámicas usando cadenas. Este hilo me ayudó a escribir el código anterior que funciona bien para mí.
Riaz
No se preocupe, querer dar respuestas es un buen comienzo, solo asegúrese de leer la pregunta primero :) En este caso, se trata de analizar una cadena desconocida "abc" para el objeto {a: {b: {c: ...} }}. Pero el manejo de elementos repetidos es un problema común, así que me alegro de que haya encontrado algo saludable en el hilo.
Erik Honn
Ah, y como señalé en otra respuesta, si se supone que los valores son verdaderos / falsos (o indefinidos), puede omitir toda la lógica y hacer $ scope [variable] ===! $ Scope [variable], ya que ! undefined es verdadero en javascript. Aunque si está usando mecanografiado, sospecho que se enojaría con usted.
Erik Honn
3

Tenga en cuenta: esto es solo una cosa de JavaScript y no tiene nada que ver con Angular JS. Así que no se confunda con el signo mágico '$';)

El principal problema es que se trata de una estructura jerárquica.

console.log($scope.life.meaning);  // <-- Nope! This is undefined.
=> a.b.c

Esto no está definido porque "$ scope.life" no existe pero el término anterior quiere resolver el "significado".

Una solución debería ser

var the_string = 'lifeMeaning';
$scope[the_string] = 42;
console.log($scope.lifeMeaning);
console.log($scope['lifeMeaning']);

o con un poco más de esfuerzo.

var the_string_level_one = 'life';
var the_string_level_two = the_string_level_one + '.meaning';
$scope[the_string_level_two ] = 42;
console.log($scope.life.meaning);
console.log($scope['the_string_level_two ']);

Dado que puede acceder a un objeto estructural con

var a = {};
a.b = "ab";
console.log(a.b === a['b']);

Hay varios buenos tutoriales sobre esto que lo guiarán bien a través de la diversión con JavaScript.

Hay algo en el

$scope.$apply();
do...somthing...bla...bla

Vaya y busque en la web 'angular $ apply' y encontrará información sobre la función $ apply. Y debe usarlo sabiamente de esta manera (si aún no está con una fase de $ aplicar).

$scope.$apply(function (){
    do...somthing...bla...bla
})
Raxa
fuente
3
Gracias por la respuesta, pero la pregunta no era por qué la "versión simple" no funcionaría, eso se sabía desde el principio. La pregunta era cuál era la alternativa. Me temo que ninguna de sus dos sugerencias funcionará, ambas requieren que conozca la estructura de la cadena de antemano, y el punto es que sea dinámica (y que pueda manejar cadenas arbitrarias). Vea la respuesta aceptada para una solución.
Erik Honn
1
Además, ninguna de las respuestas resuelve el problema. El primero no cumple con el prerrequisito básico, que deberíamos asignar a $ scope.abc y no a $ scope ['abc'], y el segundo también falla y da como resultado literalmente exactamente lo mismo que la "versión simple" del pregunta, solo con sintaxis innecesaria, pero tal vez sea un error tipográfico? :)
Erik Honn
0

Si está utilizando la biblioteca Lodash, a continuación se muestra la forma de establecer una variable dinámica en el alcance angular.

Para establecer el valor en el alcance angular.

_.set($scope, the_string, 'life.meaning')

Para obtener el valor del alcance angular.

_.get($scope, 'life.meaning')
Sagar Vaghela
fuente
-1

Si estaba tratando de hacer lo que imagino que estaba tratando de hacer, entonces solo tiene que tratar el alcance como un objeto JS normal.

Esto es lo que uso para una respuesta de éxito de la API para la matriz de datos JSON ...

function(data){

    $scope.subjects = [];

    $.each(data, function(i,subject){
        //Store array of data types
        $scope.subjects.push(subject.name);

        //Split data in to arrays
        $scope[subject.name] = subject.data;
    });
}

Ahora {{subject}} devolverá una matriz de nombres de sujetos de datos, y en mi ejemplo habría un atributo de alcance para {{trabajos}}, {{clientes}}, {{personal}}, etc. de $ alcance. trabajos, $ scope.customers, $ scope.staff

Kieran McKewen
fuente
4
Gracias por la respuesta pero eso no es lo mismo. Se trata de crear $ scope.subject.name a partir de una cadena sin la codificación $ scope.subject. Esto es relevante en algunas directivas en las que no debe asumir nada sobre el alcance si desea que la directiva sea reutilizable. Además, angular.forEach ()! No permita que jQuery se interponga en el uso de angular: P
Erik Honn