$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+
Related
I have a function that is called in a directive html. I'd like to pass that object to the controller. How do I do that? Below is a copy of what I have so far
wpGroup.html
<button ng-click="hideGroup(item.id)">-</button>
wp-group.js
scope:{hideGroup: &} //nothing else related to hideGroup or item.id
wp-view.html
<data-wp-group data-item="childItem" data-hide-group="vm.hideGroup(vm.id)"/>
I'm answering this question hoping that data-wp-group is your directive.
These should be modified,
wp-group.js
scope:{dataHideGroup: &} //nothing else related to hideGroup or item.id
inside up-group.js,
scope.hideGroup = function(id){
scope.dataHideGroup({"id":id});
}
wp-view.html
<data-wp-group data-item="childItem" data-hide-group="vm.hideGroup(id)"/>
Hope you understand what I said
functions in javascript are first class, they can be pass as arguments wherever you want.
if i understand correctly you want to pass that hideGroup function to a different controller and invoke it.
You can share the reference between the two , with some events handling (or better) using custom service.
.service('ShareDataService', function(){
var data;
this.setFn = function(fn){
data = fn;
}
this.getFn = function(){
return data;
}
})
then in your directive inject the service and set the data.
ShareDataService.setFn(scope.hideGroup);
and get the reference in your controller;
var hideGroupFn = ShareDataService.getFn();
I have the following piece of code in angular
$scope.prepare = function(){
$scope.elems = [1,2,3];
};
$scope.action = function(){
var elem = $scope.elems[0]; //undefined
}
then, in my view, I use the directive ng-init="prepare()" and attach to a button the action function on click event
<button ng-click="action()">action</button>
Inthe action function the scope hasn't the elems array defined?
Can anybody tell me why this happen?
Thanks!
Since you are not showing the controller or the scope of the HTML where you are calling init() and action(), I can't even guess why you are having problems since the code you have posted works. This is a pluker proving that much: http://plnkr.co/edit/qMzPtJtp9t9CoNKkmWIc?p=preview
<div ng-init="prepare()"></div>
<input type="button" value="Call function" data-ng-click="action()" />
<p>Init Defined: {{elems}}</p>
<p>Function call: {{redefined}}</p>
$scope.prepare = function(){
$scope.elems = [1,2,3];
};
$scope.action = function(){
$scope.redefined = $scope.elems[0]; //undefined
}
With that said, you are not using ng-init() correctly. From the angluar documentation:
"This directive can be abused to add unnecessary amounts of logic into your templates. There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat ... and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope."
Link to ng-init documentation: https://docs.angularjs.org/api/ng/directive/ngInit
You will be much better off initializing your array in the controller.
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.
I tried this with no luck:
app.controller('PageLayoutController', function ($scope)
{
// Scope properties
$scope.PageMap = {};
// Constructor
function PageLayoutController() {
alert( "contructing" );
}
return PageLayoutController;
});
I'm looking for a default or popular way of defining the construct of these controllers in angular.
I'm aware i can just create a function called Construct and then call it first, but i wondered if there was an official way of doing it?
I assume you may want to invoke the defined function during Controller getting instantiated. If that's the requirement, you can follow the following syntax.
angular.module('myApp', [])
.controller('PageLayoutController', '$scope', function($scope) {
// Scope properties
$scope.PageMap = {};
$scope.PageLayoutController = function() {
// Do stuffs here
};
// Call the function when the Controller get first invoked
$scope.PageLayoutController();
});
Also you can listen into the $routeChangeStart event and call the function as well.
$scope.$on('$routeChangeStart', function(next, current) {
... you could trigger something here ...
});
Also you can use any of the following events as well.
$routeChangeSuccess
$routeChangeError
The controller function is already a constructor so anything you write in the its body will be executed on construction.
Not quite sure what you are exactly trying to do. But being specific, angular instantiates the controller constructor with new operator when it is needed. So what you are trying to do is more of javascript specific question than angular specific. It does it as:
new ctor(); //or new ctor;
had it been new (ctor()) your code will work, but that is not how it happens obviously. The function reference passed in is newed up, not the result of the function execution. So in your case if you were to do this then you need to return the newed up instance. i.e
return new PageLayoutController;
Example
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.