Photonic
Photonic

Reputation: 1431

Cannot read property '$apply' of undefined

I have research through the internet with this popular error and I have found no solution to my problem.

What I have is a jQuery iframe post message function that receive strings from a different domain. When it get the string it will need to store it to Angular and save to the database. What I am having the trouble is, trying to update angular so that recognizes the changes.

So here is my code:

.controller('jobOrderController', function(Jobs, socketio) {
    var vm = this;
    var myImage;
    $.receiveMessage(
            function(e) {
                myImage = e.data;
                vm.$apply(function() {
                    vm.orderData.guideImage = e.data
                });
            },
            'http://aaa.com'
        );

    vm.createOrders = function() {
        vm.message = '';
        Jobs.createOrders(vm.orderData)
            .success(function(data) {
                vm.orderData = '';
                vm.message = data.message;
            });
    };

}) 

$.receiveMessage will listen for incoming string data and then when it receive it should just save it to my "controller, vm". I know that my message is being received as I can alert them. I know that I am going it wrong but everything I read is using $scope.apply so I thought it would work the same way using "this". But it doesn't seem to be updated to angular.

Upvotes: 2

Views: 5551

Answers (4)

Shashank Agrawal
Shashank Agrawal

Reputation: 25807

You can just do this:

.controller('jobOrderController', function($scope, Jobs, socketio) {
   var vm = this;

   $.receiveMessage(
        function(e) {
            myImage = e.data;
            $scope.$apply(function() {
                vm.orderData.guideImage = e.data
            });
        },
        'http://aaa.com'
    );

});

The this works differently inside the Angular's controller. It's not always same as the $scope and $scope is the object which contains $apply method.

Upvotes: 0

Dan
Dan

Reputation: 10548

The controllerAs syntax is just (currently) sugar for $scope.foo (if your controllerAs is set to foo). As a result, this will actually point at $scope.foo in this instance and not $scope, which is why you will be unable to invoke any of $scope's actions through this. To use any of those, you will have to explicitly use $scope.

The above answers all answer with a solution on how to fix the short-term problem, that is, you using $scope.$apply in the controller.

Honestly, the issue here isn't that you can't use $scope.$apply - I'm not sure exactly what you are trying to do but one of the main rules of Angular is that you have to do everything through Angular; $.receiveMessage is decidedly not Angular. Wrap your $.receiveMessage into a service; that service should also handle the $scope.$apply. This will help reduce code duplication and make your controller agnostic to the implementation of the service.

There may already be a library that exists that does this that avoids using the heavy requirement of jQuery.

Upvotes: 0

Sarjan Desai
Sarjan Desai

Reputation: 3733

For $apply, you can not use this. $scope and this are different instance so this can not access $apply.

Check snippet:

angular.module('myApp', []).controller('MyCtrl', function($scope) {
  var vm = this;
  setTimeout(function() {
    $scope.$apply(function() {
      vm.text = 'Submit';
    });
  }, 0);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl as ctrl">
  <button>{{ctrl.text}}</button>
</div>

Upvotes: 0

Alexandre Nucera
Alexandre Nucera

Reputation: 2223

I see that you are using john papa's guideline to avoid the use of $scope. You just forgot to declare vm (standing for viewmodel) at the beginning of your controller:

var vm = this;

EDIT: the guideline also says :

"Consider using $scope in a controller only when needed. For example when publishing and subscribing events using $emit, $broadcast, or $on."

For $apply as well, you need to explicitly use $scope.$apply

Upvotes: 1

Related Questions