vsternbach / angular-ts-decorators Goto Github PK
View Code? Open in Web Editor NEWA collection of angular decorators for angularjs 1.5.x projects written in typescript
License: MIT License
A collection of angular decorators for angularjs 1.5.x projects written in typescript
License: MIT License
Hi
I am trying to use @component decorators but it's not working for me. My application is written in AngularJs1.6. recently, I tried implement typescript and angular-ts-decorators for one of existing component. As mentioned in readme,
I created employeeStatusComponent.ts as
import { Component, Input, Output } from 'angular-ts-decorators';
@Component({
selector: 'employee-status',
templateUrl: '/partials/employee/employee-status.html'
})
export default class EmployeeStatusComponent{<<logic here>>}
created a employeeStatusModule.ts as
import { NgModule } from 'angular-ts-decorators';
import EmployeeStatusComponent from './employeeComponent';
@NgModule({
declarations: [EmployeeStatusComponent]
})
export default class EmployeeStatusModule{}
I injected the above module to another module where employeeStatusComponent was used as component previously in angularJS1.6 javascript style. ( it was used like EmployeePayrollComponent)
import EmployeeStatusModule from './employeeStatusModule';
import EmployeePayrollComponent from './employeePayrollComponent';
import EmployeeController from './employeeController';
export default angular.module( 'Portal.Employee', [
EmployeeStatusModule
])
.config(StateConfig)
.controller(EmployeeController.name, EmployeeController)
.component(EmployeePayrollComponent.name, EmployeePayrollComponent)
and lastly, I used <employee-status></employee-status>
(component selector) in my employee.html page. when I am running the application the employee-status component not showing any output. looks like it's not even getting executed.
I also did npm install --save-dev @types/angular
and my webpack build is running fine ( no errors)
Could you pls check and let me know if I am missing anything?
Thanks a lot in advance.
Hi, I'm trying to use angular-ts-decorators in my project:
https://github.com/ShinDarth/Othello
I've managed to convert one of my components using the @Component
decorator. It compiles without errors but when I run the app in the browser, the component is not there and I get no errors in console.
This is the file where I'm including my components and services: https://github.com/ShinDarth/Othello/blob/master/app/index.ts
can you please give an example of how to register components/services using the decorators ?
Hi
I am trying to convert an existing directive using @directive decorator. This existing directive's controller method has $scope injected. When I am trying to inject $scope in decorator class's constructor I am getting provider error.. Any idea how to access $scope in @directive class..
Upgrading to TypeScript 2.4 yields a lot of errors from angular-ts-decorators due to its breaking change of adding greater weak type checking against interfaces. Nice minor version add, right?
Since the decorators reference interfaces with all optional members, it will fail if none are provided. The workaround is to provide a dummy implementation in all of these places. Better solution would be to fix it here, if possible. Perhaps with more piecemeal interfaces such as in Angular. Thoughts?
I was using 3.1.0
before with webpack, everything worked fine. Now I've got the following error with 3.2.0
and above:
ERROR in [at-loader] ./node_modules/angular-ts-decorators/types/utils.d.ts:3:58
TS2304: Cannot find name 'Declaration'.
webpack: Failed to compile.
webpack: Compiling...
Looking at ./node_modules/angular-ts-decorators/types/utils.d.ts
:
import 'reflect-metadata';
export declare function getTypeName(target: any): string;
export declare function getTypeDeclaration(target: any): Declaration;
Hi,
there is a small error in the examples. They should use the "template" property of the component options instead of the incorrect "tempalteUrl" property.
Wrong:
...
const templateUrl = require('./todo-form.html');
@Component({
selector: 'todoForm',
templateUrl
})
...
Right:
...
const template = require('./todo-form.html');
@Component({
selector: 'todoForm',
template
})
...
Hello,
I'm coming from Angular5 and absolutely love your work here. One of our clients is required to use AngularJS so this is a real lifesaver together with typescript. I have been trying to solve an issue where I dynamically add an <a>
link using a directive to toggle the contents of said element. I wasn't able to do this DOM manipulation inside the standard directive controller without causing nested components to duplicate. I tried instead to use the legacy compile function which solved the duplication issue. However there seems to be an issue that I cannot solve where each directive's context seems linked. Every toggle causes the last to change. Is this due to this
losing context?
import { element as ngElement } from 'angular';
import { Directive, Input } from 'angular-ts-decorators';
@Directive({
selector: 'dExpandable',
})
export class ExpandableDirective {
private element: ng.IAugmentedJQuery;
private toggler: ng.IAugmentedJQuery;
private expanded = false;
constructor(
// Cannot inject when using legacy compile function
) {
this.onToggle = this.onToggle.bind(this);
}
compile(element: ng.IAugmentedJQuery, attrs: ng.IAttributes) {
this.element = element;
this.element.attr('aria-expanded', this.expanded.toString());
this.toggler = ngElement('<a class="d-expandable--toggler"></a>');
this.toggler.on('click', this.onToggle);
this.element.prepend(this.toggler);
}
onToggle() {
this.expanded = !this.expanded;
this.element.attr('aria-expanded', this.expanded.toString());
}
}
This might be unrelated to your decorators, but any pointers would be greatly appreciated!
Kind Regards
Erik Hughes
Is it possible to use partial decoration. So as an example we would use the new decorators for our service, then the components, then the modules. However when I tried this it wanted everything to be decorated together. Look forward to your thoughts.
After updating from v0.27 to 1.0.13 I've noticed that PipeTransform
is no longer importable directly from 'angular-ts-decorators'. I.e. this code does not work:
import { Pipe, PipeTransform } from 'angular-ts-decorators';
...
export default class FooPipe implements PipeTransform {
A workaround would be:
import { Pipe } from 'angular-ts-decorators';
import { PipeTransform } from 'angular-ts-decorators/types/pipe';
export default class FooPipe implements PipeTransform {
Is it intentional?
Thank you!
Would it be possible to use @NgModule
with one or several directives which are downgraded components using { downgradeComponent } from '@angular/upgrade/static';
?
I tried
import { downgradeComponent } from '@angular/upgrade/static';
import { Directive, NgModule } from 'angular-ts-decorators';
import { CoolComponent } from '../app/cool.component';
@Directive(downgradeComponent({ component: CoolComponent })
class Downgraded { }
@NgModule({
id: 'AppModule',
declarations: [
Downgraded,
],
})
export class AppModule { }
Otherwise, would it be sensible to have another function to get the correct meta data from a downgraded component?
Currently, the @Injectable has an optional "name" parameter, which at the end will be the name to be used while registering the service in Angular. However this name is only used in case you register the provider using the class:
@NgModule({
...
providers: [
MyServiceClass
]
})
In that specific case, of course the Injectable "name" parameter is needed. In case you use the provider object syntax, it is not needed:
@NgModule({
...
providers: [
{ provide: "myServiceName", useClass/Factory/Value: MyServiceClass }
]
})
But if you don't provide the "name" param in the @Injectable you will still get the warning because the library assumes you "might" not be registering the service correctly.
I think that instead of logging a warning (which might pack your console with them if you have lots of services) the metadata for the service class should only be set if the name parameter is passed; then the registerProviders() function should log an error (or even throw) in case the provider is registered with the class and there is no name defined in the metadata.
In this way the @Injectable decorator will align to the Angular2+ decorator and you will know when your are "certainly" doing things wrong when you get the error in the console.
What do you think? Is it reasonable to do this?
Hi
I have directive used in html like attribute along with ng-model..
<div test-directive
ng-model="selectedOptions" name="testModel">
</div>
I am trying convert the directive using @directive decorator like below
@Directive({
selector: '[test-directive]',
transclude: true,
require: {
ngModel: '?ngModel'
},
templateUrl: 'template\test.html'
})
export class TestDirective implements AfterViewInit {
private ngModel: INgModelController;
constructor() {
}
public ngAfterViewInit(): void {
//use ngModel here..
this.ngModel.$render
}
}
here, ngModel is not getting mapped to ngModelController.. I tried changing the directive to Component ( changed the html too..) real quick and found ngModel is getting mapped correctly but I need it work with @directive.. any help would be highly appreciated..
I was using 3.6.1
in my component with ViewChild()
and it was working fine. I just tried to update to 3.7.3
but now the references to the views point to the DOM elements themselves, i.e
export class ParentComponent {
@ViewChild('#viewId')
child: ChildComponent;
}
parentComponent.child
does not point to the controller anymore but the DOM element itself.
I guess this is due to v3.7.1...v3.7.2#diff-7db1d7c8dc88ce00d1aa236fa0c128cc, so it means support of children by selector is no longer supported? I havent seen it not being supported by angular
Hi! Angular-ts-decorators was working pretty well for me, but I just tried finally compiling with webpack's -p (minify) option, and now I'm getting an error:
Uncaught Error: [$injector:modulerr] Failed to instantiate module AppModule due to: Error: [$injector:unpr] Unknown provider: e
I had a .config and a .run method on my module, I ended up getting rid of the .run method to simplify things, and here's all I have left. From reading previous issues, it looks like this library should be automatically providing the $inject property for me, but I get it with or without my added property.
export class AppModule {
public static $inject = ["$urlRouterProvider", "$stateProvider"];
public static config($urlRouterProvider: ng.ui.IUrlRouterProvider, $stateProvider: ng.ui.IStateProvider) {
AppModule.provideStates(routes, $stateProvider);
$urlRouterProvider.otherwise("/");
}
private static provideStates(states: IComponentState[], $stateProvider: ng.ui.IStateProvider) {
Here's (most) of the compiled .js code:
var m = function () {
function e() {}
return t = e,
e.config = function (e, n) {
t.provideStates(f, n),
e.otherwise("/")
},
...
e.$inject = ["$urlRouterProvider", "$stateProvider"],
e = t = i([o.NgModule({
id : "AppModule",
imports : ["legacyModule", "ui.router", "ui.bootstrap", "kendo.directives"],
declarations : [a.RootComponent, s.NavbarComponent, l.AppsComponent, c.AppDetailsComponent, u.AppVersionDetailsComponent, d.ProgressBarComponent],
providers : [h.ChartService, p.ApiService]
})], e);
var t
}
();
t.AppModule = m,
o.platformBrowserDynamic().bootstrapModule(m)
Any idea what the issue could be? I'm near my wit's end :) Thanks!
It doesn`t always work.
I don't know why, it's possible that by building on many threads, but it doesn't always work for me (If I have many imports)
With the following code everything works fine:
function Inject(name) {
return function (target, propertyKey, parameterIndex) {
if (target.$inject) {
target.$inject[parameterIndex] = name;
}
else {
console.error("Annotations should be provided as static $inject property in order to use @Inject decorator");
}
};
}
Plese merge it.
I passed selector and template to the component decorator, and I got an error about the template:
Argument of type '{ template: string; }' is not assignable to parameter of type 'ComponentOptionsDecorated'.
Property 'selector' is missing in type '{ template: string; }'.
this[handler].bind(this)
is not the same what this[handler]
, so method $element.on/off will not work correctly (handler after destroy will still be registered).
Is there a way to access $routeConfig
using the @Component
decorator?
.component('app', {
template:
'<nav>\n' +
' <a ng-link="[\'CrisisCenter\']">Crisis Center</a>\n' +
' <a ng-link="[\'Heroes\']">Heroes</a>\n' +
'</nav>\n' +
'<ng-outlet></ng-outlet>\n',
$routeConfig: [
{path: '/crisis-center/...', name: 'CrisisCenter', component: 'crisisCenter', useAsDefault: true},
{path: '/heroes/...', name: 'Heroes', component: 'heroes' }
]
});
Some observations:
I am trying to achieve component inheritance ( something like https://scotch.io/tutorials/component-inheritance-in-angular-2) using your decorator but it's not happening. could you pls let me know if it's possible?
When we do not provide an explicit NgModule/Injectable name the library throws a warning message telling "You are not providing explicit name be careful this code might not work as expected when minified". But if I'm 100% percent sure that the code will work (i.e. by disabling function names mangling in webpack), I would want not to see those messages polluting the console.
Is it possible to introduce kind of a flag to suppress them?
Thank you!
Given the following example:
@Component({...})
class A implements OnInit {
ngOnInit() { ... }
}
@Component({...})
class B extends A {
ngOnInit() {
super.ngOnInit();
}
}
This will not work since ngOnInit
is being replaced by $onInit
.
Using super.$onInit()
or super['$onInit']()
doesn't work either (depending of compiler strictness) due to TS not being aware of $onInit
method on parent class, or parent class having no index signature.
From the source I can see that when we use @HostListener
on a component, the controller will automatically inject $element
, but if we also want to use $element
in the controller then we cannot simply use the $element
since it is set as private already. I think either the $element
should be set as protected
/public
or should only be added if not already present
Currently there seem to be no way to add service providers registered with .provider(...)
api of angular modules, to modules registered with @ngModule
.
BTW, thanks for your nice job.
Hi,
Thanks for this great package!
I'd love to use it, but I'm having a little bit of trouble with it.
I get this error:
error in /Users/vincent/Code/Docker/ecotrace/volumes/website/node_modules/angular-ts-decorators/types/component.d.ts
(8,102): error TS2694: Namespace '"/Users/vincent/Code/Docker/ecotrace/volumes/website/node_modules/@types/angular/index".angular' has no exported member 'IControllerConstructor'.
When including the package.
I'm using TypeScript 2.5.3 and AngularJS 1.6.6
According to this issue there's a problem with one-way binding of complex objects.
Angular doesn't detect changes that happen within an object.
There are two solutions... making a clone of the object with angular.copy
or adding track by
@Component()
class MyComponent {
@Input('my-arr track by _id') myArr: [MyObj];
@Input('my-obj track by version') myObj: MyObj;
}
I tried applying the second solution using your decorators and it didn't work..
Is there a way to make it work?
First of all, thank you so much for this very useful library. I am using it to ease the transition to Angular 2, and it is great to use Angular 2 syntax with AngularJS.
It would be great to add Angular 2's provider options into the NgModule's providers property. Specifically, the useClass
, useValue
, and useFactory
options. This would be extremely useful to port over existing AngularJS factories and constants.
{provide: SomeClassOrString, useFactory: () => new Implementation()}
I would be willing to make a pull request with my attempt at the implementation, if you have any thoughts or insights, please let me know.
In Angular, when using ViewChild
or ViewChildren
, the reference of the element will be available even if the element is rendered after the view is init, so if you have a template like this:
<parent>
<child *ngIf="visible"></child>
</parent>
export class Parent {
@ViewChild(Child) child: Child;
ngAfterViewInit() {
setTimeout(() => visible = true, 5000);
}
}
The child
should not be null anymore after it is being rendered in the parent component. But since here the ViewChild
is only searched in the $postLink
, if it is not available then, it's impossible to use it
Hi,
First of all thanks for this awesome library, it is really cool to be able to use some Angular2+ sugar in Angular 1 already!!
I'm refactoring my app to use angular-ts-decoratos and I noticed a issue while refactoring my directives:
When the directive has no legacy link/compile methods defined, then the directive class will be used as controller (as you explained here: #30) so it should be instantiated with the params injected in its contructor right? Well, it seems it's not always the case. Digging in the code I noticed that the line to assign the controller is missing here (which should be like the one in the @Component.)
Moreover, the issue just appears as long as the directive does not use the @HostListener() decorator :p
Unknown provider: tProvider <- t <- DinnerService
@NgModule({
id: 'Dinners',
imports: [
uiRouter
],
declarations: [
DinnersComponent,
DinnerComponent,
DinnerFilterComponent,
DinnerModalComponent
],
providers: [
DinnerService
]
})
export class DinnerModule {}
DinnerService
@Injectable(DinnerService.name)
@Autowired()
export class DinnerService { .....
I use webpack, uglifyjs, typescript
Would it be possible to use @Inject()
to inject a value that has a name not matching the variable name? i.e. if I have a provider like this:
@Injectable('My.Service')
export class MyService {}
And later I want to inject it, I will have to use the $inject
. I think it would be very neat to do this instead:
export class MyController {
constructor(@Inject('My.Service') service: MyService) {}
}
I don't know if this is possible though
We found that the component hooks aren't being mapped due to the Object.keys() strategy. Upon inspection, it doesn't return any keys. This is from a TypeScript class.
Probably don't need to go that crazy with it. Loop through the hooks. If defined on the prototype, then map.
Hey,
I've found a problem recently. My application is built and delivered as a single bundle with no AngularJS inside, My requirement is that AngularJS has to be included on the same page as a separate bundle.
When I'm using angular-ts-decorators
I see that AngularJS is included in my app bundle which is unexpected behaviour. This happens due to this lines:
import { bootstrap, element, injector, isFunction, module as module$1 } from 'angular';
import * as angular from 'angular';
Not sure why would you need both lines, but anyway the result is that AngularJS is packed into the same bundle as dependency.
I would like to see that angular-ts-decorators
has a prerequisite requirement to have AngularJS be available globally on the page. Without AngularJS being available on the page, there is no point in using decorators anyway. The same approach is used for example in the Angular Material library.
Basically you could import needed functions from the global variable window.angular
.
In my bootstrap file:
angular.bootstrap(document, [(<NgModule>MyModuleName).module.name], {strictDi: true});
});
the .name
property is not recognised:
file: https://github.com/ShinDarth/Othello/blob/master/app/index.ts
I just started using this library a few days ago and it's great! I'm trying to migrate my code base over and everything's going pretty smoothly except for @Directives. Are there any examples around showing how to use @directive? I'm trying to move my link/compile methods to @Directive-decorated class controllers, but don't really understand how to get access to $scope/$element/$attributes injected into the new class constructor. Any help would be great!
Hi again,
Another issue I found in the @directive is the fact that the lifecycle hooks should still be defined using the NG1 syntax. The reason is that the call to replaceLifecycleHooks similar to the one done in the @component is missing.
Except for the ngAfterViewInit which is a component-only hook: https://angular.io/guide/lifecycle-hooks.
As usual, many thanks for your doings!
Recently after update to typescript 2.4.1 I have noticed that compiler complaints on type inconsistency.
More details you can read here about typescript breaking changes.
Exact error that I get:
Error TS2345 (TS) Argument of type '{ id: string; declarations: (typeof AdminLayoutComponent | typeof AdminDelegationComponent | type...' is not assignable to parameter of type 'ModuleConfig'
...
Property 'transform' is missing in type 'typeof MyCustomCreatedComponent'.
The only way to workaround that is to cast component to any in declarations, in module config like this:
@NgModule({
id: 'MyModule',
declarations: [
MyCustomCreatedComponent as any
]
})
I hope, you have some ideas how to get it fixed, if you need any additional explanation - please let me know.
Hello,
is it possible to remove console.warn from this library? In some cases, when migrating legacy project to TS with angular-ts-decorators we need to have restrict='E' and link function and it's very annoying to see every time warnings.
We know that using restrict='E' for directives is wrong but we really need it and we will be very happy i you allow to remove warnings. I've created pull request #47
Could you please take a look at it?
Looks like I am having a similar issue to (#3) with DI when using @directive annotation and injecting things into the constructor. The first constructor argument is an array of everything.
@Directive({
selector: "nlImageDrop",
scope: { }
})
export class ImageDropDirective {
static $inject = ["$log", "$parse"];
constructor(
private $log: ng.ILogService,
private $parse: ng.IParseService
) { }
When this code runs, $log
is an array and $parse
is undefined. I think its a similar issue that was happening with @pipe annotation before.
I'm trying to rewrite lots of code in AngularJS to eventually port it to Angular, but this is proving quite difficult because I need to understand the whole source code to know what to do.
For example, using @Input()
, it says "you can override bindings values only in @input decorator by passing '=' or '@' if you need to", but without any examples I cannot understand how to do it. And looking at the code of input
didnt help me either.
@vsternbach, Thanks a lot for your work.
Is it possible to add to @component decorator 'style/styleUrls' properties to load template in the same way as it already is with 'templateUrl'?
I have the following pipe
@Pipe({ name: "sanitize" })
export class SanitizePipe implements PipeTransform {
/*@ngInject*/
constructor(private $sanitize: ng.sanitize.ISanitizeService) { }
public transform(input: string) {
return this.$sanitize(input);
}
}
When I try using this in my code, I keep getting errors like this typeError: this.$sanitize is not a function
.
When I try to inspect the code for some reason $sanitize is an array instead of a function.
Am I doing something wrong?
I'm trying out this tool today, and so far I really like it! I got my first component running, but I found I had to manually add hints for dependency injection to let it run with strictDi
(and therefore with minified code), like this:
@Component({...})
export class MyComponent {
public static $inject = ["$myDepdency"]; // <- I had to add this line
constructor($myDependency) {...}
}
Is there some configuration I'm missing, or is this expected? If it is expected, that would be a great feature to support that automatically - if that's even possible.
Thanks!
How can I register controller with NgModule decorator?
.controller('myController', myController)
angular-ts-decorators doesn`t work with webpack configuration:
devtool: 'nosources-source-map',
plugins: [
new UglifyJSPlugin(),
]
How to fix this without changing configuration?
Normally when using Angular this code will set the Input with a default value:
@Component{}
export class Component implements AfterViewInit {
@Input()
value = 1;
ngAfterViewInit() {
console.log(this.value); // = 1
}
}
But it does not work with angular-ts-decorators, I always have undefined
as a value and need to do the default value in ngAfterViewInit
. Could it work the same way?
Right now Inject decorator only works on class constructor, need to add support for class methods
I have a 2 components like this:
import { Component } from 'angular-ts-decorators';
@Component({
selector: 'parent-component'
template: `
<child-component></child-component>
`
})
export class ParentComponent {}
@Component({
selector: 'child-component'
template: `
<div></div>
`,
require: {
parent: '?^^parentComponent'
}
})
export class ChildComponent {
parent?: ParentComponent;
}
which works really well. How could I use @ViewParent
here instead? I've tried a bunch of things but it always tells me it can't find the controller with name parentComponent
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.