TypeScript, Angularjs injecting service - angularjs

Trying to inject angularjs' $filter service into the directive, with typescript :
full code
export class MyDirective implements ng.IDirective {
public static $inject: string[] = ["$filter"];
constructor (public $filter: ng.IFilterService) {
var result = this.$filter('orderBy')([{"price":1}], ["price"])
return this;
}
}
Got error:
angular.js:11655 TypeError: this.$filter is not a function
why $filter is undefined?

Try :
var result =$filter('orderBy')([{"price":1}], ["price"])
$filter is a parameter of your constructor... it is not yet a member of your class. If I replace 'this.$filter' by '$filter' it works.
If you want to use this.$filter you will need to add it to your class and same the argument to the member '$filter':
public static $inject: string[] = ["$filter"];
$filter: ng.IFilterService;
constructor (public $filterParameter: ng.IFilterService) {
this.$filter = $filterParameter;
var result = this.$filter('orderBy')([{"price":1}], ["price"])
return this;
}
I change the parameter in the constructor to try to clarify to $filterParameter.
I proposed the change on your full code on github. Let us know if that helps.

You can change the constructor signature to
constructor (private $filter: ng.IFilterService) {
which would automatically make the filter a class property and this. reference should work from that point on.

Ok. the fix I've just done by following these recomondations: https://github.com/dilumich/style_typescript_angularjs
removed the line:public static $inject: string[] = ["$filter"];
added the factory to the directive:
static factory(): ng.IDirectiveFactory {
var directive = (filterService: ng.IFilterService) => new OfferingsTableDirective(filterService); directive.$inject = ['$filter'];
return directive;
}
then registered it in my sample app like:
angular.module('app.directives').directive('offeringsTableDirective', OfferingsTableDirective.factory());
Then $filter became functional - not undefined.

Related

Angularjs controller as via typescript invoke controller function from a directive

I'm trying to invoke a controller function from a directive and I run into the "this" context problem. The logSerivce is not accessible if the function gets called via directive.
Here the controller class:
class MainController implements IMainScope {
static $inject = ["LogService"];
constructor(private logService:ILogService) { }
sayHello(message:string) {
console.log("MainController sayHello", message);
// Cannot read property 'logService' of undefined if gets call from directive
this.logService.log(message);
}
}
Here the directive class:
class TestDirective implements ng.IDirective {
public restrict = "E";
public templateUrl = "test-directive.html";
public replace = true;
public scope:any = {
testFn: "&"
};
constructor() { }
public link:ng.IDirectiveLinkFn = (scope:TestDirectiveScope, element:ng.IAugmentedJQuery, attrs:ng.IAttributes):void => {
scope.hello = () => {
console.log("TestDirective", scope.firstName);
scope.testFn()(scope.firstName);
};
}
static factory():ng.IDirectiveFactory {
let directive:ng.IDirectiveFactory = () => new TestDirective();
return directive;
}
}
Here is a simple plunker example which covers my problem: http://embed.plnkr.co/Ov7crFZkkjDPzilX2BmL/
The way you are calling testFn from directive isn't correct way. To pass data with calling function you have to first use
ng-click="vm.sayHello(message)"
And while calling function from directive pass it in json/object format like {message: 'some value'} in parenthesis.
scope.testFn({message: scope.firstName});
Demo Plunkr
Methods that are supposed to be passed as callbacks (Angular binding fall into this category) should be bound to their context. For TypeScript the preferable way to do this defining a method as arrow function :
sayHello = (message:string) => { ... }

Angularjs Typescript define directive with constructor parameters throws [ng:areq] Argument 'fn' is not a function, got undefined

I am trying to use typescript to create an AngularJS 1.5 app and struggling to get the directive working which has an constructor parameter.
my Directive is something like this:
export class chModalDialog implements ng.IDirective {
public restrict = 'A';
//static $inject = ["$rootScope", "modalService"]
constructor(
private $rootScope: ng.IRootScopeService,
private modalService: CH.Interfaces.IModalsService) {
}
public link(scope: any, element: ng.IAugmentedJQuery, attrs: any) {
var self = this;
scope.subview = null;
element.on("click", function handleClickEvent(event) {
if (element[0] !== event.target) {
return;
}
scope.$apply(self.modalService.reject);
}
);
}
}
I have a factory function defined like this:
function chModalDialogFactory($rootScope: ng.IRootScopeService, modalService: CH.Interfaces.IModalsService): ng.IDirective {
return new CH.Directives.chModalDialog($rootScope, modalService);
}
chModalDialogFactory.$inject = ["$rootScope", "modalService"] //including $inject annotations
finally registering the directive as below:
angular.module('ch.controllers')
.directive('chModalDialog', chModalDialogFactory)
now when I try to make use of this directive I get the following error from Angular, can some please help me understand what I missed here?
Error: [ng:areq] Argument 'fn' is not a function, got undefined
http://errors.angularjs.org/1.5.8/ng/areq?p0=fn&p1=not%20aNaNunction%2C%20got%20undefined
at http://localhost:9000/Scripts/angular.js:68:12

Static function "does not exist" on injected class

I set up a class with a bunch of static utility functions.
// utils.ts
class Utils {
public static blank(anything) {
if (_.isNil(anything) || anything === "") {
return true;
}
return false;
};
// ...
}
window.Utils = Utils;
I inject this into my angular app as a constant:
angular.module("myApp", []).constant("Utils", window.Utils);
Then I can use it in my controllers or in a provider:
class MyCtrl {
constructor(private $scope, private Utils: Utils, private MyService: MyService) { }
// ...
}
angular.module("myApp").controller("MyCtrl", MyCtrl);
However, when I compile I get this error:
javascripts/admin/controllers/my_ctrl.ts(6,29): error TS2339:
Property 'blank' does not exist on type 'Utils'.
How can I get the correct type information on static methods when injecting a constant like this into an angular controller?
The correct type of injected private Utils is not Utils, which would imply an instantiated instance of the class Utils.
The correct type is typeof Utils, which tells TypeScript that private Utils is the class itself (or rather, something with the same type as the class Utils), not an instance of the class.
You will also need to use another name for the type Utils or the parameter Utils. The unary expression passed for TypeScripts typeof will include the parameters in its scope, so simply changing :Utils to :typeof Utils will cause error about circular reference.
Something like this should do the trick
constructor(private $scope, private Utils: typeof window.Utils, private MyService: MyService){ }

using $inject syntax with additional constructor arguments

Looking at the answer here: https://stackoverflow.com/a/19272093/2547709
Using the $inject syntax my controller ends up looking like this:
class MyCtrl {
public static $inject: string[] = ['$scope'];
constructor($scope){
// stuff
}
}
// register the controller
app.controller("MyCtrl", MyCtrl);
My question is- what happens if I want to pass my own custom arguments to the constructor as well as any injected variables?:
class MyCtrl {
public static $inject: string[] = ['$scope'];
constructor($scope, customArg){
// stuff
}
}
// Now how do I pass customArg in without it complaining?
app.controller("MyCtrl", MyCtrl(customArg)); // Nope
I feel like I'm missing something fundamental, using this syntax, does everything you pass in to the .controller() function have to be registered with angular and so I shouldn't be trying to pass in custom arguments at all? Or can I pass in an arbitrary value/object? And if so how?
customArg
You cannot pass in custom argument if angular is going to call the constructor. You can however register other things with Angular e.g. Services,Factories,Values(constants) that angular will pass to the controller for you.
More : https://www.youtube.com/watch?v=Yis8m3BdnEM&hd=1
Sorry for the answer I don't have enough points to comment.
I have the exact same scenario and here is my situation:
export abstract class DataService<T> {
static $inject = ['$resource'];
private svc: ng.resource.IResourceClass<ng.resource.IResource<T>>;
constructor(
protected $resource: ng.resource.IResourceService, url: string
) {
this.svc = <ng.resource.IResourceClass<ng.resource.IResource<T>>>this.$resource(url, { id: '#id' });
}
public get(id: number): ng.IPromise<T> {
return this.svc.get({ id: id }).$promise;
}
}
export class MyDataService
extends DataService<IItem> {
// big problem here!!!
constructor(
) {
super("/api/items/:id");
}
}
Looks like I would have to repeat the injection on every derived class and also pass in the super... so redundant

Best practice for dependency injection in an AngularJS service with TypeScript

I find dependency injection for AngularJS services in TypeScript to be somewhat cumbersome. Currently, I define a factory method inside my service class, and have to repeat all dependency injection arguments three times:
class MyService {
public static Factory($rootScope, myController) { // 1st time
return new MyService($rootScope, myController); // 2nd time
}
constructor(public $rootScope, public myController) {} // 3rd time
}
myModule.factory('myService', MyService.Factory);
I would like to do the following, but that does not seem to work:
class MyService {
constructor(public $rootScope, public myController) {} // only once
}
myModule.factory('myService', MyService);
This approach works fine for controllers, but not so for services. Is there a better way?
Thanks in advance!
You should user service not factory :
class MyService {
constructor(public $rootScope) {} // only once
}
myModule.service('myService', MyService);
You could simply use angular's injector to create your controller instead of having a factory method.
Here is a sample in typescript
/// <reference path='d.ts/DefinitelyTyped/angularjs/angular.d.ts' />
class MyCtrl {
public phrase: string;
constructor($window) {
this.phrase = 'I was loaded by injector';
}
speak() {
alert(this.phrase);
}
}
function main() {
var injector = angular.injector(['ng']);
var ctrl = injector.instantiate(MyCtrl);
ctrl.speak();
}
And a fiddler to prove it works: http://jsfiddle.net/UPv5j/3/

Resources