AngularJS Scope Apply does not working on click - javascript

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.

Related

How to trigger element.on('mouseleave') in a Uint Test to call my AngularJS function

I want to call myFunction. Then I want to verify it was called with a Unit Test.
Javascript:
var self = this;
self.myFunction= function myFunction() {
// do stuff
};
self.onClickChange = function onClickChange() {
$('.checkBox').on('mouseleave', function(){
self.myFunction();
});
};
I have tried this test:
it('should call myFunction', inject(function ($compile, $rootScope) {
spyOn(this.instance, 'onClickChange').and.callThrough();
spyOn(this.instance, 'myFunction').and.callThrough();
this.instance.onClickChange('messageBoard');
var scope = $rootScope.$new(),
element = $compile('<md-checkbox class="checkBox"></md-checkbox>')(scope);
element.triggerHandler('mouseleave');
scope.$digest();
expect(this.instance.myFunction).toHaveBeenCalled();
}));
I have also tried using
scope.$broadcast('mouseleave');
And
var event = document.createEvent('MouseEvents');
event.initEvent('mouseleave', true, true);
$('.checkBox').dispatchEvent(event);
And this instead of the last line in the previous three lines:
document.body.dispatchEvent(event);
How can I get .on('mouseleave') to work and ferify that myFunction has been called?
Several things here. First, to answer your immediate question, you are calling: this.instance.onClickChange('messageBoard'); before you have created any DOM. So, you are going to try adding the click handler to absolutely nothing.
You need to add the click handler only after you create the DOM when you call compile.
But, there's another problem. You are compiling the directive and creating a DOM element, but it is not rooted in the document, so using the jquery $ on the document will not work.
It is bad style to use jQuery selectors in angular.
You could do something like this, where you use the $element of the current scope.
var self = this;
var $element = getElement(); // do some magic to get the current element
self.myFunction= function myFunction() {
// do stuff
};
self.onClickChange = function onClickChange() {
$element.on('mouseleave', function(){
self.myFunction();
});
};
And then your tests could work in the same way. The difference is that the $element never needs to be added to the DOM in order for the code to work.
But really, I would avoid this whole approach. You should not be using jQuery inside of angular. That is an outdated style. Instead, I recommend using ng-click to add the handler directly to the DOM.

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+

Why I can't see a $scope variable that was defined in a $scope function?

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.

Check the availability of a javascript funcion in a page

What I want is very simple, I want the Expand All button to be auto clicked when I open this pluralsight course page. Its HTML is:
<a id="expandAll"
ng-click="expandAllModules()"
ng-hide="allModulesExpanded()">
Expand All
</a>
So it seems easy and we just need to call the function expandAllModules(). However I don't know why it give me undefined when I check its type:
typeof expandAllModules
=> "undefined"
Generally typeof a function should give me "function" like this:
function a(){}
=> undefined
typeof a
=> "function"
Since the function expandAllModules() is not available, I can't call it. Anyone can give me a hand on this issue?
Edit
Perhaps I need to elaborate on my question. I'm not the author of that page. I just want to make a simple greasemonkey or tempermonkey script and expand the modules automatically when I enter the page.
The Problem
The reason calling just expandAllModules() doesn't work is because this function belongs to one of Angular's scopes and isn't a method assigned to window. This function is defined in Plural Sight's table-of-contents-controller-v9.js like so:
"use strict";
pluralsightModule
.controller("TableOfContentsController", ['$scope', ..., function ($scope, ...) {
...
$scope.expandAllModules = function() {
_.each($scope.courseModules, function (module) { module.visible= true; });
};
...
}])
The Solution
In order for us to call this function ourselves, we have to go through this scope.
scope is an object that refers to the application model. It is an execution context for expressions. Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events. – AngularJS: Developer Guide
The scope is part of the element which triggers the function. We can access this particular scope by passing the element's id attribute into angular.element(), then calling scope() on that object:
angular.element('#expandAll').scope()
This will give us the following data, where we can see the expandAllModules() function:
Unfortunately AngularJS doesn't let us simply execute scope().expandAllModules(); instead we have to go through it's $apply and $eval methods:
var scope = angular.element('#expandAll').scope();
scope.$apply(function() {
scope.$eval(scope.expandAllModules())
});
We can now also collapse the modules as well by calling:
scope.$apply(function() {
scope.$eval(scope.collapseAllModules())
});
I apologize if I am off-base here. Are you trying to "link" into that page and 'force' the page to "expand all", or do you have access to the page, and want to trigger the click with some code on the page, by you inserting the code? Just doing something like this seems to work from commandline.
jQuery(function(){
jQuery('#expandAll').trigger('click');
});
Since I do not know your need, my thought is that this is a bit simplistic and not what you are looking for. From the responses of others, it appears you want to create your own directive to initiate the click?
I might have some typos -- but the idea is there.
angular.element(document.body).ready(function() {
var el = angular.element( document.getElementById('expandAll') );
var scope = el.scope();
scope.expandAllModules();
scope.$digest(); <--- might not be needed, but when i check your site, it needs to have this
});
updates
if it was just 'onclick' instead of 'ng-click', you do not need to get the scope; and just call the function directly.
updates
I have tried this on your site, you need to have scope.$digest(). When I tried it, i was using the developer console.
see the developer console below
I was playing with it on your site.

controller and link functions in directive do not have same scope

I have an issue with my angular.js directive.
It should be a kind of autocomplete, in directive's controller property I'm loading an array of values and inside link function compiling template to show the results.
But when I update scope inside link it doesn't reflect on controller and template, please take look at the example here - http://plnkr.co/edit/Lz3QGwklghPo3as2QTqU
Should I apply scope changes or smth similar?
Your code has two problems
Attach click event to document instead of body
Use $apply() inside bind
Below code will resolve your problem
$document.bind('click', function (e) {
scope.results = [];
scope.$apply();
});
I update your $body.bind('click',...) method to
$body.bind('change', function (e) {
scope.results = [];
});
and it seemed to work (I mean that after 0.5 sec I typed a letter, the list of name is re-displayed).

Categories

Resources