I'm attempting to access a custom service from a custom event yet wheneven the event is fired the service reference is null
#Component({
selector: "my-component",
template: mySource,
legacy: { transclude: true }
})
export class myComponent {
constructor(
#Inject("$scope") private $scope: ng.IScope,
private myService: MyService) {
$scope.$on('$routeChangeSuccess', function (event) {
this.myService.myFunction();
});
}
});
}
when myService is referenced the following error is shown:
Cannot read property 'myService' of null
The solution is not to refer to myService from within myComponent prepended with
this.myService
but instead use
myService.myFunction()
Related
Currently I have this .directive in AngularJS:
aegroApp.directive('poorLegacy', function () {
return {
restrict: 'E',
scope: {
hasSomething: '=?',
anotherBinding: '#',
},
controller,
templateUrl: function (_, attrs) {
return attrs.hasSomething ? 'file.html' : 'file2.html';
},
}
});
function controller($attrs, /* others */) {
// perform some checks with $attrs
// rest of the code
}
and I'm trying to use UpgradeComponent to be able to use this in Angular:
#Directive({ selector: 'my-bridge' })
export class MyBridgeDirective extends UpgradeComponent {
#Input() hasSomething?: boolean;
#Input() anotherBinding?: string;
constructor(elementRef: ElementRef, injector: Injector) {
super('poorLegacy', elementRef, injector);
}
}
<my-bridge [hasSomething]="false"></my-bridge>
But it fails... and the reason is explained here (basically it doesn't support $attrs).
So, the question is: how could I use that legacy directive without rewriting this right now?
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 have an Angular factory service in JavaScript that I defined like this:
app.service('MyServiceFactory', ['$http', '$timeout', '$interval',
function($http, $timeout, $interval) {
function MyService() {
// I can use all injected values here without additional efforts
}
this.Create = function() {
return new MyService();
}
}]);
Now I want to convert it into TypeScript:
module Services {
export class MyServiceFactory {
static $inject: string[] = ['$timeout', '$interval', '$http'];
constructor(
private timeout: angular.ITimeoutService,
private interval: angular.IIntervalService,
private http: angular.IHttpService) {
}
public create(): MyService { return new MyService(); };
}
export class MyService() {
// I have a problem here. I need to redefine and
// initialize all variables, injected into my factory class
}
angular.module('MyModule').service('MyServiceFactory', MyServiceFactory);
}
Do you see what I mean? TypeScript does not allow nested classes, which could have solved the issue. Also TypeScript solution looks very uncool. Is there a more elegant solution?
Instead of :
export class MyService() {
// I have a problem here. I need to redefine and
// initialize all variables, injected into my factory class
}
You can put the Create on MyServiceFactory i.e.:
module Services {
export class MyServiceFactory {
static $inject: string[] = ['$timeout', '$interval', '$http'];
constructor(
private timeout: angular.ITimeoutService,
private interval: angular.IIntervalService,
private http: angular.IHttpService) {
}
public create(){
// Use the revealing module pattern
// And let the compiler infer the return type
// e.g.
var foo = 23;
return {
foo
}
};
}
angular.module('MyModule').service('MyServiceFactory', MyServiceFactory);
}
Please note that valid JavaScript is valid TypeScript (more)
You can pass the injected variables as parameters to your other class, e.g:
export class MyServiceFactory {
static $inject: string[] = ['$timeout', '$interval', '$http'];
constructor(
private timeout: angular.ITimeoutService,
private interval: angular.IIntervalService,
private http: angular.IHttpService) {
}
public create(): MyService {
return new MyService(this.timeout, this.interval, this.http);
}
}
export class MyService {
constructor(
private timeout: angular.ITimeoutService,
private interval: angular.IIntervalService,
private http: angular.IHttpService) {
// no more problems here, you can play with the injected variables again
}
}
angular.module('MyModule').service('MyServiceFactory', MyServiceFactory);
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 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.