Définition de variables de portée dynamique dans AngularJs - scope. <some_string>


97

J'ai une chaîne que j'ai obtenue à partir d'un routeParamattribut de directive ou d'un autre, et je veux créer une variable sur la portée en fonction de cela. Alors:

$scope.<the_string> = "something".

Cependant, si la chaîne contient un ou plusieurs points, je veux la diviser et en fait "explorer" la portée. Alors 'foo.bar'devrait devenir $scope.foo.bar. Cela signifie que la version simple ne fonctionnera pas!

// 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!

Lors de la lecture d'une variable basée sur une chaîne, vous pouvez obtenir ce comportement en faisant $scope.$eval(the_string), mais comment le faire lors de l'attribution d'une valeur?

Réponses:


174

La solution que j'ai trouvée est d'utiliser $ parse .

"Convertit l'expression angulaire en fonction."

Si quelqu'un en a une meilleure, veuillez ajouter une nouvelle réponse à la question!

Voici l'exemple:

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

1
Alors c'est beau! Mais (et il y en a presque toujours un) je ne peux pas obtenir le $ scope. $ Apply () pour propager mes données. Je peux le voir dans un journal de console, mais ma vue n'affiche pas les données nouvellement ajoutées (j'ai une barre de pied de page où je montre le $ scope) - Des idées?
william e schroeder

Difficile à savoir sans voir le code, mais ma première hypothèse serait que le pied de page n'est pas dans la même portée. Si le pied de page est en dehors de l'élément avec ng-controller = "YourController" alors il n'aura pas accès à ses variables.
Erik Honn

3
Merci d'avoir répondu. Je tiens à souligner que l'assignation fonctionne également avec des objets complexes, pas seulement des types primitifs.
Patrick Salami

1
Pourquoi $ scope. $ Apply est-il nécessaire?
sinθ

Je ne m'en souviens pas vraiment. Je pense que c'est parce que model.assign ne déclenche pas la liaison de données, vous devez donc appliquer manuellement une fois que vous avez fait tout ce que vous devez faire. Essayez-le et voyez s'il est possible de le supprimer ou non (peut-être également changé dans les versions plus récentes d'Angular, je pense que c'était une ou deux versions).
Erik Honn

20

En utilisant la réponse d'Erik, comme point de départ. J'ai trouvé une solution plus simple qui a fonctionné pour moi.

Dans ma fonction ng-click, j'ai:

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

Je l'ai testé avec et sans $ scope. $ Apply. Fonctionne correctement sans cela!


13
Notez que c'est quelque chose de complètement différent de ce sur quoi porte la question. Le but de la question est de créer une variable d'étendue dynamique avec plus d'une profondeur basée sur une chaîne. Donc, pour créer «scope.life.meaning» et non «scope.lifeMeaning». L'utilisation de $ scope [the_string] ne fera pas cela. Et pour votre cas spécifique, rappelez-vous que! Undefined est en fait vrai dans javascrip, vous pouvez donc ignorer toute l'instruction if et simplement faire $ scope [the_string] =! $ Scope [the_string] ;. Cela pourrait cependant brouiller un peu le code, donc je ne suis pas sûr que ce soit vraiment quelque chose que vous voudriez.
Erik Honn

@ErikHonn, en fait, $ scope [life.meaning] = true a fonctionné pour moi!
Jesse P Francis

1
Cela "fonctionne", mais à moins que quelque chose ne change en 1.6, cela ne fait pas la même chose que la question demande. Si vous faites $ scope [life.meaning] = true, le nom de la propriété sera "life.meaning", et ce n'est pas ce que nous voulons. Nous voulons une propriété appelée vie qui a une propriété enfant appelée signification.
Erik Honn le

13

Créer des variables angulaires dynamiques à partir des résultats

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

ps. n'oubliez pas de passer l'attribut $ parse dans la fonction de votre contrôleur


5

Si vous êtes d'accord avec Lodash, vous pouvez faire ce que vous vouliez en une seule ligne en utilisant _.set ():

_.set (objet, chemin, valeur) Définit la valeur de propriété du chemin sur l'objet. Si une partie du chemin n'existe pas, elle est créée.

https://lodash.com/docs#set

Donc, votre exemple serait simplement: _.set($scope, the_string, something);


4

Juste pour ajouter aux réponses déjà données, ce qui suit a fonctionné pour moi:

HTML:

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

$index est l'index de la boucle.

Javascript (où valueest le paramètre passé à la fonction et ce sera la valeur de $index, index de la boucle actuelle):

var variable = "val"+value;
if ($scope[variable] === undefined)
{
    $scope[variable] = true;
}else {
    $scope[variable] = !$scope[variable];
}

Cela n'a rien à voir avec la question, veuillez rester sur le sujet.
Erik Honn

Désolé, j'ai pensé que cela pourrait aider à créer des variables dynamiques à l'aide de chaînes. Ce fil de discussion m'a aidé à écrire le code ci-dessus qui fonctionne très bien pour moi.
Riaz

Pas de soucis, vouloir fournir des réponses est un bon début, assurez-vous simplement de lire la question en premier :) Dans ce cas, il s'agit d'analyser une chaîne inconnue "abc" à l'objet {a: {b: {c: ...} }}. Mais la gestion des éléments répétitifs est un problème courant, donc je suis heureux que vous ayez trouvé quelque chose de sain dans le fil.
Erik Honn du

