AngularJS 1 events - "this" variable in callback method is the window - javascript

I'm trying to learn to use AngularJS 1.6 events.
I created a component which is registered to the event:
'use strict';
var module = angular.module("Test", []);
module.controller("Controller1", Controller1);
Controller1.$inject = ["$scope"];
function Controller1($scope) {
var self = this;
self.try = function () {
$scope.$broadcast("test:try");
}
}
module.controller("Controller2", Controller2);
module.component("compo2", {
controller: "Controller2",
});
Controller2.$inject = ["$scope"];
function Controller2($scope) {
var self = this;
this.$onInit = function () {
$scope.$on("test:try", self.try);
}
self.try = function (event, data) {
console.log("Logged event!!!")
}
}
and this is my html file:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./Test/angular.js"></script>
<script src="./compoTest.js"></script>
</head>
<body ng-app="Test" ng-controller="Controller1 as ctrl1">
<compo2> T.E.S.T </compo2>
<button ng-click="ctrl1.try();"> Try! </button>
</body>
</html>
But when the callback "try" is called - I see that the this variable is the window instead of my component's controller.
What am I doing wrong?

This is what happens when object method is called as a callback in JS.
To avoid accidental use of window, use strict mode in the beginning of JS file:
'use strict';
The method should be bound to the context:
this.try = function (event, data) {
this....
}.bind(this);
Or self recipe should be used:
var self = this;
self.try = function (event, data) {
self....
};

Related

Backbone event callback bind

