this point set to null using afterSelectionChange in ngGrid - angularjs

I'm writing an application using angular and typescript.
I'm using ng-grid and I have to handle the afterSelectionChange event.
I tried to set the event handler in two ways
this.$scope.settoriGridOptions.afterSelectionChange = this.afterSelectionChange;
where this.afterSelectionChange is a method of the controller class,
and
this.$scope.settoriGridOptions.afterSelectionChange = (... ) => {};
including the code inside, but in both cases the this pointer is incorrect and I cannot access to the services of the controller.
how can I fix this?
after a more tests and reading a few articles I see that the problem is the implicit passing of the this pointer as parameter in the function call.
if I write
$scope.filtroSoluzione = this.filtroSoluzione;
when called the this pointer is set to null, but if I write
$scope.filtroSoluzione = () => { return this.filtroSoluzione() };
or
$scope.filtroSoluzione = () => { .. inline code ... };
the this pointer I set correctly.
How can I have a more consistent behavior? I don't like to write always the code inside because this makes the class harder to read and navigate
thanks,
Luca

Thanks for the extra information in your edits, I now see the problem.
class foo {
public afterSelectionChange = () => {
console.log(this);
}
}
When you declare your function like I did above, your this is the instance instead of what you are seeing know because it captures the this variable. It comes with a cost though, because now it creates a new afterSelectionChange function for every instance of your class. In this case I think it is still what you want though.
var foo = (function () {
function foo() {
var _this = this;
this.afterSelectionChange = function () {
console.log(_this);
};
}
foo.prototype.baz = function () {
console.log(this);
};
return foo;
})();
In the above code-gen you can see the difference when declaring the function with name = () => {} and the normal way.
Another solutions might be this:
this.$scope.settoriGridOptions.afterSelectionChange = this.afterSelectionChange.bind(this);
But I don't find that really nice either... (this should work with the normal public afterSelectionChange() {} declaration you are used to.

Related

ReactJS map: Expected an assignment or function call and instead saw an expression - no-unused-expressions

I'm in the midst of cleaning up errors for a repo and I've come across this error where someone's trying to to assign a tag value object to a const variable inside of a map function. Here's its current form:
const BatchEditState = {
CURRENT: 'CURRENT',
DELETE: 'DELETE',
PUT: 'PUT',
}
handleShow = () => {
this.batchEditSet = {};
this.state.currentTags.map((tag) => {
this.batchEditSet[tag.tag_name] = BatchEditState.CURRENT;
});
};
As far as I've researched, one is definitely not supposed to go about it this way even if it does still function. I've seen plenty examples returning a jsx element, but I'm pretty sure that's not the point for this. I do know a map function is supposed to at least return a value however.
I attempted to use a spread operator and an implicit return, but that didn't work out. I also tried making a basic return & even though I'm not encountering any immediate errors in our application, I'm still not sure if this is the right way to go. Still fairly new at this, but appreciate any info, help, and education I can get
handleShow = () => {
this.batchEditSet = {};
this.state.currentTags.map((tag) => {
this.batchEditSet[tag.tag_name] = BatchEditState.CURRENT;
return(
BatchEditState.CURRENT
)
});
};
.map is only for creating new arrays by iterating over an existing array. While you want to iterate over an existing array, you don't want to create a new array - rather, you want to construct a plain object - so .map should not be used here. (The array you're constructing in your current code is going unused.)
To procedurally assign to properties of the object, do:
handleShow = () => {
this.batchEditSet = {};
this.state.currentTags.forEach((tag) => {
this.batchEditSet[tag.tag_name] = BatchEditState.CURRENT;
});
};
Or create an array of entries, then turn that array into an object.
handleShow = () => {
this.batchEditSet = Object.fromEntries(
this.state.currentTags.map(tag => [tag.tag_name, BatchEditState.CURRENT])
);
};
But also, doing this.batchEditSet = in the first place looks like a mistake in React. If this is a component, you should almost certainly be calling this.setState instead of mutating the instance.

Check which parameters were passed when a function is called

I am unit testing a method where I do two different things with same parameter.I want to check which parameters are passed in both the statements. and either they are same.
Method I am testing
doWork(value: boolean) {
if (value) {
add(value);
} else {
delete(value);
}
}
I know I can test with "toHaveBeenCalledWith" for a specific value.
But here I would like to test where it's the same for add and for delete.
I think you can take advantage of callFake on Jasmine. callFake calls the function you supply every time that function is called.
Something like this.
it('should call add and delete with the same value', () => {
const addParameter;
const deleteParameter;
spyOn(component, 'add').and.callFake(value => addParameter = value);
spyOn(component, 'delete').and.callFake(value => deleteParamter = value);
component.add(5);
component.delete(5);
expect(addParameter).toBe(deleteParameter);
});
Of course that is a contrived example but something like that should get you going. I also wrote it as if you were using Angular 2+.

How to properly save self reference with ES6 classes?

