Similar to this question, I would like to break on a variable change in Chrome. However, I'm using Angular, so the variable I would like to break on is only defined in the HTML. Its essentially something like this -
<div ng-click="show = !show">...<div>
<div ng-class="{expanded : show}">...
With no code in the controller. So how can I get Chrome to pause when show is changed? I know that I can change the code to a function call that wraps show and then put a breakpoint there, but its an inefficient use of time and I would like to know if there is a way to directly break on variable change.
Well, you can achive the desired feature via console:
First get access to the scope, on which show property is defined. Here
is an explanation of how you can do this.
Then you may observe changes of this scope object via Chrome's Object.observe.
To simplify this process you may define global function like this one:
function breakOn(property, object) {
Object.observe(object, function (changes) {
changes.forEach(function (change) {
if (change.name === property) {
console.log("Property " + property + " changed");
console.log(change);
debugger;
}
});
});
}
And then after step 1 type in the console: breakOn('show', $scope);
Related
Coming from a C++ background, trying to work with an OO language that doesn't have explicit typing is a little more than a headache.
So I have dynamic elements for a webpage that are "controlled" by objects since there are tons of stuff I need to manage on each for it to work. The element is just the visual output of the data inside of the object itself, that's all I really need it for.
Except that I need the object to perform an internal function when it's clicked. That seems to be the biggest source of my headache thus far.
Javascript:
function onClick(file) //The external onClick function I use to try to get it to call from.
{
file.state = INUSE;
file.checkState();
}
function fileObject () { //The file object itself
this.element;
this.newElement();
//initialize stuff for the object
}
fileObject.prototype.newElement = function() { //creates a new element and sets its event listener
this.element.click(function() {onClick(this)});
}
fileObject.prototype.checkState = function() {/*does stuff*/} //apparently this is "not a function"
The error I get exactly is "file.checkState is not a function" from Firefox's console panel.
I'm still new to javascript, but after doing some debugging, I've come to find out that it's explicitly the onClick(this) function that is causing all of the errors. When used with something else, the onClick function works perfectly, but for some reason, the this keyword doesn't appear to actually be sending the reference to the fileObject since all checks show file being undefined when inside of the onClick scope.
Is there something fundamentally wrong about the way I'm trying to do this or am I just missing a step (or adding something that I don't need) that will help get this snippet working.
So you know, your initial problem isn't actually handling the action, but listening to it. click will trigger a synthetic click event, rather than liste for one.
You want ... .element.addEventListener("click", callback); that said, you face a second problem, immediately thereafter.
I will leave my example code as you've written it to not confuse the matter...
But when you see click( ) know that I mean subscribing with addEventListener, if element really does mean a browser DOM element. If it's not a standard browser element, and your own API, then ignore the previous portion, and carry on.
this is dynamically bound at the invocation time of the function (not at definition time).
The nearest function, scoped above, is your callback function that you are passing into .click( ... ).
Which is entirely different than the this which you mean outside of the callback.
Whatever is on the left-hand side of the dot is the this context for the duration of that particular invocation.
Needless to say, click() doesn't know enough to bind the this you mean, to the left-hand side of your callback.
The solution (or one of many) is to use lexical scoping and/or closure to retain the value of the object you mean.
// easy but messier
var fileObject = this;
... .click(function () { onClick(fileObject); });
// Cleaner with thunks:
function clickHandler (onClick, obj) {
return function () { onClick(obj); };
}
... .click(clickHandler(this));
Coming from c++ the way Javascript handles this will seem a little crazy, it looks like here you need to tell the function you've defined what this is - like so:
this.element.click(function() {onClick(this)}.bind(this));
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.
I am trying to get some old code to work properly with minimal modification. The code was written on the assumption that it would run from a particular context. I have the context object.
Original code:
function oldExample(){
console.log(window); //New Error: window is undefined
console.log(gBrowser); //New Error: gBrowser is undefined
}
New, working code:
function fixedExample(){
console.log(this.window);
console.log(this.gBrowser);
}
//Elsewhere
function loadData(context) {
fixedExample.call(context);
}
Notes:
1. loadData and oldExample are defined in separate files.
2. context has other children besides window and gBrowser; This is an example
Is there a way to transition my oldExample code to work properly without needing to stuff this. everywhere? I.e., how can I run oldExample in a different context?
The only way I know how to do this is to define the properties as variables of the current context:
var object = {gBrowser: 'test'};
function oldExample(){
console.log(gBrowser);
}
var gBrowser = object.gBrowser;
oldExample();
This example outputs 'test'
But all this does is move the property access outside of the function definition, doesn't save you anything.
You can use bind method in javascript.
fixedExample.bind (context);
Now you need not use 'this' inside fixedExample and can use window directly.
I want to use window.open to open a window to one of my JSP file. But the browser keeps showing connecting... And even firebug stops working every time I click the text. Neither the p nor the input tags work, but when I use a href to link the JSP it can link to the file:
<!DOCTYPE html>
<html>
<head><title>Sample JSP Page</title>
<script>
function open(){
//window.open("hello.jsp","hello","height=700, width=800");
var x=window.open("hello.jsp","window","status=1,height=700, width=800");
x.focus();
}
</script>
</head>
<body>
<h1>Sample JSP Page</h1>
<p onclick="open()">do not work</p>
<form>
<input type="button" value="new window" onclick="window.open('test-with-utils')"></form>
</body>
</html>
That's because you have redefined window.open when you defined the function open. Use a different function name instead.
Change the name of the function.
The window object is the top level object in JavaScript, and contains in itself several other objects, such as "document", "history" etc.
When you define a variable or a function of your own you really add a new property to the window object. And this will work ( and a little live example ):
var foo = "bar";
alert ( window.foo ); // says "bar"
In addition if you add this little snippet to your code:
window.onerror = function ( msg, url, num ) {
alert ( "Error: " + msg + "\nURL: " + url + "\nLine: " + num );
return true;
};
you will get this error, when press the button:
Error: Uncaught RangeError: Maximum call stack size exceeded
This means an endless recursion. It is a side effect - you define a new open function, and when you call window.open(), you recursively call your function.
Just to expand on the reason that you are having problems here, you may want to read a little about javascript Scope (Very Helpful Blog). Essentially, consider the following code:
<script>
var thisOne=true;
function thatOne() {
alert("whizbang");
}
var theOther={foo:"bar"};
//More code here...
</script>
Once you reach the comment, you know you can access those variables and the function directly, like if (thisOne) {...}, element.onclick=thatOne; or console.log(theOther.foo). However, you can also access them as children of the root object which, in a web browser, is called window. So you can do:
console.log(window["thisOne"]);
window.thatOne.call(obj, params);
console.log(window.foo.bar);
so by defining open() as a function which is not inside another element (which is to say, is inside the root element), you overwrite the window.open() function. When you attempt to call the function later on, you get problems because the open function calls window.open, which calls window.open, which calls window.open...
There's a few ways to get around this -
Define the onclick handler inline
To do this, get rid of the whole <script>..</script> element then, using whichever element you choose (that supports it) add the onclick attribute:
onclick="window.open('hello.jsp','window','status=1,height=700, width=800');"
This is a nice and quick method, and it keeps all the logic right there with it's triggering element, but it is not easily extensible and you may find yourself sneered at by some. ("Oh, you use inline javascript? how quaint")
change the method name
This will take the least effort from you in terms of getting your page working now from what you have (it's also essentially what everyone else has suggested). Just change the name of the open method to something like openANewWindow() or gotoJSP() or anything that doesn't already exist in the root object, making sure to get both where you define it (in the script element) and where you use it (in the onclick attributes).
Use a closure
This is almost definitely not what you want in this case, its more complexity than you need for a single function. Just including this as an example of how to get out of the root object, seeing as being in it seems to be the heart of your problem.
You have probably already seen in javascript how to define an object, but you may not know that by defining an object, all you are really doing is adding an object property to the root object. You can use this behavior to your advantage, to give a hierarchical structure to your functions.
For example:
<script>
var MyFunctions = (function() {
function open(){
var x=window.open("hello.jsp","window","status=1,height=700, width=800");
x.focus();
}
return {open:open};
})();
</script>
This creates an anonymous function that is immediately run. Inside the scope of this function, another function, open() is defined, however it is defined within the scope of that anonymous function, not the root object (window). After open() is defined, a reference to it is returned as the value of the object property: open.
The result of all this is that the open property of the MyFunctions object is the function you need. You can then call it with MyFunctions.open() or even window.MyFunctions.open().
I am working on the new Palm Pre WebOS, the Apps for Palm Pre are developed in MojoSDK which is developed on top of Prototype Javascript Framework.
I am trying to access variables defined at assistant level in event handlers which are a part of the same assistant as well. When I access the assistant level variables in an event handler, I get it as undefined. However, the variables are accessible in the setup function.
For reference, have a look at the Code below:
Code:
function MyTestAssistant(passedValue)
{
this.passedValue = passedValue;
}
MyTestAssistant.prototype.setup = function()
{
Mojo.Log.info("Passed Value Is: " + this.passedValue); // Prints the value set in Constructor
}
MyTestAssistant.prototype.testListTapHandler = function(event)
{
Mojo.Log.info("Passed Value Is: " + this.passedValue); // Logs undefined
}
I call this here:
Mojo.Event.listen(this.myTestList, Mojo.Event.listTap, this.testListTapHandler);
Is anyone else having this issue or I am doing something wrong here? Is it possible to access the variables in handler or can we have think of workarounds to achieve it?
I'm not familiar with mojo-sdk, but this sounds a lot like you've just got your "this" reference mixed up when you set up the event handler. In all likelihood, when testListTapHandler is called, this references the object which is firing the event.
Prototype has the very handy bind() method to help clear up this confusion though.
I'll guess you had something like this
elem.observe('eventname', myTestAssistant.testListTapHandler);
Trouble is, when the event is fired, inside testListTapHandler, this will refer to elem. To correct this, we bind the event handler with the desired object:
elem.observe('eventname', myTestAssistant.testListTapHandler.bind(myTestAssistant));
I have found the solution to the problem. Another Forum helped me as well.
The core issue, as pointed out by Paul is of Binding and Scope.
I updated my implementation to the following for making it work:
function MyTestAssistant(passedValue)
{
this.passedValue = passedValue;
}
MyTestAssistant.prototype.setup = function()
{
Mojo.Log.info("Passed Value Is: " + this.passedValue); // Prints the value set in Constructor
// Was Using the following code before and this.passedValue wasn't accessible in
// testListTapHandler
// Mojo.Event.listen(this.testList, Mojo.Event.listTap, this.testListTapHandler);
// Used the following code now and this.passedValue is accessible in
// testListTapHandler
this.testListTapHandler = this.testListTapHandler.bindAsEventListener(this);
Mojo.Event.listen(this.testList, Mojo.Event.listTap, this.testListTapHandler);
}
MyTestAssistant.prototype.testListTapHandler = function(event)
{
Mojo.Log.info("Passed Value Is: " + this.passedValue); // Prints the value set in Constructor
}
Thanks for your help Paul.
Regards,
Muhammad Haseeb Khan