Definindo variáveis ​​de escopo dinâmicas em AngularJs - escopo. <some_string>

97

Eu tenho uma string que obtive de um routeParamou de um atributo de diretiva ou de qualquer outra coisa, e quero criar uma variável no escopo com base nisso. Assim:

$scope.<the_string> = "something".

No entanto, se a string contém um ou mais pontos, quero dividi-la e realmente "detalhar" o escopo. Então 'foo.bar'deve se tornar $scope.foo.bar. Isso significa que a versão simples não 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!

Ao ler uma variável baseada em uma string, você pode obter esse comportamento fazendo $scope.$eval(the_string), mas como fazê-lo ao atribuir um valor?

Erik Honn
fonte

Respostas:

174

A solução que encontrei é usar $ parse .

"Converte a expressão angular em uma função."

Se alguém tiver uma melhor, adicione uma nova resposta à pergunta!

Aqui está o exemplo:

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
fonte
1
Então isso é lindo! Mas (e quase sempre há um) não consigo fazer o $ scope. $ Apply () para propagar meus dados. Eu posso ver isso em um log do console, mas minha visão não mostra os dados recém-adicionados (eu tenho uma barra de rodapé onde mostro o $ scope) - Alguma ideia?
william e schroeder
É difícil saber sem ver o código, mas meu primeiro palpite seria que o rodapé não está no mesmo escopo. Se o rodapé estiver fora do elemento com ng-controller = "YourController", ele não terá acesso às suas variáveis.
Erik Honn,
3
Obrigado pela resposta. Gostaria de salientar que assign também funciona com objetos complexos, não apenas com tipos primitivos.
Patrick Salami
1
Por que $ scope. $ Apply é necessário?
sinθ
Não me lembro bem, na verdade. Acho que foi porque model.assign não aciona vinculação de dados, então você precisa aplicar manualmente depois de ter feito tudo o que precisa fazer. Experimente e veja se é possível removê-lo ou não (pode ter mudado nas versões mais recentes do Angular também, acho que foi uma ou duas versões anteriores).
Erik Honn
20

Usando a resposta de Erik, como ponto de partida. Encontrei uma solução mais simples que funcionou para mim.

Na minha função ng-click, tenho:

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

Eu testei com e sem $ scope. $ Apply. Funciona corretamente sem ele!

Andrew Gray
fonte
13
Observe que isso é algo completamente diferente do que é a pergunta. O objetivo da questão é criar uma variável de escopo dinâmica com mais de 1 profundidade com base em uma string. Portanto, para criar "scope.life.meaning" e não "scope.lifeMeaning". Usar $ scope [the_string] não fará isso. E para o seu caso específico, lembre-se de que! Undefined é realmente verdadeiro em javascrip, então você poderia pular toda a instrução if e apenas fazer $ scope [the_string] =! $ Scope [the_string] ;. Pode confundir um pouco o código, então não tenho certeza se é realmente algo que você gostaria.
Erik Honn
@ErikHonn, na verdade, $ scope [life.meaning] = true funcionou para mim!
Jesse P Francis
1
Ele "funciona", mas a menos que algo seja alterado no 1.6, ele não faz a mesma coisa que a pergunta pede. Se você fizer $ scope [life.meaning] = true, o nome da propriedade será "life.meaning", e não é isso que queremos. Queremos uma propriedade chamada vida, que tem uma propriedade filha chamada significado.
Erik Honn
13

Crie variáveis ​​angulares dinâmicas a partir dos resultados

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

ps. não se esqueça de passar o atributo $ parse para a função de seu controlador

Demodave
fonte
5

Se estiver tudo bem com o Lodash, você pode fazer o que deseja em uma linha usando _.set ():

_.set (objeto, caminho, valor) Define o valor da propriedade do caminho no objeto. Se uma parte do caminho não existir, ela será criada.

https://lodash.com/docs#set

Portanto, seu exemplo seria simplesmente: _.set($scope, the_string, something);

Estrela do norte
fonte
4

Apenas para adicionar às respostas já dadas, o seguinte funcionou para mim:

HTML:

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

Onde $indexestá o índice do loop.

