How do I make services available outside of constructor in angular? - javascript

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
};

Related

Using dependency injection with typescript in AngularJs

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);
}
}

How to handle closures in TypeScript (Angular injections)?

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);

AngularJS Dependency Injection Fails in module

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";
...

AngularJS Service in Typescript

I have an AngularJS service written in JavaScript and i would like to convert this to TypeScript. The Javascript version returns a function immediately. Why would it do this and how would I implement this in TypeScript?
JavaScript
Geisha.service("loginModal", ["$uibModal", "$rootScope", function ($uibModal: angular.ui.bootstrap.IModalService, $rootScope: ICustomRootScopeService) {
function assignCurrentUser(user: User) {
$rootScope.currentUser = user;
return user;
}
return function () {
var instance = $uibModal.open({
templateUrl: "/Angular/Security/Views/loginTemplate.html",
controller: "loginModalCtrl"
});
return instance.result.then(assignCurrentUser);
}}]);
TypeScript
In TypeScript I have only gone as far as creating the constructor and one of the methods but not sure how to implement the immediate return function
module G.Services {
export class LoginModal {
$rootScope: ICustomRootScopeService;
$uibModal: angular.ui.bootstrap.IModalService;
static $inject = ["$rootScope", "$uibModal"];
constructor($rootScope: ICustomRootScopeService, $uibModal: angular.ui.bootstrap.IModalService) {
this.$rootScope = $rootScope;
this.$uibModal = $uibModal;
}
assignCurrentUser(user: User): User {
this.$rootScope.currentUser = user;
return user;
}
}
Geisha.service("loginModal", LoginModal);}
How to implement the immediate return function
In your case you have a service that is just a function. If you use a class it will be something that has functions. Better if you just use function as you had.
Note
If you are willing to do the refactoring have loginModal.assignCurrentUser(...stuff...) elsewhere instead of loginModal(...stuff...)

AngularJs + typescript directives dependency injection

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.

Categories