I'm working on a table in my Angular 2 application. I'm using ag-grid. this is my gridOptions
this.gridOptions = <GridOptions>{
onGridReady: (() => {
this.gridOptions.api.sizeColumnsToFit();
this.gridOptions.onFilterModified = function () {
console.log("Value Changed")
}
})
}
This code works fine. If i change the value in the search field, i will get the text "Value Changed" in the console. I want to use a variable in the OnFilterModified function that is defined outside that function , but it doesn't work.
console.log(this.) after the dot i only get onGridReady function. That's it. How can i access other functions/variables in the OnFilterModified function?
This isn't really an ag-grid or angular 2 quesion, but rather a javascript scoping one.
In the function (and I assume you're just using javascript here, not typescript) "this" isnt the out scope, but rather the functions, which isn't useful.
The easiest fix for you is to do the following:
var that = this;
this.gridOptions = <GridOptions>{
onGridReady: (() => {
that.gridOptions.api.sizeColumnsToFit();
that.gridOptions.onFilterModified = function () {
console.log(that.gridOptions)
console.log("Value Changed")
}
})
};
I'd check out this (there are many articles on this subject) for more information about JavaScript scope: http://ryanmorr.com/understanding-scope-and-context-in-javascript/ for a better understanding of the issue.
Related
I am trying to port some javascript to actionscript 3. I am new to javascript, and whereas much of js is familiar, I am having a devil of a time deciphering some js code. A global variable, "action", seems to be defined in a function prototype call, which is then apparently referenced later as a function parameter in the body of the script:
Variable "action" Definition:
function SteppedAction(proUpdater, unbrInterval, slInterval) {
}
SteppedAction.prototype.getResult = function
SteppedAction_getResult(recipient)
{
this.subactions.push({
action: function(action)
{
// function body
},
prop: 0
});
return this;
};
In the body of the script, this same "action" seems to be referenced again in other functions, even though it appears to be defined as a function parameter itself in other anonymous functions and as an argument in the generatePl() function:
function generate () {
activeAction = new SteppedAction(updateProgressUI)
.executeSubaction(function(action) { ui.progressPanel.show(); }, 0)
.executeSubaction(function(action) { generatePl(subdivs,
dist, count, rate, level, action); });
}
I am using Sublime Text to help decipher the javascript, and when I hover over any of the "action" named variables anywhere in the script, whether as function parameters or function call arguments, it references the original "action" in the definition given above. I simply do not understand this. How does a function parameter "know" it is referring to the original variable definition, unless function parameters in anonymous functions can somehow obtain global scope? I do not use anonymous functions normally.
The code for the SteppedAction.executeSubaction() is:
SteppedAction.prototype.executeSubaction = function
SteppedAction_executeSubaction(subaction, proportion, name) {
proportion = (typeof(proportion) === "number" && proportion >= 0) ?
proportion : 1;
this.subactions.push({ action: subaction, proportion: proportion,
name: name });
return this;
};
Any help would be greatly appreciated.
The short answer to your question is that the variable name of a function declaration is independent of its surrounding body. The variable name will "shadow" any previously declared variable. For example:
const action = "local variable";
const myFn = (action) => console.log("inside fn, action is", action);
myFn("other variable")
You'll notice that in this case, even though the myFn has a function argument of action it is completely independent of the action in the outer scope.
Similarly, it's important to note that this.subactions is storing a function as action, and not executing the action. Here's an example:
const subactions = [];
const myFn = (action) => console.log("calling myFn with", action);
console.log("about to push subactions");
subactions.push({action: myFn});
console.log("action pushed");
console.log("calling myFn");
subactions[0].action("other variable");
Notice that, very similarly to the above, the action parameter of myFn isn't set until it's called, and it has nothing to do with the outer scope. I hope this clears up your questions.
Addendum
I was putting together your code snippets and here's the ES2015 version of your code, if it helps. Hopefully the syntax is a bit more understandable.
class SteppedAction {
constructor(proUpdater, unbrInterval, slInterval) {
this.subactions = []; // missing but presumably exists
}
getResult(recipient) {
this.subactions.push({
action: (a) => {},
prop: 0, // should this be proportion to match executeSubaction?
});
return this;
}
executeSubaction(subaction, proportion, name) {
proportion = (typeof(proportion) === "number" && proportion >= 0) ?
proportion : 1;
this.subactions.push({
action: subaction,
proportion: proportion,
name: name
});
return this;
}
}
const generate = () => {
const activeAction = new SteppedAction();
// ui is not defined here.
activeAction.executeSubaction(() => ui.progressPanel.show(), 0);
// several of these variables are not defined in the code snippet
activeAction.executeSubaction((action) => generatePl(subdivs, dist, count, rate, level, action));
}
So, here's what I found out (at least how the Sublime Text text editor seems to work) through fiddling with the text editor: when one defines a function at the global level, (named or assigned to a variable or object), that function is evidently assigned a namespace that is referenced within the rest of the program, including function parameter definitions and arguments.
Thus,
myArray.push(action: function(x){});
var action = function(x){};
function action(x){}
will all create a reference to themselves for future calls using the "action" identifier (including within function parameter signatures), so that the parameter, "action," in
var myFunction(action) {}
will reference all three of the function declarations above when the cursor is hovered over it, giving line numbers where each are defined/declared. This only seems to happen with global function declarations. Regular global variables with a single value ARE NOT referenced within function parameter signatures like function declarations are.
As long as this is a quirk in Sublime Text itself and not some strange convention in javascript, where global function declarations create a namespace issue within subsequent function parameter signatures, I can understand it, and ignore it in the future. Thanks for the help!
This question has already got some response here. But this doesn't seem to be working for me.
I am trying to create a tree structure using jQuery. The problem is I can not use a declared angular 4 variable inside a jQuery function. Here's the code.
employees = ["Mr. John", "Mr. Steve"];
ngOnInit() {
(function($) => {
function OrgChart($container, opts){
console.log(this.employees);
}
});
}
I see an error in console stating, "Function declarations are not allowed inside blocks in strict mode when targeting ES3 or ES5"
1st (the employees of undefined problem)
To bind the "this" of the component, use the arrow notation for functions :
($) => { console.log(this.employees)}
instead of function($) { ... }
2nd (the "function declarations are not allowed inside blocks")
You could declare your other inner function in another place in your Angular component, and then refer to it :
ngOnInit() {
// this declaration is nonsense to me, this will declare a function that is never called and exists only here, but anyway...
($) => {
// you can call OrgChart here :
this.OrgChart()
}
}
OrgChart($container, opts) {
console.log(this.employees);
}
You need store angular component reference in another variable before start jquery block.
export class PlanGruposComponent implements OnInit {
group = 'Meu grupo';
OnInit() {
//reference of currect component object
let temporaryThis = this;
$(document).ready(function () {
console.log("Print : " + temporaryThis.group);
});
}
}
I am working with Ionic2 and Meteor. I do however have a Javascript/Typescript issue relating to the scope of the this object.
I have read that I should use bind when I don't have handle on this at the appropriate level.
I probably don't understand the concept, because I try the following, but get an error trying to call a function.
this.subscribe('messages', this.activeChat._id, this.senderId, () => {
this.autorun(() => {
let promiseMessages: Promise<Mongo.Collection<Message>> = this.findMessages();
promiseMessages.then((messageData: Mongo.Collection<Message>) => {
messageData.find().forEach(function (message: Message) {
setLocalMessage.bind(message);
});
});
});
and
private setLocalMessage(message: Message): void {
this.localMessageCollection.insert(message);
}
I get the following error when I try build the app:
ERROR in ./app/pages/messages/messages.ts
(72,19): error TS2304: Cannot find name 'setLocalMessage'.
UPDATE
Thank you for the advise below.
I am now using the following, and it works.
let promiseMessages: Promise<Mongo.Collection<Message>> = this.findMessages();
promiseMessages.then((messageData: Mongo.Collection<Message>) => {
messageData.find().forEach((message: Message) => {
this.setLocalMessage(message);
});
});
I have read that I should use bind when I don't have handle on this at the appropriate level.
That's a bit outdated now, better have a look at How to access the correct `this` context inside a callback? these days which also shows you how to use arrow functions.
You're getting the error message because setLocalMessage is not a variable but still a property of this so you have to access it as such. There are basically three solutions in your case:
bind
messageData.find().forEach(this.setLocalMessage.bind(this));
the context argument of forEach (assuming it's the Array method):
messageData.find().forEach(this.setLocalMessage, this);
another arrow function:
messageData.find().forEach((message: Message) => {
this.setLocalMessage(message);
});
There are a few things wrong here.
In ES6 (and thus TypeScript), you need to refer to instance members using explicit this, such as this.setLocalMessage. Just writing setLocalMessage is invalid no matter where the code is.
Inside a function, the this object will probably not be what you expect anyway. You need to capture the this object from outside the function and put it in a variable, like so:
this.subscribe('messages', this.activeChat._id, this.senderId, () => {
this.autorun(() => {
let self = this;
let promiseMessages: Promise<Mongo.Collection<Message>> = this.findMessages();
promiseMessages.then((messageData: Mongo.Collection<Message>) => {
messageData.find().forEach(function (message: Message) {
self.setLocalMessage(message);
});
});
});
Alternatively, you can use an arrow expression, in which this is the same as what it is in the code around it:
this.subscribe('messages', this.activeChat._id, this.senderId, () => {
this.autorun(() => {
let promiseMessages: Promise<Mongo.Collection<Message>> = this.findMessages();
promiseMessages.then((messageData: Mongo.Collection<Message>) => {
messageData.find().forEach(message => this.setLocalMessage(message));
});
});
});
It's not an issue of TypeScript itself. Without it, the code will just fail at runtime.
So I have an APP I'm working on in Angular JS v1.4.0 and I'm running into a scoping issue. There's a section that has a form that needs to be submitted, but the data needs to be modified before its sent over. I'm currently trying to do this in the javascript before making the call to the server.
I have $scope.msgEditor, that is an object of a bunch of different values that are necessary for the form, as well as the message variables itself. The important part looks something like this:
msgEditor [
msg: {
groups: {
selected: {
0: '1',
1: '2',
}
}
}
]
I'm trying to take this $scope variable, assign it to a local variable, and begin parsing the data like such:
$scope.formOnSubmit = function () {
formattedMessage = formatDataForSave($scope.msgEditor.msg);
};
function formatDataForSave(message) {
message.groups = message.groups.selected.join(', ');
return message;
}
What I want to happen, is $scope.msgEditor.msg to not change at all, and formattedMessage to be returned from the second function, so it can be placed into a $http call. However, the join changes message, formattedMessage, AND $scope.msgEditor.msg
I did a bit more testing, to see what was happening:
$scope.formOnSubmit = function () {
$scope.test = $scope.msgEditor.msg;
var formattedMessage = $scope.test;
formattedMessage = formatDataForSave(formattedMessage);
};
And found that the change made to formattedMessage, would change $scope.test, which would change $scope.msgEdtior.msg.
Any direction on why this is happening, or how to prevent it would be amazing.
I believe you are confusing about passing arguments into functions in javascript: in javascript all arguments are passed by reference so the consequence is what you are experiencing. Have a look at angular.copy function.
https://code.angularjs.org/1.3.17/docs/api/ng/function/angular.copy
I cannot test this but you could try:
$scope.formOnSubmit = function () {
var msgCopy = angular.copy($scope.msgEditor.msg);
formattedMessage = formatDataForSave(msgCopy);
};
function formatDataForSave(message) {
message.groups = message.groups.selected.join(', ');
return message;
}
I'm trying to implement a custom binding for an accordion-like document layout on a webpage, but I'm encountering an issue I can't easily solve.
Immediately on page load, I am presented with the error:
Uncaught TypeError: Unable to process binding "accordion: function (){return currentAccordionSection }"
Message: undefined is not a function
I have tried declaring my observable as both a function and normally in the data-bind syntax without success. I have initialized my observable with a default value (null) and it has not fixed this issue. Below is my entire ViewModel:
var libraryViewModel = function () {
var self = this;
ko.bindingHandlers.accordion = {
update: function (element, valueAccessor) {
console.log(ko.unwrap(valueAccessor()));
var value = ko.unwrap(valueAccessor());
var section = $(element.text());
//ko.bindingHandlers.css.update(element, function () {
// if (value === section) {
// return 'library-section-active';
// }
//});
//ko.bindingHandlers.css.update($(element).children('i:last-child').get(0), function () {
// if (value === section) {
// return 'fa fa-chevron-up';
// } else {
// return 'fa fa-chevron-down';
// }
//});
}
}
self.currentAccordionSection = ko.observable(null);
self.updateAccordionSection = function (section) {
self.currentAccordionSection(section);
}
}
Some of the code above is commented out as it is not relevant to the problem at hand and I have disabled it to implement a reduced test case to narrow down the problem. Here is my binding declaration:
<h2 class="library-header" data-bind="accordion: currentAccordionSection, click: updateAccordionSection.bind('Text')">
What exactly am I doing wrong?
The problem is this line:
var section = $(element.text());
as per knockout's documentation
element — The DOM element involved in this binding
text is a jQuery function not a DOM function so I think you are looking for something like:
$(element).text() or $($(element).text()) instead? I'd assume the former since it makes more sense.
As for the nested binding handler I'm not sure why that is in the viewmodel since it's exposed on the knockout global object you're not protecting yourself from anything just making your code more unreadable. They are designed to be resuable so you can use them with different viewModels