Javascript (onde valueé o parâmetro passado para a função e será o valor de $index, índice de loop atual):

var variable = "val"+value;
if ($scope[variable] === undefined)
{
    $scope[variable] = true;
}else {
    $scope[variable] = !$scope[variable];
}
Riaz
fonte
Isso não tem nada a ver com a pergunta, por favor, fique no tópico.
Erik Honn
Desculpe, pensei que poderia ajudar na criação de variáveis ​​dinâmicas usando strings. Este tópico me ajudou a escrever o código acima, que está funcionando bem para mim.
Riaz
Não se preocupe, querer fornecer respostas é um bom começo, apenas certifique-se de ler a pergunta primeiro :) Neste caso, trata-se de analisar uma string desconhecida "abc" para o objeto {a: {b: {c: ...} }} Mas lidar com itens repetidos é um problema comum, então fico feliz que você tenha encontrado algo saudável no tópico.
Erik Honn
Ah, e como observei em outra resposta, se os valores forem apenas verdadeiros / falsos (ou indefinidos), você pode simplesmente ignorar a lógica inteira e fazer $ escopo [variável] ===! $ Escopo [variável], uma vez que ! undefined é verdadeiro em javascript. Embora se você estiver usando texto datilografado, eu suspeito que ficaria bravo com você!
Erik Honn
3

Lembre-se: isso é apenas uma coisa do JavaScript e não tem nada a ver com Angular JS. Portanto, não se confunda sobre o sinal mágico '$';)

O principal problema é que esta é uma estrutura hierárquica.

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

Isso é indefinido porque "$ scope.life" não existe, mas o termo acima deseja resolver o "significado".

Uma solução deve ser

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

ou com um pouco mais de esforço.

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

Uma vez que você pode acessar um objeto estrutural com

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

Existem vários bons tutoriais sobre isso que o orientam bem na diversão com JavaScript.

Há algo sobre o

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

Vá e pesquise na web por 'angular $ apply' e você encontrará informações sobre a função $ apply. E você deve usar é mais sabiamente desta forma (se você ainda não estiver com uma fase $ apply).

$scope.$apply(function (){
    do...somthing...bla...bla
})
Raxa
fonte
3
Obrigado pela resposta, mas a pergunta não era por que a "versão simples" não funcionaria, isso era conhecido desde o início. A questão era qual era a alternativa. Receio que nenhuma das duas sugestões funcionará, ambas exigem que você conheça a estrutura da string de antemão, e o objetivo é que ela seja dinâmica (e seja capaz de lidar com strings arbitrárias). Veja a resposta aceita para uma solução.
Erik Honn
1
Além disso, nenhuma das respostas resolve realmente o problema. O primeiro falha no pré-requisito básico, que devemos atribuir a $ scope.abc e não a $ scope ['abc'], e o segundo também falha nisso e resulta literalmente exatamente na mesma coisa que a "versão simples" do pergunta, apenas com sintaxe desnecessária, mas talvez isso seja um erro de digitação? :)
Erik Honn
0

Se você estiver usando a biblioteca Lodash, abaixo é a maneira de definir uma variável dinâmica no escopo angular.

Para definir o valor no escopo angular.

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

Para obter o valor do escopo angular.

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

Se você estava tentando fazer o que eu imagino que estava tentando fazer, então você só precisa tratar o escopo como um objeto JS regular.

Isso é o que eu uso para uma resposta de sucesso da API para a matriz de dados 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;
    });
}

Agora {{sujeitos}} retornará uma matriz de nomes de assunto de dados, e em meu exemplo haveria um atributo de escopo para {{empregos}}, {{clientes}}, {{equipe}}, etc. de $ escopo. empregos, $ scope.customers, $ scope.staff

Kieran McKewen
fonte
4
Obrigado pela resposta, mas não é a mesma coisa. Trata-se de criar $ scope.subject.name a partir de uma string sem codificar $ scope.subject. Isso é relevante em algumas diretivas onde você não deve assumir nada sobre o escopo se quiser que a diretiva seja reutilizável. Além disso, angular.forEach ()! Não deixe o jQuery atrapalhar o uso do angular: P
Erik Honn