Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

This problem has eluded me for a while and I couldn't find an answer here on SO, so now that I solved it I thought I would put it here in case someone else has the same problem as me and can't find help.

I have a string I have gotten from a routeParam or a directive attribute or whatever, and I want to create a variable on the scope based on this. So:

$scope.<the_string> = "something".

However, if the string contains one or more dots I want to split it and actually "drill down" into the scope. So 'foo.bar' should become $scope.foo.bar. This means that the simple version won't work!

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

When reading a variable based on a string you can get this behavior by doing $scope.$eval(the_string), but how to do it when assigning a value?

share|improve this question

5 Answers 5

Using Erik's answer, as a starting point. I found a simpler solution that worked for me.

In my ng-click function I have:

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

I've tested it with and without $scope.$apply. Works correctly without it!

share|improve this answer
2  
Note that this is something completely different than what the question is about. The goal of the question is to create a dynamic scope variable with more than 1 depth based on a string. So to create "scope.life.meaning" and not "scope.lifeMeaning". Using $scope[the_string] will not do that. And for your specific case, remember that !undefined is actually true in javascrip, so you could actually skip the entire if statement and just do $scope[the_string] = !$scope[the_string];. Might muddle the code a bit though, so I am not sure if it is really something you would want. –  Erik Honn Feb 25 at 13:37

Create Dynamic angular variables from results

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

ps. don't forget to pass in the $parse attribute into your controller's function

share|improve this answer
up vote 54 down vote accepted

The solution I have found is to use $parse.

"Converts Angular expression into a function."

If anyone has a better one please add a new answer to the question!

Here is the example:

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();
console.log($scope.life.meaning);  // logs 42
share|improve this answer
    
Saved my life! Thank you! –  Fals Mar 6 '14 at 13:31
    
So this is beautiful! But (and there almost always is one) I cannot get the $scope.$apply() to propagate my data. I can see it in a console log, but my view does not show the newly added data (I have a footer bar where I show the $scope) - Any ideas? –  william e schroeder Sep 12 '14 at 20:07
    
Hard to know without seeing code, but my first guess would be that the footer is not in the same scope. If the footer is outside the element with ng-controller="YourController" then it won't have access to its variables. –  Erik Honn Sep 15 '14 at 11:26
1  
Thanks for the answer. I'd like to point out that assign also works with complex objects, not just primitive types. –  Patrick Salami Sep 19 '14 at 0:59
    
Thanks, very helpful! –  Mitch Glenn Oct 21 '14 at 16:16

Please keep in mind: this is just a JavaScript thing and has nothing to do with Angular JS. So don't be confused about the magical '$' sign ;)

The main problem is that this is an hierarchical structure.

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

This is undefined because "$scope.life" is not existing but the term above want to solve "meaning".

A solution should be

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

or with a little more efford.

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

Since you can access a structural objecte with

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

There are several good tutorials about this which guide you well through the fun with JavaScript.

There is something about the

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

Go and search the web for 'angular $apply' and you will find information about the $apply function. And you should use is wisely more this way (if you are not alreay with a $apply phase).

$scope.$apply(function (){
    do...somthing...bla...bla
})
share|improve this answer
1  
Thanks for the answer, but the question was not why the "simple version" would not work, that was known from the start. The question was what the alternative was. I am afraid neither of your two suggestions will work, both of them require you to know the structure of the string beforehand, and the point is for it to be dynamic (and to be able to handle arbitrary strings). See the accepted answer for a solution. –  Erik Honn Jun 12 '14 at 7:24
1  
Also, neither answer actually solves the issue. The first fails the basic prerequisite, that we should assign to $scope.a.b.c and not to $scope['a.b.c'], and the second one also fails this and result in literally the exact same thing as the "simple version" from the question, just with unneeded syntax, but perhaps that is a typo? :) –  Erik Honn Jun 12 '14 at 7:42

If you were trying to do what I imagine you were trying to do, then you only have to treat scope like a regular JS object.

This is what I use for an API success response for JSON data array...

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;
    });
}

Now {{subjects}} will return an array of data subject names, and in my example there would be a scope attribute for {{jobs}}, {{customers}}, {{staff}}, etc. from $scope.jobs, $scope.customers, $scope.staff

share|improve this answer
3  
Thanks for the answer but that is not the same thing. It is about creating $scope.subject.name from a string without hard-coding $scope.subject. This is relevant in some directives where you shouldn't assume anything about the scope if you want the directive to be reusable. Also, angular.forEach()! Don't let jQuery get in the way of using angular :P –  Erik Honn Dec 2 '13 at 8:16

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.