Ustawianie zmiennych zakresu dynamicznego w AngularJs - scope. <some_string>

97

Mam ciąg, który otrzymałem z routeParamatrybutu lub dyrektywy lub czegokolwiek, i chcę na tej podstawie utworzyć zmienną o zakresie. Więc:

$scope.<the_string> = "something".

Jeśli jednak ciąg zawiera jedną lub więcej kropek, chcę go podzielić i faktycznie „drążyć” w zakresie. Tak 'foo.bar'powinno się stać $scope.foo.bar. Oznacza to, że prosta wersja nie będzie działać!

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

Czytając zmienną opartą na łańcuchu, możesz uzyskać to zachowanie, robiąc $scope.$eval(the_string), ale jak to zrobić, przypisując wartość?

Erik Honn
źródło

Odpowiedzi:

174

Rozwiązaniem, które znalazłem, jest użycie $ parse .

„Konwertuje wyrażenie kątowe na funkcję”.

Jeśli ktoś ma lepszy, dodaj nową odpowiedź na pytanie!

Oto przykład:

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
źródło
1
Więc to jest piękne! Ale (i prawie zawsze jest jeden) nie mogę uzyskać $ scope. $ Apply () do propagacji moich danych. Widzę to w dzienniku konsoli, ale mój widok nie pokazuje nowo dodanych danych (mam pasek stopki, na którym pokazuję $ scope) - Jakieś pomysły?
william e schroeder,
Trudno to wiedzieć bez zobaczenia kodu, ale moje pierwsze przypuszczenie byłoby takie, że stopka nie jest w tym samym zakresie. Jeśli stopka znajduje się poza elementem z ng-controller = "YourController", to nie będzie miała dostępu do swoich zmiennych.
Erik Honn
3
Dziękuję za odpowiedź. Chciałbym zaznaczyć, że przypisywanie działa również z obiektami złożonymi, a nie tylko z typami prymitywnymi.
Patrick Salami,
1
Dlaczego element $ scope. $ Apply jest konieczny?
sinθ
Właściwie to nie całkiem pamiętam. Myślę, że było tak, ponieważ model. assign nie wyzwala powiązania danych, więc po wykonaniu wszystkich czynności należy zastosować ręcznie. Wypróbuj go i zobacz, czy można go usunąć, czy nie (mogło się to zmienić również w nowszych wersjach Angulara, myślę, że była to jedna lub dwie wersje wstecz).
Erik Honn
20

Używając odpowiedzi Erika jako punktu wyjścia. Znalazłem prostsze rozwiązanie, które działało dla mnie.

W mojej funkcji ng-click mam:

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

Przetestowałem to z $ scope i bez niego. $ Apply. Działa poprawnie bez tego!

Andrew Gray
źródło
13
Zauważ, że jest to coś zupełnie innego niż to, o co chodzi w pytaniu. Celem pytania jest utworzenie zmiennej zakresu dynamicznego o większej niż 1 głębokości na podstawie ciągu. Aby utworzyć „scope.life.meaning”, a nie „scope.lifeMeaning”. Użycie $ scope [the_string] tego nie zrobi. A w twoim konkretnym przypadku pamiętaj, że! Undefined jest faktycznie prawdą w javascripcie, więc możesz pominąć całą instrukcję if i po prostu zrobić $ scope [the_string] =! $ Scope [the_string] ;. Może to jednak trochę pomieszać kod, więc nie jestem pewien, czy naprawdę byś chciał.
Erik Honn
@ErikHonn, właściwie, $ scope [life.meaning] = true zadziałało dla mnie!
Jesse P Francis
1
To "działa", ale jeśli coś nie zmieniło się w 1.6, nie robi tego samego, o co pyta pytanie. Jeśli zrobisz $ scope [life.meaning] = true, nazwa właściwości będzie brzmiała „life.meaning”, a nie tego chcemy. Chcemy własności zwanej życiem, która ma własność potomną zwaną znaczeniem.
Erik Honn
13

Utwórz dynamiczne zmienne kątowe na podstawie wyników

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

ps. nie zapomnij przekazać atrybutu $ parse do funkcji twojego kontrolera

Demodave
źródło
5

Jeśli nie przeszkadza ci używanie Lodash, możesz zrobić to, co chciałeś w jednej linii za pomocą _.set ():

_.set (obiekt, ścieżka, wartość) Ustawia wartość właściwości ścieżki na obiekcie. Jeśli część ścieżki nie istnieje, zostaje utworzona.

https://lodash.com/docs#set

Więc twój przykład byłby po prostu: _.set($scope, the_string, something);

Gwiazda Polarna
źródło
4

Aby dodać do już podanych odpowiedzi, zadziałały dla mnie następujące:

HTML:

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

Gdzie $indexjest indeks pętli.

