I'm trying to import $timeout with ES6 and webpack and I keep getting that $timeout is undefined.
Can anyone help?
If there's a way that doesn't involve using $inject I'd prefer it because I'm gradually trying to get rid of angularjs in my code.
randomTVNames.service.js:
import angular from 'angular';
class RandomTVNames {
constructor($timeout) {
this.tv = ['Shield', 'Walking Dead', 'Castle', 'Leftovers'];
this.timeout = $timeout;
console.log(this.timeout);
}
getName() {
const totalNames = this.tv.length;
const rand = Math.floor(Math.random() * totalNames);
return this.tv[rand];
}
getTimeout(){
this.timeout(function () {
alert("this is timeout")}, 3000);
}
}
RandomTVNames.$inject = ['$timeout'];
//todo - try to inject angular argument (such as $timeout) with $inject
var randomTVNames = new RandomTVNames();
export default randomTVNames;
home.controller.js:
import randomTVNames from '../../services/randomTVNames.service';
import mainModule from '../../mainModule';
class HomeController {
constructor() {
this.tv = randomTVNames;
this.name = 'World';
}
randomTVName($timeout) {
this.name = this.tv.getName();
}
getCtrlTimeout(){
this.tv.getTimeout();
}
}
mainModule.controller('HomeController', HomeController);
ES6 modules are not compatible with the module system from Angular 1.x. This means that exporting and importing services and controllers won't work, you need to register and inject them using Angular's module system.
randomTVNames.service.js:
import mainModule from '../../mainModule';
class RandomTVNames {
// ... snip ...
}
RandomTVNames.$inject = [ /* ... snip ... */ ];
mainModule.service('randomTVNames', RandomTVNames);
home.controller.js:
import mainModule from '../../mainModule';
class HomeController {
constructor($scope, randomTVNames) {
this.$scope = $scope;
this.tv = randomTVNames;
}
}
HomeController.$inject = ['$scope', 'randomTVNames'];
mainModule.controller('HomeController', HomeController);
Then in your main webpack file, make sure to import both of them so they get bundled:
import 'services/randomTVNames.service';
import 'controllers/controller.service';
There is no way to get rid of $inject, unless you are not minifying you code (but i hope you are).
Angular is injecting variables using their names, so when it sees $scope, it know to look for it and inject it, but when minifying your code the variable names are changed ($scope becomes c etc.) and angular does not know what object you want to inject.
That is what $inject is for since strings are not minified.
please take a look at this loader. it helps with what you're trying to do. but mind adding 'ngInject' anywhere in your js file, that injects anything
Related
I'm trying to figure out how to use TypeScript in an existing Angular 1.5 app. I can't figure out how to inject custom services. Angular services and 3rd party services work fine. The services I need to inject are still vanilla JS services. In the example below I need to inject reportService.
The error I get is getReportById is not a function.
class ReportController implements IReportController {
static $inject = ['$stateParams', 'reportService'];
constructor(private $stateParams: angular.ui.IStateParamsService, private reportService) {
this.getReport($stateParams.id);
}
getReport(id: number): object {
reportService.getReportById(id)
.then(function(res) {
console.log(res)
});
}
}
Here is the service.
angular.module('reportsServiceModule', [])
.factory('reportsService', [
'$http',
reportsService
]);
function reportsService(
$http
) {
'use strict';
var service = {};
service.getReportById = function(reportID) {
return 'A promise';
};
return service;
}
The only real difference between JS and TS development in AngularJS is that the units can be typed. This doesn't affect anything but type safety.
Here it would be
interface IReportService {
getReportById: (someType) => string;
}
...
constructor(
private $stateParams: angular.ui.IStateParamsService,
private reportService: IReportService
) {
...
There's no problem with dependency injection. If you have DI problem in Angular, you get an error from injector.
The real problem here is that strict mode isn't used. With use strict statement or alwaysStrict TS option the error would show what's really wrong - reportService variable is not defined in getReport method.
Instead, it should be
getReport(id: number): object {
this.reportService.getReportById(id)
...
It's a typo.
You defined a reportsService
.factory('reportsService')
But injected a different one
static $inject = ['$stateParams', 'reportService'];
I'm setting up a new MVC 5 project using Angular and TypeScript, and I am having issues instantiating a controller and a service. I get the following error when I include the ng-controller in my HTML:
angular.js:14110 Error: [ng:areq] Argument 'fn' is not a function, got undefined
Here's my setup:
app.ts:
module mqApp {
'use strict';
if (typeof (angular) != "undefined") {
var modules;
modules = [];
angular.module("mqApp", modules)
.controller("MasterController", MasterController)
.service("UserService", UserService);
}
}
userService.ts:
module mqApp {
'use strict';
export class UserService {
public static $inject = [
'$scope',
'$http',
'$window'
];
private scope: angular.IScope;
private httpSvc: angular.IHttpService;
private window: angular.IWindowService;
constructor($scope: angular.IScope, $http: angular.IHttpService, $window) {
this.scope = $scope;
this.httpSvc = $http;
this.window = $window;
alert(2);
}
logOff() {
this.httpSvc.get('/Account/LogOff');
this.window.location.href = '/';
}
}
}
masterController.ts:
module mqApp {
'use strict';
export class MasterController {
public static $inject = [
'$scope',
'UserService'
];
private userService: UserService;
private scope: angular.IScope;
contructor($scope: angular.IScope, userService: UserService) {
alert(1);
this.userService = userService;
this.scope = $scope;
}
}
}
Constructor is spelled wrong in MasterController.ts.
The problem might be that typescript translates your UserService to something similar to
var UserService = function($scope, /* ... */) {
this.$scope = $scope;
// ...
}
In JavaScript, one can use a variable that is defined with var before its definition, but the value will be undefined:
console.log(UserService); // => undefined
class UserService {}
console.log(UserService); // => function() { .... }
Thus, the order of imports matters: You have to make sure the code that defines your classes is executed before calling .service(...) or .controller(...) (it has to be positioned literally above the calls).
If you want to use classes and split them over files I recommend to use typescripts import mechanisms and a module loader system like amd. This ensures that the classes will be defined when used.
in folder directives i created two files: directives.js and color.js
directives i've imported into app.js
directives.js:
import angular from 'angular';
import ColorDirective from './color';
const moduleName = 'app.directives';
angular.module(moduleName, [])
.directive('color', ColorDirective);
export default moduleName;
color.js
import angular from 'angular';
let ColorDirective = function () {
return {
link: function (scope, element) {
console.log('ColorDirective');
}
}
}
export default ColorDirective;
and on one element in component i've added color as attr.
But it's not working. I mean inner link loop. Why? What i have wrong coded? How to use directives with angular 1.5 & es2016 ?
From what you have written its not possible to see the problem. The code you provide works, assuming that you have included your module into the page and the code is correctly compiled.
I have put your code into a fiddle, https://jsfiddle.net/fccmxchx/
let ColorDirective = function () {
return {
link: function (scope, element) {
console.log('ColorDirective');
element.text('ColorDirective');
}
}
}
angular.module('app.directives', [])
.directive('color', ColorDirective);
unfortunately I cannot split your code into modules, but that is what your code is trying to do
I'm not very familiar with es6 syntax but here is the typescript's way I'm using:
class ColorDirective implements angular.IDirective {
constructor() { }
link(scope, iElement, iAttrs, ngModelCtrl) {
}
/**
* Instance creation
*/
static getInstance(): angular.IDirectiveFactory {
// deendency injection for directive
// http://stackoverflow.com/questions/30878157/inject-dependency-to-the-angularjs-directive-using-typescript
let directive: angular.IDirectiveFactory = () => new ColorDirective();
directive.$inject = [];
return directive;
}
}
angular
.module('moduleName')
.directive('color', ColorDirective.getInstance());
EDIT: after some research, I've found the es6 way to do the same thing as above:
import angular from 'angular';
class ColorDirective {
constructor() {
}
link(scope, element) {
console.log('ColorDirective');
}
static getInstance() {
var directive = new ColorDirective();
}
}
export default ColorDirective.getInstance();
Im trying to create a custom service in angular 2 but i can't seem to find any documentation on angular 2 services in es5 (which is what im writing my code in) i've tried using this
(function(app){
app.database=ng.core.Injectable().Class({
constructor:[function(){
this.value="hello world";
}],
get:function(){return this.value},
});
ng.core.Injector.resolveAndCreate([app.database]);
ng.core.provide(app.database,{useClass:app.database});
})(window.app||(window.app={}));
however when i inject it into my component it throws the error no provider for class0 (class10 -> class0) i can't seem to figure out how to create the custom service. does anyone know how to do this in es5?
Here is a complete sample of dependency injection with ES5. (service into component, service into service). Don't forget to specify your service when bootstrapping your application or within the providers attribute of components.
var OtherService = function() {};
OtherService.prototype.test = function() {
return 'hello';
};
var Service = ng.core.Injectable().Class({
constructor: [ OtherService, function (service) {
this.service = service;
}],
test: function() {
return this.service.test();
}
});
var AppComponent = ng.core
.Component({
selector: 'my-app',
template: '<div>Test: {{message}}</div>',
})
.Class({
constructor: [Service, function (service) {
this.message = service.test();
}],
});
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(AppComponent, [
OtherService, Service
]);
});
In your case, I think that your forgot to add app.database in providers. Something like:
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(AppComponent, [
app.database
]);
});
You could also have a look at this question:
Dependency Injection in Angular 2 with ES5
I've the following AngularJS App written with TypeScript
The Main App where I initalize the App:
module MainApp {
export class App {
public static Module : ng.IModule = angular.module("mainApp", [])
}
}
And my controller
module MainApp {
export class Person {
public firstName: string;
public lastName: string;
}
export interface IMainAppCtrl {
}
export class MainAppCtrl implements IMainAppCtrl {
public person : Person;
constructor() {
this.person = new Person();
this.person.firstName = "Vorname";
this.person.lastName = "Nachname";
}
}
MainApp.App.Module.controller("mainAppCtrl", MainAppCtrl);
}
Thats working, but I am not very happy with this solution, because here I have to register the controller for my App in my controller itself
MainApp.App.Module.controller("mainAppCtrl", MainAppCtrl);
It would be nice If ther is a possiblity to register the controller in the "App" class directly like:
public static Module : ng.IModule = angular.module("mainApp", []).controller("mainAppCtrl", MainAppCtrl);
but thats not working here i get the error Message from the browser
"Argument 'mainAppCtrl' is not a function, got undefined"
in my old plain JS angular controllers I had an mainApp where I've registered all my Controllers like
angular.module("app.main", [
"ui.router",
"ngSanitize",
"DirectiveTestsCtrl",
....
]);
and in my controller i've only registerd the controller name for angular:
angular.module("DirectiveTestsCtrl", [])
.controller("DirectiveTestsCtrl", function () { ... });
is this also possible with the above shown snipes with typescript or what is best practise here - I've searched the web and found many examples but not a good one for controlerAs syntax and with "module Name { class xyz }" which was really working.
TLDR; - Export a static function on your main module that will handle all the bootstrapping/initialization logic. For child/sub modules export a static readonly property which will allow you to build once and retrieve multiple times a defined angular module.
Main Module
I have taken a similar approach, but it at least encapsulates the additional of components (controllers, services) inside the module definition
module appName {
export class App {
static createModule(angular: ng.IAngularStatic) {
angular.module('moduleName', ['ngRoute'])
.controller('refCtrl', ReferencedControllerClass)
.service('dataService', DataService);
}
}
}
appName.App.createModule(angular);
Child Module(s)
Then in other internal modules I create a static reference to the module to account for it being referenced more than once...
module appName {
export class SubModuleName {
private static _module:ng.IModule;
public static get module():ng.IModule {
if (this._module) {
return this._module;
}
this._module = angular.module('subModuleName', []);
this._module.controller('SomeCtrl', SomeController);
return this._module;
}
}
}
With this approach I can inject my SubModule into the main angular module via:
angular.module('moduleName', ['ngRoute', appName.SubModuleName.module.name])
Note: The main module could be defined as the child one's are for consistency, but I am only ever referencing/building it once, so I didn't bother caching it with a local static instance.