Honestly, I'm not sure of what is the cause for the behavior: systemjs, babel or my own fault. I'm using class for custom control controller and saving class reference in self variable. Apparently that gets overriden by any subsequent controller instances.
I created a simple repository to demonstrate:
clone, install, run live-server or your preferred server. You will see 2 buttons, each is a custom control. Clicking on a button only affects one control.
https://github.com/alexkolt/selfIsThis
How can I get this working with ES6 class?
I should have posted the code, sorry.
The reason you'd want to save reference to self is for example in callbacks calling this might result in a different reference.
I was trying to do this:
var self;
class Test {
constructor(dependency) {
self = this;
self.dependency = dependency;
}
method() {
self.dependency().then(value => self.property = value);
}
}
Like it was mentioned before the self becomes shared when declared outside of the module. I didn't realize that would happen as files would be wrapped in a closure. Joe Clay answer is correct, but to do what I was trying to do self needs to be declared in every method that needs it.
class Test {
constructor(dependency) {
this.dependency = dependency;
}
method() {
var self = this;
this.dependency().then(value => self.property = value);
}
}
You're not really using ES6 classes right. You don't need to save a reference to this - just access it directly in class methods. The way you have it at the minute, all your instances of CustomControlController are sharing a single self variable.
class CustomControlController {
constructor() {
this.value = 0;
}
click() {
var newValue = this.value * 2;
this.value = newValue;
}
}
export default CustomControlController;

Why do i have to re-declare properties in typescript angular controller functions

export interface IFooModel {
foo:string;
fooFunction(fooProp:string):void;
}
export class FooCtrl implements IFooModel {
constructor(public foo:string){
}
fooFunction(fooProp:string):void{
}
}
The code above is fairly standard. My question is , when i want to access foo:string in the function i have to do this
fooFunction(fooProp:string):void{
var fooAgain = this.foo;
// Pretend i set it up properly for $mdDialog to work
this.$mdDialog.show(options).then(function(answer: boolean) {
if (answer) {
// fooAgain works
// this.foo does not work
}
}
Why do i have to set this.foo to a variable in order to access it inside another function , instead of just writing this.foo ? In some functions i end up for about 4 variables declarations that are already declared in the constructor. Is there maybe a better way to this? I get the feels that there is too much repeat code in the controller.
Yes this is a problem in Javascript, but thankfully in TypeScript this problem is no more thanks to fat arrows! Yay!
Fat arrows are like anonymous functions but handle the this variable for you.
Let me show you:
fooFunction(fooProp:string):void {
// Pretend i set it up properly for $mdDialog to work
this.$mdDialog.show(options).then((answer: boolean) => {
if (answer) {
this.foo = "";
}
});
}
Using fat arrows this will now compile to this in JS:
FooCtrl.prototype.fooFunction = function (fooProp) {
var _this = this;
// Pretend i set it up properly for $mdDialog to work
this.$mdDialog.show(options).then(function (answer) {
if (answer) {
_this.foo = "";
}
});
};
So Typescript automatically creates a _this variable for you, so that you no longer have the problem. Pretty neat if you ask me.
Here's the documentation for Arrow functions:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
The only reason to place the value in a local variable prior to calling a function is because you expect the class to go out of scope (i.e. something will happen to change the scope, such as a callback or asynchronous operation).
By putting the value in a local variable it becomes part of the closure for the function and the value is stored alongside the function itself.
This is one of the trickiest aspects of JavaScript - scope is kinda funky.
There are other options to solve this problem, including fat arrows...
() => {
// this.foo is now preserved
}
Or call / apply which allow you to set the scope.

Code Highlighting in PHPStorm/WebStorm for Angularjs

In my controller, I have a function defined as:
var ProductListingHeaderController = function (FilterService, CategoryService) {
this.isCategorySet = function () {
return (FilterService.categoryID);
};
this.categoryName = function () {
return (CategoryService.categoryName());
};
};
The IDE (via code highlighting) reports categoryName() as being used and isCategorySet() as unused.
This is kind of understandable, since:
categoryName() is used inside {{ }} in the html file:
<h2>{{productListingHeader.categoryName()}}</h2>
and isCategorySet() is used in an ng-if string:
ng-if="productListingHeader.isCategorySet()"
Given that this is such a common usage, I suspect I may be missing a setting in Storm as to how to set things up so that this type of usage (inside a string) by an Angular directive gets picked up as "used".
Thanks in advance for any feedback.
That's normal behavior for PHP/WebStorm.
Template -> JavaScript is really just an ambiguous connection. There is no support for jsDoc type inferring in the AngularJS templates. So PHP/WebStorm will match a template function call to JavaScript if only that function name is unique.
PHP/WebStorm have issues inferring closure methods as object functions. I've had better success using prototype declaration for AngularJS controllers.
var ProductListingHeaderController = function (FilterService, CategoryService) {
this.filterService = FilterService;
this.categoryService = CategoryService;
}
ProductListingHeaderController.prototype.isCategorySet = function () {
return (this.filterService.categoryID);
};
ProductListingHeaderController.prototype.categoryName = function () {
return (this.categoryService.categoryName());
};
Compare the above code with your code, and look at the structure explore in WebStorm. When you use prototypes for controllers they appear in the explorer properly.

Resources