Skip to content

[FEATURE] Angular skills should use modern way #72

@pkurcx

Description

@pkurcx

Problem Statement

Angular skills use legacy patterns like:

DI: injecting via constructor

constructor(private logger: Logger) {
  this.logger.log('Component initialized');
}

// 

constructor(@Inject(APP_CONFIG) private config: AppConfig) {
  console.log(this.config.apiUrl);
}

DI: @NgModule

@NgModule({
  providers: [loggerProvider]
})
export class AppModule {}

DI: Factory provider uses deps array

const apiUrlProvider: Provider = {
  provide: API_URL,
  useFactory: (config: AppConfig) => {
    // logic
  },
  deps: [AppConfig] // Dependencies for factory
};

DI: String Tokens (Legacy)

// Not type-safe, avoid when possible
const providers: Provider[] = [
  { provide: 'API_URL', useValue: 'https://api.example.com' },
  { provide: 'TIMEOUT', useValue: 5000 }
];

// Usage
export class MyService {
  constructor(
    @Inject('API_URL') private apiUrl: string,
    @Inject('TIMEOUT') private timeout: number
  ) {}
}

DI: Optional and Self Decorators

@Optional
@Self
@SkipSelf 
@Host

DI: ForRoot and ForChild Patterns

RxJS: Subscription Management

private subscription = new Subscription();

this.subscription.add(/*...*/)

ngOnDestroy() {
  // Unsubscribe from all
  this.subscription.unsubscribe();
}

// or

private destroy$ = new Subject<void>();

takeUntil(this.destroy$)

ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

RxJS: Data Service with State

Using BehaviorSubject for state

RxJS: structural directives

Uses *ngIf, *ngFor etc

Proposed Solution

Use modern patterns:

DI: injecting using inject() function

private readonly logger = inject(Logger);
// 
private readonly config = inject(APP_CONFIG);

DI: @NgModule

Use Standalone Components and inject using component providers

@Component({
  providers: [loggerProvider]
})
export class MyComponent {}

DI: Factory provider uses deps array

Use inject() in factory function

const apiUrlProvider: Provider = {
  provide: API_URL,
  useFactory: (config: AppConfig) => {
    const config = inject(AppConfig);
    // logic
  },
};

DI: String Tokens (Legacy)

If it is legacy do not add it at all.

DI: Optional and Self Decorators

Use inject() options

readonly #dep = inject(Dep, {
  optional: true,
  self: true,
  skipSelf: true,
  host: true,
});

DI: ForRoot and ForChild Patterns

Use Environment Providers

RxJS: Subscription Management

Use takeUntilDestroyed() operator

observable$.pipe(
  takeUntilDestroyed()
).subscribe();

or use DestroRef

readonly  #destroyRef = inject(DestroyRef);

this.#destroyRef.onDestroy(() => subscription.unsubscribe);

RxJS: Data Service with State

Use signals

RxJS: structural directives

Use new control flow syntax: @if, @for, @swtich

How It Embodies Bushido

  • Righteousness (義) - Does the right thing
  • Courage (勇) - Makes a bold improvement
  • Compassion (仁) - Helps users or contributors
  • Respect (礼) - Honors existing conventions
  • Honesty (誠) - Transparent about trade-offs
  • Honor (名誉) - Maintains quality standards
  • Loyalty (忠義) - Long-term maintainability

Alternatives Considered

Additional Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions