Cannot read property '$apply' of undefined - javascript

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.

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

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.

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>

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.

Related

Getting access to angular $scope from own event

$I have a custom javascript object, that can fire events.
I would like to access the angular $scope inside the event-handler, but I have read somewhere that using angular.element(...).scope() is not good, because it's only meant for testing.
My other idea was to register the handle on my object inside the controller, but this is not working (looks like $scope.somevalue gets set, but I don't think $scope is the same object).
I have found many answers here on Stack Overflow for similar questions, but they all seem to be using directives. All I want is to get a value from the object when it's updated, and display it.
Here are the two ways I have tried.
var myObj = GetMyObjInstance();
// Working, but apparently it's not good practise to call .scope() on an element.
myObj.onUpdated = function(){
console.log("myObj updated");
var v = myObj.getValue();
var controllerDiv = document.getElementById("controller");
var $scope = angular.element(controllerDiv).scope();
$scope.apply(function(){
$scope.someValue = v;
});
}
// Tried to do this, thinking i would get closure on the scope.
angular.module('myApp', []).controller('controller', function($scope){
myObj.onUpdated = function(){
console.log("myObj updated"); // Gets logged to console...
var v = myObj.getValue();
$scope.somevalue = v; // ... but somevalue does not get displayed.
$scope.apply(); // Error, says it's not a function, so maybe this is not the right object?.
}
});
Use AngularJS directives to handle events and update scope.
app.directive("xdEvent", function() {
return linkFn(scope, elem, attrs) {
elem.on("event", function(e) {
scope.$eval(attrs.xdEvent, {$event: e});
scope.$apply();
});
};
};
USAGE
<div xd-event="fn($event)"></div>
I think using a Service instead of a controller is a better practice. You can call a service from outside javascript with the injector like explained in this thread :
Call angularjs service from simple js code
If it is still important for you to access this variables from controller, you can use $watch to tell your controller to update itself when the service variables change.
Hope this help.
A+

Angularjs watch is not triggering

I have three elements to this program. First, in the service, I have:
$scope.loaded = MySvc.loaded;
$scope.loaded = false;
Then, in the controller which imports this service, I have a series of introductory things. I need to trigger events in the directives once the controller is done with its asycnhronous work, so in the controller, when that trigger is ready, I do
MySvc.loaded = true;
And then in the directive, which also imports the service, I have,
$scope.loaded = MySvc.loaded;
$scope.$watch('loaded', function (newValue, oldValue) {
The directive triggers when loaded is initialized to 'false', but when I change the value to 'true', nothing happens. The watch simply does not trigger. Why not? How do I fix this?
This is a fine way to do things, and I view it as orthogonal to promises (which are indeed extremely useful). Your problem comes from breaking the linkage you've created with the assignment. Instead try:
$scope.data = MySvc.data;
And append to that, e.g. MySvc.data.loaded = false. This way the data variable is never reassigned, so your linkage between controller and service stays intact.
Then you can either watch data.loaded or watch data as a collection by passing true as the 3rd option to $watch.
As I said in comment, you may have problems with yours scopes overridding the loaded property.
Try using the angular events to solve your problem.
angular.module('test', [])
.controller 'MyController', ($scope, $timeout) ->
$timeout ->
$scope.$broadcast('READY')
, 2000
.directive 'myDirective', ->
scope: {}
template: '<span>{{ value }}</span>'
link: ($scope) ->
$scope.value = "I'm waiting to be ready..."
$scope.$on 'READY', ->
$scope.value = "I'm ready!!"
See this in action (CoffeeScript as it's faster for prototyping but should be clear enough).
You're making this complicated. Use promises instead!!! They're sweet. Promises keep state of their resolution.
.factory('hello_world', function ($q, $timeout) {
var promise = $q.defer;
$timeout(function () {
promise.resolve('hello world');
}, 1000)
return promise.promise;
})
.controller('ctrl', function ($scope, hello_world) {
hello_world.then(function (response) {
console.log("I will only be ran when the async event resolves!");
console.log(response);
});
});
As you can see promises are much better, no watches. No weird gates. Simpler, and sexy. ;)

Sharing data between controllers in Angular JS?

Before this is marked as duplicate I've read quite of few similar questions, but all the answers I've found seem to use $scope, and after reading the documentation I'm not really sure I understand $scope, or why I'd use it in this situation.
I found this tutorial which describes how to do what I'm trying to do.
However, it's using an array of data. I just need one solid variable. In addition, I don't know why he's declaring an additional object to the factory service he creates; why not just use the factory as the object?
I was thinking I could do something like this, but I'm not sure if it will work or not.
Creating my factory/service:
var demoModule = angular.module("demoModule", []);
demoModule.factory("demoService", function() {
var demoSharedVariable = null;
return demoSharedVariable;
});
Accessing the shared variable in each controller:
var demoControllerOne = demoModule.controller("demoContollerOne", function(demoSharedVariable) {
this.oneFunction = function(oneInput){
demoSharedVariable = oneInput;
};
});
var demoControllerTwo = demoModule.controller("demoContollerTwo", function(demoSharedVariable) {
this.twoFunction = function(twoInput){
demoSharedVariable = twoInput;
};
});
Will this method produced the shared variable I'm after?
You need to inject the service in order to use it, then access the service variable.
demoModule.controller("demoContollerOne", function($scope, demoService) {
$scope.oneFunction = function(){
demoService.demoSharedVariable = $scope.oneInput;
};
});
demoModule.controller("demoContollerTwo", function($scope, demoService) {
$scope.twoFunction = function(){
demoService.demoSharedVariable = $scope.twoInput;
};
});
If you are using controllerAs, you rarely (or shouldn't) need to inject and use $scope. As controllerAs is a relatively newer feature, back then we have no choice but to use $scope, so it is not strange to find example with $scope.
Edit: If you are not using controllerAs (like in this example) you would need $scope to expose functions or variables to the view.
There are several place that are not correct I've found while fiddling with it, I'll edit the code. I don't know how to showcase the effect without using advanced concept like $watch, please provide your own fiddle if you don't understand.
Jsbin
One important thing is if you want to use angular, you have to understand the knowledge of scope.
Since neither you factory or controller is correct, i write a simple example for you to help you understand the service:
detail implementation in this plnkr:
service:
angular.module('myApp').service('MyService', [function() {
var yourSharedVariable; // Your shared variable
//Provide the setter and getter methods
this.setSharedVariable = function (newVal) {
yourSharedVariable = newVal;
};
this.getSharedVariable = function () {
return yourSharedVariable;
};
}
]);
controller:
myApp.controller('Ctrl2', ['$scope', 'MyService', '$window', function($scope, MyService, $window) {//inject MyService into the controller
$scope.setShared = function(val) {
MyService.setSharedVariable(val);
};
$scope.getShared = function() {
return MyService.getSharedVariable();
};
$scope.alertSharedVariable = function () {
$window.alert(MyService.getSharedVariable());
};
}]);

Integrating non-Angular code?

I'm developing a Cordova/PhoneGap app, and I'm using the $cordovaPush plugin (wrapped for PushPlugin) to handle push notifications.
The code looks something like this:
var androidConfig = {
"senderID" : "mysenderID",
"ecb" : "onNotification"
}
$cordovaPush.register(androidConfig).then(function(result) {
console.log('Cordova Push Reg Success');
console.log(result);
}, function(error) {
console.log('Cordova push reg error');
console.log(error);
});
The "ecb" function must be defined with window scope, ie:
window.onNotification = function onNotification(e)...
This function handles incoming events. I'd obviously like to handle incoming events in my angular code - how can I integrate the two so that my onNotification function can access my scope/rootScope variables?
Usually, you'll wrap your 3rd party library in a service or a factory, but in the spirit of answering your particular scenario...
Here's one possibility:
angular.module('myApp').
controller('myController', function($scope, $window) {
$window.onNotification = function() {
$scope.apply(function() {
$scope.myVar = ...updates...
});
};
});
A couple of things to notice:
Try to use $window, not window. It's a good habit to get into as it will help you with testability down the line. Because of the internals of Cordova, you might actually need to use window, but I doubt it.
The function that does all of the work is buried inside of $scope.apply. If you forget to do this, then any variables you update will not be reflected in the view until the digest cycle runs again (if ever).
Although I put my example in a controller, you might put yours inside of a handler. If its an angular handler (ng-click, for example), you might think that because the ng-click has an implicit $apply wrapping the callback, your onNotification function is not called at that time, so you still need to do the $apply, as above.
...seriously... don't forget the apply. :-) When I'm debugging people's code, it's the number one reason why external libraries are not working. We all get bit at least once by this.
Define a kind of a mail controller in body and inside that controller use the $window service.
HTML:
<body ng-controller="MainController">
<!-- other markup .-->
</body>
JS:
yourApp.controller("BaseController", ["$scope", "$window", function($scope, $window) {
$window.onNotification = function(e) {
// Use $scope or any Angular stuff
}
}]);

AngularJS Scope Apply does not working on click

I'm trying to se scope variable value after on click event using the following code:
$('#teamDetailTabs a').click(function(data) {
$scope.$apply(function(){
console.log($(this).attr('data-target'));
$scope.activeTab = $(this).attr('data-target');
console.log($scope.activeTab);
});
});
Problem is that value $scope.activeTab is undefined even if I used $scope.apply.
How can I solve it please?
Thanks for any advice.
The context (this) inside apply call is not what you expect, it's no longer a DOMElement. So you can fix it like this:
var self = this;
$scope.$apply(function() {
console.log($(self).attr('data-target'));
$scope.activeTab = $(self).attr('data-target');
console.log($scope.activeTab);
});
However, I strongly encourage you to go with ngClick and never use jQuery style approach in Angular app. Take a look at this very detailed thread.

Categories

Resources