0

Weird behavior for this piece of code:

I have some Angular controller:

.controller('View1Ctrl', ['$scope', 'CrawlServerExecutor', function ($scope, CrawlServerExecutor) {

    $scope.results = [];

    var showResults = function (results) {
        results.forEach(function (result) {
            $scope.results.push(result);
        });
    };

When writing it like this:

    var showResults = function (results) {
        results.forEach($scope.results.push);
    };

I get this error: Error: undefined is not an object for trying to access the push function, I looked $scope.results inside the debugger and the object was recognized as an array, but for some reason in this syntax it does not refer to it's push function. and I like this code better because it's more elegant.

Anyone has any idea why this happens?

Thanks

10
  • Have you tried binding the array to the function (so when it's called, this will be correctly set) as in results.forEach($scope.results.push.bind($scope.results)); Commented Jan 7, 2016 at 22:46
  • @AlexCorreiaSantos - you're correct of course. Why not post an answer? Commented Jan 7, 2016 at 22:51
  • I didn't have a chance to test and just trying to figure out from the top of my head, so I didn't want to give a false answer. Glad it worked (I've added it as an answer) Commented Jan 7, 2016 at 22:54
  • Does showResults gets called repeatedly? Are the results aggregated, or replaced? And is the parameter (results) an array of array-like object? Commented Jan 7, 2016 at 22:54
  • 1
    Then why not simply assign the value to the property, like: $scope.results = results;? Commented Jan 7, 2016 at 23:34

3 Answers 3

5

You need to bind the array to the function, so it can be called in the right context (this set properly).

results.forEach($scope.results.push.bind($scope.results));
Sign up to request clarification or add additional context in comments.

3 Comments

This results in this error: Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys.
If you call the function multiple times with the same values, then you can clear the list before inserting as a quick fix $scope.results = [], then proceed to do the forEach
@johni: It is another story, the answer is correct, you can accept the answer and ask new question in SO. By the way, I suggest to downvoter to comment its downvote.
0

In the latter notation you pass the reference of the push function. Therefore inside of the push function itself (which is some defined javascript-code) the "this"-reference will be "window". This is what happens if you call a function without any bound context. In the first case you call $scope.results.push(result), hence the context (and therefore the "this"-reference inside the push function) will be the $scope.results array. In javaScript "this" can change depending on the context.

The problem now is that the push function of the Array-Prototype assumes that the context will be the array itself, which isn't the case here.

So what you can do is "bind" a specific context to a naked function with the "bind" method.

So you can try

results.forEach ($scope.results.push.bind($scope.results));

now the context (this variable) in the push function will be the array $scope.results again and the function will work properly

Comments

0

You get an exception because when push (which is the function Array.prototype.push) is called, it's being called without context, without any this value to operate on. Doing so is like calling it on an undefined value which is what the error message is telling you...

What your basically doing is concatenating 2 arrays and keeping the end result.

You don't need to iterate the array yourself to do that.

You can simplify your code with:

$scope.results = $scope.results.contact(results);

Or, if you prefer an "in place mutation" of $scope.results, try:

[].push.apply($scope.results, results);

Or, if you really want to iterate yourself and use forEach, use the last parameter to pass the this context:

results.forEach($scope.results.push, $scope.results);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.