I trie to implement a new directive in my app. Directive's code :
module Myapp.NV.Directives {
export interface placeHolderScope extends ng.IScope {
txt: string;
}
/**
* PlaceHolder
*
* #class
* #classdesc This directive is use to simulate placeholder HTML5 attributes
*/
export class PlaceHolder implements IDirective {
static $inject = ['$log','$timeout'];
constructor($log: ng.ILogService, $timeout: ng.ITimeoutService) {
var txt;
var directive: any = {
restrict: "A",
scope: { txt: "#ngPlaceholder" },
link: function (scope: placeHolderScope, elem: ng.IAugmentedJQuery, attrs: ng.IAttributes, $log: ng.ILogService, $timeout: ng.ITimeoutService) {
console.log($log);
console.log($timeout);
}
}
return directive;
}
}
}
Myapp.NV.registerDirective('PlaceHolder', ['$log', '$timeout']);
My probleme is log and timeout are always undefined...
static $inject = ['$log','$timeout'];
Won't work...
The code for registerDirective function :
export function registerDirective(className: string, services = []) {
var directive = className[0].toLowerCase() + className.slice(1);
services.push(() => new Myapp.NV.Directives[className]());
angular.module('Myapp.NV.Directives').directive(directive, services);
}
Thanks for help me :)
As boindill points out in the original answer, dependencies are injected through the TypeScript constructor, not through the link function.
This is my solution, myDirective depends on myService:
export class MyDirective implements ng.IDirective {
static $inject = ["myService"];
constructor(private myService: IMyService) {
}
restrict = "A";
scope = {};
link = (scope: IMyScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes) => {
// directive implementation
// Access myService through 'this.myService'.
};
}
Register the directive in Angular the following way. Here ng.IDirectiveFactory is implemented as an anonymous function:
angular.module("app", []).directive("myDirective", (myService: IMyService) => {
return new MyDirective(myService);
});
Here the Angular dependency injection works because it recognizes the constructor parameter names (myService).
To make sure dependency injection still recognizes the dependencies when the generated JavaScript is minified the static $inject property provides their names in a string array. Just as with plain JavaScript, make sure the constructor parameters and the $inject array members are in the same order.
You cannot inject dependencies in the link function of a directive. The link function has a fixed signature function link(scope, element, attrs) { ... } this is taken from the official angularjs documentation.
What you is to define a controller for your directive and inject the dependencies into the controller. Afterwards just use this controller function.
Related
I'm converting a component (AngularJS 1.6) from javascript to typescript.
class NewProjectCtrl {
price: number;
static $inject = ['$http'];
constructor($http) {
let ctrl = this;
ctrl.price = '50';
...
}
createProject() {
$http.post('/api/project', ctrl.project)
...
}
}
angular
.module('app.projects-selection')
.component('newProject', {
templateUrl: 'app/projects-selection/components/new-project/new-project.tmpl.html',
controller: NewProjectCtrl,
bindings: {
user: '<',
}
});
Typescript complains on the $http in my createProject method (Cannot find name $http). All the examples I can find only uses dependency injections in the constructor.
You've forgot this. After injecting use this.$http
Also let me suggest you some approach according to styleguides:
namespace myNameSpace {
class NewProjectCtrl {
private static $inject = ['$http'];
public price: number;
constructor(private $http) {}
createProject() {
this.$http.post('/api/project', ctrl.project)
}
}
class NewProjectComponent implements ng.IComponentOptions {
constructor() {
this.bindings: {
user: '<',
};
this.templateUrl: 'app/projects-selection/components/new-project/new-project.tmpl.html';
this.controller: NewProjectCtrl;
}
}
angular
.module('app.projects-selection', [])
.component('newProject', new NewProjectCtrl());
}
You're creating a class, not a mere function. $http is injected into your constructor function and exists there, but it does not exist in createProject. You need to store it on the instance as property:
private $http; // should be declared first too, preferably with type hint
constructor($http) {
this.$http = $http;
}
createProject() {
this.$http.post('/api/project', ctrl.project)
}
TypeScript has a shorthand for that:
constructor(private $http) {
}
createProject() {
this.$http.post('/api/project', ctrl.project)
}
let ctrl = this also seems rather weird, I don't think that makes a lot of sense. I don't know what else you're doing in the constructor, but I'd refactor what you have to this:
class NewProjectCtrl {
price: number = 50;
static $inject = ['$http'];
constructor(private $http) { }
createProject() {
this.$http.post('/api/project', ctrl.project);
}
}
I have the following problem. When I have a class in ionic with angular I can access angular services in the constructor after injecting them:
export class HomeController {
static $inject = [
'$scope',
'$state'
];
constructor(
public $scope : any,
public $state : any
) {
this.$state.go('test');
this.$scope.changeController = this.changeController;
}
changeController() {
console.log("change controller");
};
}
However when I change it to the change controller function, it doesn't work
export class HomeController {
static $inject = [
'$scope',
'$state'
];
constructor(
public $scope : any,
public $state : any
) {
this.$scope.changeController = this.changeController;
}
changeController() {
console.log("change controller");
this.$state.go('test'); // Fails here
};
}
This is the error:
Error: undefined is not an object (evaluating 'this.$state.go')
What can I do?
On top of that, is it correct add the changeController function to the scope or is there an easier method to make it available to a template?
Thx in advance
In your case the value of this is incorrect which is a very common issue due to misunderstanding of ES6 classes. Try using lambda to have lexical scoping:
changeController = () => {
console.log("change controller");
this.$state.go('test'); // Fails here
};
I'm trying to add an Angular function parameter to a directive written in TypeScript but I must not have the syntax right. Here is an example of my directive:
export class NewUserDirective implements ng.IDirective {
public restrict: string = "E";
public replace: boolean = true;
public templateUrl: string = '/views/_dialogs/newUserDialog.html';
public controller = Directives.NewUserController;
public controllerAs: string = 'Ctrl';
public bindToController: boolean = true;
public scope: any = {
showDialog: '=',
saveInProgress: '=',
onSuccessCallback: '&',
onClosingCallback: '&'
};
}
and here is a snippet of my controller:
export class NewUserController {
static $inject = ["$scope", "$q", "UserServices"];
public showDialog: boolean;
public saveInProgress: boolean;
public onSuccessCallback: () => void;
public onClosingCallback: () => void;
....
public save(): void {
//Flag as saving
this.saveInProgress = true;
//Save the plan
this.UserServices.createUser(this.newUser).then(x => {
//When finished, hide the modal
this.showDialog = false;
//Let parent know new user is created
this.onSuccessCallback();
});
}
}
Everything is working except for the function parameter in the parent controller. I've tried a few different syntaxes in the directive implementation like this:
<new-user-directive on-success-callback="loadData()" ...
and this:
<new-user-directive on-success-callback="loadData" ...
Are you using controllerAs for the outer controller that has the loadData method on it? If so, then you would need to create your directive like this (assuming you named our outer controller 'Ctrl'). Notice the Ctrl. in front of loadData()
<new-user-directive on-success-callback="Ctrl.loadData()" ...
You have controllerAs = 'Ctrl' so on-success-callback="Ctrl.loadData()"
The methods are passed through the $scope and not on the controller.
You should create a constructor:
constructor(private $scope, ....){
}
And call the method from the $scope like so:
this.$scope.onSuccessCallback();
i have a problem regarding the angular dependency injection mechanism.
I have written a directive in a standalone module, that does some neat things on a datatable. I'm using TypeScript and my directive looks as follows:
export class MdListControls implements angular.IDirective {
public templateUrl = "/app/templates/mdListControls.html";
public static instance(): any {
return new MdListControls();
};
public restrict = 'E';
public transclude = true;
public replace = true;
public scope = {
grid: '=',
};
public controller = "mdListControlsController"
public controllerAs = "vm";
public link = function (scope: angular.IScope, element: angular.IAugmentedJQuery, attrs: angular.IAttributes) {
};
}
My Controller looks something like this:
export class MdListControlsController {
static $inject = ['scope']
constructor($scope: angular.IScope){
//Setup the constructor
}
//Here goes the controller Logic
}
I'm registering the directive and the controller at a new module as follows:
angular.module("base.directives.mdListControls", [])
.controller("mdListControlsController", MdListControlsController)
.directive("mdListControls", function () { return MdListControls.instance() });
However, once I run the app, I get the following error once the directive shall be loaded:
Argument 'mdListControlsController;' is not a function, got undefined
This doesn't quite look like a problem with dependency injection at first. But when I register the controller on the application itself (which gets injected the "base.directives.mdListControls" Module) like so
app.controller("mdListControlsController", MdListControlsController);
everything works fine and the controller gets injected properly into the directive.
Does anyone know what I'm doing wrong here?
Thanks for your help!
In Your MdListControls class try to provide controller class, not controller name.
...
public scope = {
grid: '=',
};
public controller = MdListControlsController
public controllerAs = "vm";
...
I am trying to add put my whole class containing the controller inside my directive, put for some obvious reasons scope and syntax is incorrect.
I am using typescript as language and grunt-ts for the automatic generation and compiling.
/// <reference path="../reference.ts" />
directives.directive('myDirective', function ():ng.IDirective {
return {
restrict: 'EAC',
template: directiveHTML.html, \\ thanks to grunt-ts this work fine
controller: MyControllerClass, \\ here I get the error and here I would like to
put my own controller class instead of a function
link: function (scope, elements, attrs) {
}
}
});
and here the class of my controller
module Controllers {
export class CursorController {
constructor($scope, socket){
}
}
}
Where all the controller are then added to the controllers module of angularJS (references are generated automatically by grunt-td).
/// <reference path="../reference.ts" />
angular.module('controllers',[]).controller(Controllers);
Any clue or suggestion on how to solve this problem would be great.
You should be able to do :
directives.directive('myDirective', function ():ng.IDirective {
return {
restrict: 'EAC',
template: directiveHTML.html, \\ thanks to grunt-ts this work fine
controller: Controllers.CursorController, \\ Lookup controller by name
link: function (scope, elements, attrs) {
}
}
});
I'd suggest something like this:
export class MyDirective implements ng.IDirective {
public injection(): Array<any> {
return [
() => { return new MyDirective() }
]
}
public replace: boolean = true;
public controller = () => {
console.log('trying');
}
}
And here:
angular.module('myApp', []).directive('myDirective', MyDirective.prototype.injection())