Javascript (gdzie valuejest przekazanym parametrem do funkcji i będzie to wartość $index, indeks pętli prądowej):

var variable = "val"+value;
if ($scope[variable] === undefined)
{
    $scope[variable] = true;
}else {
    $scope[variable] = !$scope[variable];
}
Riaz
źródło
To nie ma nic wspólnego z pytaniem, proszę pozostań na temat.
Erik Honn,
Przepraszamy, pomyślałem, że może to pomóc w tworzeniu zmiennych dynamicznych za pomocą ciągów. Ten wątek pomógł mi napisać powyższy kod, który dla mnie działa dobrze.
Riaz
Bez obaw, chęć udzielenia odpowiedzi to dobry początek, po prostu upewnij się, że najpierw przeczytałeś pytanie :) W tym przypadku chodzi o przeanalizowanie nieznanego ciągu „abc” do obiektu {a: {b: {c: ...} }}. Jednak obsługa powtarzających się elementów jest częstym problemem, więc cieszę się, że w wątku znalazłeś coś dobrego.
Erik Honn,
Aha, i jak zauważyłem w innej odpowiedzi, jeśli wartości mają być po prostu prawdziwe / fałszywe (lub nieokreślone), możesz po prostu pominąć całą logikę i zrobić $ scope [zmienna] ===! $ Scope [zmienna], ponieważ ! undefined jest prawdziwe w javascript. Chociaż podejrzewam, że gdybyś używał maszynopisu, byłby na ciebie zły!
Erik Honn,
3

Pamiętaj: to tylko rzecz JavaScript i nie ma nic wspólnego z Angular JS. Więc nie daj się zmylić magicznym znakiem '$';)

Główny problem polega na tym, że jest to struktura hierarchiczna.

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

Nie jest to zdefiniowane, ponieważ „$ scope.life” nie istnieje, ale powyższy termin chce rozwiązać „znaczenie”.

Powinno być rozwiązaniem

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

lub z nieco większym wysiłkiem.

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

Ponieważ możesz uzyskać dostęp do obiektu strukturalnego za pomocą

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

Istnieje kilka dobrych tutoriali na ten temat, które dobrze poprowadzą Cię przez zabawę z JavaScriptem.

Jest coś w tym

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

Idź i wyszukaj w Internecie „angular $ apply”, a znajdziesz informacje o funkcji $ apply. I powinieneś używać mądrzejszego sposobu w ten sposób (jeśli nie jesteś już z fazą stosowania).

$scope.$apply(function (){
    do...somthing...bla...bla
})
Raxa
źródło
3
Dziękuję za odpowiedź, ale pytanie nie brzmiało, dlaczego „prosta wersja” nie zadziała, o której wiedzieliśmy od początku. Pytanie brzmiało, jaka była alternatywa. Obawiam się, że żadna z twoich dwóch sugestii nie zadziała, obie wymagają wcześniejszej znajomości struktury struny, a chodzi o to, aby była dynamiczna (i była w stanie obsłużyć dowolne struny). Zobacz zaakceptowaną odpowiedź, aby znaleźć rozwiązanie.
Erik Honn
1
Żadna z odpowiedzi nie rozwiązuje również problemu. Pierwsza nie spełnia podstawowego warunku wstępnego, który powinniśmy przypisać do $ scope.abc, a nie do $ scope ['abc'], a druga również zawodzi i powoduje dosłownie to samo, co „prosta wersja” z pytanie, tylko z niepotrzebną składnią, ale może to jest literówka? :)
Erik Honn
0

Jeśli korzystasz z biblioteki Lodash, poniżej jest sposób na ustawienie zmiennej dynamicznej w zakresie kątowym.

Aby ustawić wartość w zakresie kątowym.

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

Aby uzyskać wartość z zakresu kątowego.

_.get($scope, 'life.meaning')
Sagar Vaghela
źródło
-1

Jeśli próbujesz zrobić to, co sobie wyobrażam, próbujesz zrobić, wtedy musisz traktować zasięg jak zwykły obiekt JS.

To jest to, czego używam do odpowiedzi API sukcesu dla tablicy danych 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;
    });
}

Teraz {{subject}} zwróci tablicę nazwisk osób, których dane dotyczą, aw moim przykładzie będzie atrybut zakresu dla {{jobs}}, {{customers}}, {{staff}} itd. Z $ scope. Jobs, $ scope.customers, $ scope.staff

Kieran McKewen
źródło
4
Dzięki za odpowiedź, ale to nie to samo. Chodzi o utworzenie $ scope.subject.name z łańcucha bez kodowania $ scope.subject na stałe. Jest to istotne w przypadku niektórych dyrektyw, w których nie należy zakładać nic na temat zakresu, jeśli chcesz, aby dyrektywa była wielokrotnego użytku. Ponadto angular.forEach ()! Nie pozwól, aby jQuery przeszkadzało w używaniu
angular