Access TypeScript functions from JavaScript - javascript

I want to use TypeScript with jsTree. How can I call the setCurrentNode function in the bound jsTree function?
class MyController {
thescope: any;
static $inject = ['$scope'];
constructor($scope) {
$scope.vm = this;
this.thescope = $scope;
(<any>$("#demo2")).jstree({
.bind("select_node.jstree", function (e, data) {
// how can I call setCurrentNode(data) here?
}
});
}
setCurrentNode(node: any): any {
... // do Stuff in this typescript function
}
}

Solution:
(<any>$("#demo2")).jstree({
.bind("select_node.jstree", this.setCurrentNode.bind(this) )
}
public setCurrentNode(e:any,data: any): any {
...
}

I'm not completely sure, so please correct me if I'm wrong, but wouldn't the use of the lamba expression solve this problem as well?
As follows:
class MyController {
thescope: any;
static $inject = ['$scope'];
constructor($scope) {
$scope.vm = this;
this.thescope = $scope;
(<any>$("#demo2")).jstree({
.bind("select_node.jstree", (e, data) => {
this.setCurrentNode(e, data);
}
});
}
setCurrentNode(e: any, node: any): any {
... // do Stuff in this typescript function
}
}
The lambda (=>) expression will make sure the function is executed in the same scope as the scope you're defining it in. If you'd look at the compiled JavaScript code you'll see he will keep a reference to the constructor scope and will call setCurrentNode on that scope. Simplified example:
var _this = this;
$("#demo2").jstree({
.bind("select_node.jstree", (e, data) => {
_this.setCurrentNode(e, data);
});
I believe this would solve your problem?
On a side note, you should look for a jsTree definition file or at least add a stub declaration yourself so you don't need to cast JQuery to any. Just my 2cts, it looks ugly to me.

As per Anzeo's suggestion to prevent the need for casting $ to any the following is all you need to get you started :
interface JQuery{
jstree:Function;
}

What it happens is that the inner jsTree callback is overwriting the this initial reference to the instance object.
There are two safe ways to solve this:
1- By the use of the arrow function as pointed by #Anzeo, which I don't recommend and I never use since if you need to have another nested callback then you can have a this reference to the innermost event object.
2- By caching the this reference to the instance object like:
class MyController {
thescope: any;
static $inject = ['$scope'];
constructor($scope) {
// Caching the reference for using it in the inner callbacks.
var self = this;
$scope.vm = this;
this.thescope = $scope;
(<any>$("#demo2")).jstree({
.bind("select_node.jstree", function (e, data) {
// Call to the instance method here!
self.setCurrentNode(/*Node params here*/);
}
});
}
setCurrentNode(node: any): any {
... // do Stuff in this typescript function
}
}
I recommend you to stick with 2 since it will work though any nesting level and you can have the this in each nested callback pointing to the right event object.
See Javascript 'this' overwriting in Z combinator and every other recursive function for more references since the problem is the same as here.

Related

Class that depends on its closure

Because of angular factories, I am thinking about using a class that uses some methods that are on its closure as if they were private methods.
This is not 100% classical inheritance nor revealing pattern, so I'm not sure if this is correct or not. Here is a small example:
myClass = (function() {
function privateMethod (stuff) { return something }
function TheClass () { this.someValue = 'Hello'}
TheClass.prototype.someMethod = function () { return privateMethod(this.someValue)}
return TheClass
}());
And then do something like
instance = new myClass()
I think this works fine for helper functions or private methods that do not change the values of the instantiated object. Using private "variables" may be useful for singelton classes or maybe fixed values.
Is there any problem with this implementation?
That's typically fine to do and its essential that most classes depend on their closure. Usually your class will be in its own file - its own module. If you are using a module loader (I hope you are), then closure is already implied and the methods are already "private" when defined like this:
// Begin MyClass.js
function privateMethod (stuff) { return something }
function TheClass () { this.someValue = 'Hello'}
TheClass.prototype.someMethod = function () { return privateMethod(this.someValue)}
// depending on which module loader you are using
return TheClass;
module.exports = TheClass;
export default TheClass;

Can I avoid using the word "this" inside Typescript when calling a function that came in through a constructor?

I have:
class AdminHomeController {
private config1; // I tried different variations here but none worked
public config2; //
constructor(
private $scope: IAdminHomeControllerScope
) {
this.config = $scope.config; // << this works
}
static $inject = [
'$scope'
];
configChanged = (clear) => {
this.config.clear();
};
}
This code works and this.config has all the methods I need. However is there a way
that I can remove the need for the this? What I would like to be able to do is to code the following:
configChanged = (clear) => {
config.clear();
};
I tried with many different variations but I cannot get it to work.
Here's an example of the same code before changing to Typescript:
angular.module('admin')
.controller('AdminHomeController', [
'$http',
'$q',
'$scope',
'utilityService',
adminHomeController]);
function adminHomeController(
$http,
$q,
$scope,
utilityService
) {
var app = $scope.app;
var config = app.config;
var home = this;
var util = utilityService;
home.autoSave = false;
home.data = {};
home.entityType = null;
home.forms = { grid: null, modal: null };
home.grid = { view: [], data: [] };
home.modal = { visible: false };
home.row = {};
home.rowSelected = null;
home.configChanged = function (clear) {
config.put();
if (clear) {
home.grid.backup = [];
home.grid.data = [];
}
};
As djechilin said:
If you omit "this," the interpreter will simply look for the variable name in local, closured and global scopes, not actually look up object properties
So you can potentially do that in TypeScript by moving the function definition into the body of the constructor (where you have access to the closured $scope variable). Here we also close over config and then use it in the function:
class AdminHomeController {
configChanged: ()=>void; // Need to declare the function
constructor(private $scope: any) {
var config = $scope.config;
this.configChanged = ()=> { // And then assign it
config.clear();
};
}
}
As you can see, it's not elegant. You have function definition + declaration split. Also, the constructor body is becoming needlessly heavy.
TypeScript is not to blame here. It's just JavaScript.
In Javascript definitely not, and in Typescript I'm pretty sure no. References in Javascript bind lexically, not whatever the other way is called. If you omit "this," the interpreter will simply look for the variable name in local, closured and global scopes, not actually look up object properties. You would need some sort of magical flag to say "this variable is an object property," and the way this is implemented is with "this."
This is very usual in Javascript. Compiled languages like Java don't suffer this problem, and I'm not sure about other dynamic languages. It's not necessarily hard to solve; it's just not how JS has done things (arguably wrongly).

Is it possible to let Angularjs work with prototype methods and variables

You know, in angularjs, most of logical are based on $scope:
function Ctrl($scope) {
$scope.name = "Freewind";
$scope.hello = function() {
alert($scope.name);
}
$scope.method1 = function() {}
$scope.method2 = function() {}
$scope.method3 = function() {}
$scope.method4 = function() {}
$scope.method5 = function() {}
}
Now I'm using haxe to generate angularjs code, it works if my code is:
class Ctrl {
public function new(scope:Scope) {
scope.name = "Freewind";
scope.hello = function() {
alert(scope.name);
}
scope.method1 = function() {}
scope.method2 = function() {}
scope.method3 = function() {}
scope.method4 = function() {}
scope.method5 = function() {}
}
}
typedef Scope = {
name:String,
hello:Void->Void,
method1: Void->Void,
method2: Void->Void,
method3: Void->Void,
method4: Void->Void,
method5: Void->Void
}
But I want to be benefited from haxe's class system(the code may be simpler and clearer), to declare it like:
class Scope {
public var name:String;
public function hello() {}
public function method1() {}
public function method2() {}
public function method3() {}
public function method4() {}
public function method5() {}
}
Then find a way to associate the Scope class with the $scope of angularjs.
But the generated Scope from haxe are using prototypes:
Scope = function();
Scope.prototype.name = "something";
Scope.prototype.hello = function() {}
Scope.prototype.method1 = function() {}
Scope.prototype.method2 = function() {}
Scope.prototype.method3 = function() {}
Scope.prototype.method4 = function() {}
Scope.prototype.method5 = function() {}
In this case, I can't find a solution to let angularjs work with it.
Is it possible to let angularjs to work with prototypes, so it can work with haxe class system (also other languages like coffeescript/typescript which have class support)?
For the sake of having an actual answer to this question, one should mention that now this library solves the problem: https://github.com/freewind/HaxeAngularSupport ;)
Scope's constructor is declared within a closure, so you don't have easy access to it... BUT(!) JavaScript has the constructor available to you right off of any existing object.
Theoretically, you could get the $rootScope's constructor, alter it's prototype, and that should alter any subsequently created Scopes. You'd probably want to do this in the .run() or .config() on your application module, however.
app.run(function($rootScope) {
$rootScope.constructor.prototype.foo = 'Set from prototype';
});
It works and here's the plunker.
HOWEVER: You probably don't need to do this. The only reason I could think of that you might need to do this is in some edge-case where you needed to make sure some function or property was available on a scope, even if it was an isolated scope (as was the one in the directive in the plunker I linked).
Because there is scope inheritance, and because you have $rootScope to use, I can't really think of any valid reason to need to alter Scope via prototyping.

Why can't I declare local variables and functions within a TypeScript class?

In TypeScript, I can't seem to declare a function in a class without the compiler adding it to the prototype. For example:
class MyTypeScriptClass {
// method, is added to prototype
foo1(): void {
alert('invoked foo1');
}
// private method also added to prototype
private foo2(): void {
alert('invoked foo2');
}
//// can I have a local function, without making it a private method?
//function foo3() {
// alert('invoked foo3');
//}
}
The above compiles to this:
var MyTypeScriptClass = (function () {
function MyTypeScriptClass() { }
MyTypeScriptClass.prototype.foo1 = function () {
alert('invoked foo1');
};
MyTypeScriptClass.prototype.foo2 = function () {
alert('invoked foo2');
};
return MyTypeScriptClass;
})();
What I am looking for is typescript that can compile to the following JavaScript:
var fvm = new FlasherViewModel2();
var MyTypeScriptClass = (function () {
function MyTypeScriptClass() { }
MyTypeScriptClass.prototype.foo1 = function () {
alert('invoked foo1');
};
MyTypeScriptClass.prototype.foo2 = function () {
alert('invoked foo2');
};
function foo3() {
alert('invoked foo3');
}
return MyTypeScriptClass;
})();
Can it be done?
(As a side note, I know that foo3 would not be callable from external code. I would actually invoke foo3 from another method within the class, for example, to pass a function to a jQuery fadeOut.)
As apsillers mentions, private static is probably what you want. While it's not supported in current builds, you will be able to have a private static member in TypeScript at some point in the future (the design team changed its mind on this one based on feedback similar to this).
I just discovered another way to have private methods in a typescript class, though the pattern may smell a little funny. As far as I can tell, you can only do this when you wrap the class in a module. For example:
module MyApp {
// not accessible externally, `this` must be passed in if needed
function foo3(that: MyTypeScriptClass): void {
that.foo1();
alert('invoked foo3');
}
// not accessible externally
function foo4(): void {
alert('invoked foo4');
}
export class MyTypeScriptClass {
// normal method, is added to prototype
foo1(): void {
alert('invoked foo1');
}
// private method also added to prototype, is accessible externally
private foo2(): void {
alert('invoked foo2');
foo3(this);
foo4();
}
}
}
The above compiles into:
var MyApp;
(function (MyApp) {
function foo3(that) {
that.foo1();
alert('invoked foo3');
}
function foo4() {
alert('invoked foo4');
}
var MyTypeScriptClass = (function () {
function MyTypeScriptClass() { }
MyTypeScriptClass.prototype.foo1 = function () {
alert('invoked foo1');
};
MyTypeScriptClass.prototype.foo2 = function () {
alert('invoked foo2');
foo3(this);
foo4();
};
return MyTypeScriptClass;
})();
MyApp.MyTypeScriptClass = MyTypeScriptClass;
})(MyApp || (MyApp = {}));
With the above, external javascript could invoke foo2() on an instance of MyTypeScriptClass, but neither foo3() nor foo4() is accessible externally at runtime. The biggest caveat is that if your private method needs access to members of the instance, you have to pass this in as a function parameter. Essentially, these are private static methods.
var instance = new MyApp.MyTypeScriptClass();
// public, accessible externally
instance.foo1();
// will not compile in a .ts file, but works at runtime from manual js file
instance.foo2();
// runtime exception, foo3 is not defined
foo3(instance);
MyApp.foo3(instance);
// runtime exception, foo4 is not defined
foo4();
MyApp.foo4();
This approach also sort of works with scalar variables, but the variables are also essentially static -- you can't have different values for different class instances. To do that, as far as I can tell, you still need to declare them within the class. Marking them private will keep the typescript compiler from allowing external calls to them, but other javascript code can still access them externally.

Creating new JS library - Help deciding the base structure

Im creating a new library for a company. The structure I follow is
(function() {
var lib = function() {
//some private and public fn definitions
//setting publically avbl functions
return {
func1 : func1, func2: func2
};
};
return (window.lib = lib);
})();
Now how I call this is
lib.func1();
I want to be able to call my library as
lib(function | string | object).someproperty
How do I convert my code. Tried something like this
function lib() {
return new arguments.callee(arguments);
}
lib.prototype={
publicfunc: function() {
}
}
In this i'm having some problems accessing private functions since it is out of the scope for the public functions defined in the lib's prototype.
var lib = (function (param) {
var func = function () {
/// your code
return {
animate : function () {
// do the animation
return this;
}
}
return func;
})();
this can be the basic fprmat.
ok here is how i can be used.
lib(function() {...});
or
lib(selectItem).animate();
because i returned this i can run another method if there is any.
lib(selectItem).animate().animate().animate().animate() ....;
i hope you can find a use of this format, of course i made it very basic
In this i'm having some problems accessing private functions since it is out of the scope for the public functions defined in the lib's prototype.
This is because there is no such thing as private there is only local. You cannot access local variables out of scope.
The prototype cannot talk to local variables in the constructor. There are various hacks around this like keeping a public hash of instances but then you lose the "privacy"

Categories

Resources