I am trying to bind a user defined callback as Backbone's click event.
var View = Backbone.View.extend({
events: {
'click': 'testClick'
},
tagName: "li",
attributes: {
class: "item"
},
initialize: function() {
this.render();
},
testClick: function(){
},
render: function() {
$("#container").append(this.$el.html("Click Me!!"));
}
});
function Item() {
var _view = View.extend({
testClick: this.testClick.bind(this)
});
this.view = new _view();
}
Item.prototype = {
testClick: function() {
alert("testClick from prototype called!");
}
};
var myItem = new Item();
myItem.testClick = function() {
alert("testClick from instance called!");
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
</head>
<body>
<div id="container"></div>
</body>
</html>
Clicking on "Click me", it alerts "testClick from prototype called!"
I am not sure why the alert from the instance is not getting called. What am I doing wrong here? Please help!
Because the following line:
testClick: this.testClick.bind(this)
detaches the testClick function member of the Item instance. You are essentially reusing a function and there is no linkage between the 2 methods.
Consider this example:
var obj = {
foo: function() {
console.log('I was foo!');
}
}
var detachedFoo = obj.foo;
obj.foo = function() {
console.log('The new foo!');
}
obj.foo === detachedFoo // false
obj.foo() // logs 'The new foo!'
deatchedFoo(); // logs 'I was foo!'
If you use the following syntax the alert will show "testClick from instance called!".
testClick: () => {
this.testClick();
}
This is because the above code calls the current .testClick method of the Item instance.

How can I send a statement to be used as a command within code?

What I've been trying to do is to pass a statement like oninput or onmouseover and then use them as actual statements in a method.
function Controller() {
this.listen = function(elem, func, statement) {
elem.statement = function() {//and then use it here
func(elem);
};
return true;
};
}
var age = document.getElementById('age');
var controller = new Controller();
controller.listen(age, callMe, oninput);//set the statement here
function callMe(elem) {
console.log('hey');
}
<!DOCTYPE html>
<html>
<head>
<title>HTML5, CSS3 and JavaScript demo</title>
</head>
<body>
<input type="number" id="age" />
</body>
</html>
Is there a way to do this?
Pass the oninput as a string ("oninput") and then use square brackets to access the property of elem:
function Controller() {
this.listen = function(elem, func, statement) {
elem[statement] = function() {//and then use it here
func(elem);
};
return true;
};
}
var age = document.getElementById('age');
var controller = new Controller();
controller.listen(age, callMe, "oninput");//set the statement here
function callMe(elem) {
console.log('hey');
}
However, I recommend you using element.addEventListener() instead of .onevent, see addEventListener vs onclick.

Unable to target a selector using Namespace ,Modular, Private

i am trying to target a jquery selector by using namespaces in my script and also making function private but i think i am still missing something here, can anyone guide. It works if i try by adding a breakpoint on the last line and than use devtools to access MyUtility.Selectors.ColorCss.myBorder()
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Selectors</title>
</head>
<body>
<ul>
<li class="test">First</li>
<li>Second</li>
<li>Third</li>
</ul>
<!--<script>
$('document').ready(function(){
$('li.test').css('color','green')
})
</script>-->
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script>
var customModule = (function () {
var MyUtility = {
Selectors: {
ColorCss: function () {
var myBorder = function () {
$('li').css('color', 'red')
console.log('hello')
}
return{
myBorder: myBorder
}
}()
}
}
}())
</script>
</body>
</html>
As you said It works if i try by adding a breakpoint on the last line and than use devtools to access MyUtility.Selectors.ColorCss.myBorder()
This is your code:
var customModule = (function () {
var MyUtility = {
Selectors: {
ColorCss: function(){
var myBorder = function(){
$('li').css('color', 'red');
console.log('hello');
}
return{ myBorder: myBorder }
}()
} // Selectors
} // MyUtility
}())
Your code above can be written as:
function myBorderFunc() { $('li').css('color', 'red'); console.log('hello');}
var selectorObj = { ColorCss : function(){ return{ myBorder: myBorderFunc } }()};
var MyUtility = { Selectors: selectorObj};
var customModule = ( function(){ MyUtility }() );
This shows the problem
var customModule is a function expression that does not return anything and it is therefore undefined
since customModule is undefined you can not use customModule.MyUtility
as you said you can call MyUtility.Selectors.ColorCss.myBorder() since MyUtility is an object that has a property Selectors and so on
you can test it out with this example:
// undefined since nothing is returned
var bar = (function(){ {Foo: "i am foo"} }());
// returns foo and can be used bar.Foo ---> "i am foo"
var bar = (function(){ return {Foo: "i am foo"} }());
To 'fix your code' return MyUtility
var customModule = (function () {
var MyUtility = {
Selectors: {
ColorCss: function(){
var myBorder = function(){
$('li').css('color', 'red');
console.log('hello');
}
return{ myBorder: myBorder }
}()
} // Selectors
} // MyUtility
return MyUtility;
}())
This way you can access it like this customModule.Selectors.ColorCss.myBorder().
More info about Function expressions vs. Function declarations

Javascript "this" issue in Model View Presenter design

The issue is in the model, when recalled by the presenter it does not work like I assumed, in fact it works as if the this keyword refers to an [object HTMLTextAreaElement], not to a Model object.
/** PRESENTER **/
function Presenter() {
var toolbarView;
var outputView;
var sourceCodeModel;
var public = {
setToolbarView: function (view) {
toolbarView = view;
},
setOutputView: function (view) {
outputView = view;
},
setModel: function (_model) {
sourceCodeModel = _model;
},
init: function () {
toolbarView.setNewTableHandler(function () {
outputView.updateSource(sourceCodeModel.string);
});
toolbarView.setNewConstraintHandler(function () {
/*stub*/
alert("new constraint");
});
}
}
return public;
}
/** TOOLBAR VIEW **/
function toolbarView() {
this.setNewTableHandler = function (handler) {
$("#newTable").click(handler);
}
this.setNewConstraintHandler = function (handler) {
$("#newConstraint").click(handler);
}
}
/** OUTPUT VIEW **/
var outputView = {
updateSource: function (newVal) {
$("#sourcetext").val(newVal);
},
draw: function () {
//stub
}
};
/** MODEL **/
var model = new Object(); //o {};
model.source = [];
model.string = function () {
/* ALERT(this) returns [object HTMLTextAreaElement] wtf? */
var stringa = "";
for (var i = 0; i < this.source.length; i++) { //this does not work, since this = HTMLTextAreaElement
stringa += this.source[i] + "\n";
}
return stringa;
}
$(document).ready(function () {
var presenter = new Presenter();
var view1 = new toolbarView();
presenter.setToolbarView(view1);
presenter.setOutputView(outputView);
presenter.setModel(model);
presenter.init();
});
and the HTML is pretty simple:
<!doctype html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="mvp.js"></script>
<meta charset="UTF-8">
<title>Titolo documento</title>
<style type="text/css">
/*unnecessary here*/
</style>
</head>
<body>
<div id="container">
<div id="toolbar">
<button id="newTable">New table</button>
<button id="newConstraint">New Constraint</button>
</div>
<div id="source">
<textarea id="sourcetext"></textarea>
</div>
<button id="update">Update</button>
<div id="output"></div>
</div>
</body>
</html>
What am i doing wrong on the model object?
When you pass a function as a listener the this property will not be available inside the function:
var obj = {
a: function() {
alert(this)
}
};
$('body').click(obj.a);
When body is clicked the function's this property will be document.body.
To prevent this you must bind the function:
$('body').click(obj.a.bind(obj));
Or in older browsers wrap it:
$('body').click(function() {
obj.a();
});
So you must bind the function before pass it:
outputView.updateSource(sourceCodeModel.string.bind(sourceCodeModel));
More info about javascript function's context: http://www.quirksmode.org/js/this.html
this line: var public = { try to do not to use public, that is reserved word.
And a general note, try to bind this to a variable, because this changes context where it is currently.
/** TOOLBAR VIEW **/
function toolbarView() {
var that = this;
that.setNewTableHandler = function (handler) {
$("#newTable").click(handler);
}
that.setNewConstraintHandler = function (handler) {
$("#newConstraint").click(handler);
}
}

HTA Javascript - why is object out of scope in onbeforeunload?

This is a bit of a strange one. I have been trying to call a function in a child object from the parent object but it seems to be going out of scope in onbeforeunload function. These function calls work outside of a onbeforeunload, so it only fails when called in onbeforeunload. I can fix it by making the child object a global but I was trying to avoid that. Anyone know why my childobject is out of scope when called in onbeforeunload? Notice I call that same function in windows onload event and it works fine. Here is the code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Broken HTA</title>
<HTA:APPLICATION
ID="Broken HTA"
APPLICATIONNAME="Broken HTA"
BORDERSTYLE = "flat"
CAPTION="Yes"
CONTEXTMENU = "no"
INNERBORDER = "no"
MAXIMIZEBUTTON = "no"
MINIMIZEBUTTON = "yes"
NAVIGABLE = "No"
SCROLL = "auto"
SCROLL FLAT = "Yes"
SELECTION="no"
SYSMENU="yes"
SHOWINTASKBAR="yes"
SINGLEINSTANCE="yes"
VERSION = "1.0"
BORDER="thick"
>
<script language="javascript" type="text/javascript">
var myparent;
function windowLaunch()
{
myparent = new parent();
myparent.getchildvalue();
window.onbeforeunload = myparent.getchildvalue;
window.resizeTo(500, 610);
}
function parent()
{
this.mychild = new childobject("myinput");
this.getchildvalue = function()
{
var tmpval = this.mychild.returnvalue();
};
}
function childobject(thename)
{
this.myprop = thename;
this.returnvalue = function()
{
return (document.getElementById(this.myprop).value);
};
}
</script>
</head>
<body id="thebody" onload="windowLaunch();">
<div id="outerdiv">
<span title="This Input Box">My Input:</span><br />
<input id="myinput" style="width: 290px"/>
</div>
</body>
</html>
this is based on how a function is called. Calling myparent.getchildvalue() is fine, but as soon as you assign myparent.getchildvalue as a handler, it will be called out of context. You can demonstrate this simply:
var obj = { val: 42, fn: function(){ alert(this.val) } };
ref = obj.fn;
alert(obj.fn === ref); // true, so expect the following to do the same thing...
obj.fn(); // 42, so far so good
ref(); // undefined. uh oh...
You can get around this by wrapping:
...
window.onbeforeunload = function(){ myparent.getchildvalue(); };
...
or binding:
...
window.onbeforeunload = myparent.getchildvalue.bind(myparent);
...

Categories

Resources