Oh, et comme je l'ai noté dans une autre réponse, si les valeurs sont simplement supposées être true / false (ou non définies), vous pouvez simplement ignorer toute la logique et faire $ scope [variable] ===! $ Scope [variable], puisque ! undefined est vrai en javascript. Bien que si vous utilisez dactylographié, je soupçonne que cela vous fâcherait!
Erik Honn

3

Veuillez garder à l'esprit: il ne s'agit que de JavaScript et n'a rien à voir avec Angular JS. Alors ne soyez pas confus avec le signe magique '$';)

Le problème principal est qu'il s'agit d'une structure hiérarchique.

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

Ceci n'est pas défini car "$ scope.life" n'existe pas mais le terme ci-dessus veut résoudre le "sens".

Une solution devrait être

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

ou avec un peu plus d'effort.

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 ']);

Puisque vous pouvez accéder à un objet structurel avec

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

Il existe plusieurs bons tutoriels à ce sujet qui vous guident bien à travers le plaisir avec JavaScript.

Il y a quelque chose dans le

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

Allez chercher sur le Web «angular $ apply» et vous trouverez des informations sur la fonction $ apply. Et vous devriez l'utiliser plus judicieusement de cette façon (si vous n'êtes pas déjà d'accord avec une phase $ apply).

$scope.$apply(function (){
    do...somthing...bla...bla
})

3
Merci pour la réponse, mais la question n'était pas de savoir pourquoi la "version simple" ne fonctionnerait pas, cela était connu dès le départ. La question était de savoir quelle était l'alternative. Je crains qu'aucune de vos deux suggestions ne fonctionne, les deux nécessitent que vous connaissiez au préalable la structure de la chaîne, et le but est qu'elle soit dynamique (et qu'elle soit capable de gérer des chaînes arbitraires). Voir la réponse acceptée pour une solution.
Erik Honn

1
De plus, aucune des deux réponses ne résout le problème. Le premier échoue au prérequis de base, que nous devrions attribuer à $ scope.abc et non à $ scope ['abc'], et le second échoue également et aboutit littéralement à la même chose que la "version simple" du question, juste avec une syntaxe inutile, mais c'est peut-être une faute de frappe? :)
Erik Honn

0

Si vous utilisez la bibliothèque Lodash, voici le moyen de définir une variable dynamique dans la portée angulaire.

Pour définir la valeur dans la portée angulaire.

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

Pour obtenir la valeur de la portée angulaire.

_.get($scope, 'life.meaning')

-1

Si vous essayiez de faire ce que j'imagine que vous essayiez de faire, alors vous n'avez qu'à traiter scope comme un objet JS normal.

C'est ce que j'utilise pour une réponse de réussite d'API pour un tableau de données 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;
    });
}

Désormais, {{sujets}} renverra un tableau de noms de sujets de données, et dans mon exemple, il y aurait un attribut scope pour {{jobs}}, {{customers}}, {{staff}}, etc. de $ scope. emplois, $ scope.customers, $ scope.staff


4
Merci pour la réponse mais ce n'est pas la même chose. Il s'agit de créer $ scope.subject.name à partir d'une chaîne sans coder en dur $ scope.subject. Ceci est pertinent dans certaines directives où vous ne devriez rien supposer sur la portée si vous voulez que la directive soit réutilisable. Aussi, angular.forEach ()! Ne laissez pas jQuery vous empêcher d'utiliser angular: P
Erik Honn
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.