0.7.1 • Published 6 years ago

@ngrx-utils/cli v0.7.1

Weekly downloads
1
License
MIT
Repository
github
Last release
6 years ago

@ngrx-utils CircleCI Maintainability codecov Known Vulnerabilities

Quick start

npm i -S @ngrx-utils/store
# or
yarn add @ngrx-utils/store

What in the box?

routerLinkMatch directive

This directive will give you ability to add a class to the element when router url match a regular expression. The syntax is same with ngClass but replace the true/false expression with your string based regexp (like the string you pass to new RegExp('')).

Example: active-class will be added to a tag when router URL contains this segment: products/12345

<a routerLink="/products"
   [routerLinkMatch]="{
     "active-class": "products/\\d+"
   }"></a>

push pipe

This is a modified version of async pipe in @angular/common package. All the code implement are almost the same but this push pipe will call detectChanges() instead of markForCheck() so your pipe continue to work even in {ngZone: 'noop'} environment.

// main.ts
platformBrowserDynamic()
  .bootstrapModule(AppModule, { ngZone: 'noop' })
  .catch(err => console.log(err));

// app.module.ts
import { PushPipeModule } from '@ngrx-utils/store';
@NgModule({
  imports: [PushPipeModule]
})
export class AppModule {}
// heavy-compute.component.ts
import { Component, OnInit, NgZone } from '@angular/core';
@Component({
  template: `<h2>Test: {{ test$ | push }}</h2>`
})
export class HeavyComputeComponent implements OnInit {
  compute() {
    //...heavy computing
  }

  ngOnInit() {
    this.compute();
  }
}

ngLet directive

We often use *ngIf="stream$ | async as stream" to subscribe to an observable property and rename it to a template variable. But with nested template, *ngIf might remove your template which may not be expected.

NgLet to rescue:

import { NgLetModule } from '@ngrx-utils/store';

@NgModule({
  imports: [NgLetModule]
})
export class FeatureModule {}

Replace *ngIf with *ngLet:

    <ng-container *ngLet="(filterDate$ | async) as filterDate">
      <pick-date [registeredAt]="(device$ | async)?.registeredAt"
                    [firstDate]="filterDate?.from"
                    [secondDate]="filterDate?.to"></pick-date>
    </ng-container>

*ngLet just hold a reference to the result of async pipe in a template variable and don't have any special logic like structure directives such as *ngIf or *ngFor so it run faster and very handy.

You can also subscribe to multiple observable separately with *ngLet like this:

    <ng-container *ngLet="{
        device: device$ | async,
        date: filterDate$ | async
      } as options">
      <pick-date [registeredAt]="options.device?.registeredAt"
                    [firstDate]="options.date?.from"
                    [secondDate]="options.date?.to"></pick-date>
    </ng-container>

Actually this is an feature request in angular for quite long time as described in here but not yet been accepted.

untilDestroy pipeable operator

Have you ever feel odd when have to repeat calling unsubscribe with subscriptions in ngOnDestroy, or creating a Subject property, add takeUntil() to subscription, call next() in ngOnDestroy?

With untilDestroy pipeable operator:

import { untilDestroy } from '@ngrx-utils/store';

export class MyComponent implements OnDestroy {
  user: User;

  constructor(userService: UserService) {
    userService
      .getUsers()
      /** Automatically unsubscribe on destroy */
      .pipe(untilDestroy(this))
      .subscribe(user => (this.user = user));
  }

  /** Must have */
  ngOnDestroy() {}
}

NOTE: You still have to declare ngOnDestroy in Component because Angular does not support dynamically add component method in AOT mode

Credit to @SanderElias, this operator is inspired from his idea but he's currently not publishing it as an npm package.

Strong type pluck operator

This is just a wrapper function of rxjs/operators/pluck but has a nice type checking with plucked properties.

import { pluck } from '@ngrx-utils/store';

someObservable
  /** Type check here */
  .pipe(pluck('matches'));

picture

@Dispatch, @Select & @Pluck decorator

Shorter store.select and store.dispatch and you don't have to inject store in to your component anymore.

  • @Select accepts first parameter as a selector type (state: any) => any to select prop from your store (like selectors created with createSelector from @ngrx/store) and follows up to 8 pipeable operators. You can use operators like take(1) to automatically unsubscribe, or transform that value by using map and reduce selectors for nested properties in store...

  • @Pluck accepts a list of state property name start from root state. It also supports a 'dot' separated shorthand syntax or use component property name when no argument is specified. Inspired from ngrx-actions by @amcdnl.

  • @Dispatch mark your method return result as an action to dispatch from store. You can also return an array of actions if you want to dispatch multi actions in 1 method.

// app.module.ts
import { NgrxSelectModule } from '@ngrx-utils/store';

@NgModule({
  // Include `NgrxSelectModule` to your app.module.ts (Only add this to your AppModule):
  imports: [, /* ... */ NgrxSelectModule]
})
export class AppModule {}

// my.component.ts
import { take, map } from 'rxjs/operators';
import { Select, Pluck, Dispatch } from '@ngrx-utils/store';

@Component({
  template: ``
})
export class MyComponent {
  @Select(fromRoot.getRouterState, map(state => state.url), take(1))
  url$: Observable<string>;

  @Pluck('featureState', 'prop1')
  prop1: Observable<any>;

  @Pluck('featureState.prop2') prop2: Observable<any>;

  @Pluck() featureState: Observable<any>;

  @Dispatch()
  onLayoutChanged($event) {
    return new SetLayout($event);
  }

  @Dispatch()
  onMultiAction() {
    return [new SetLanguage(), new Navigate('/home')];
  }
}

This feature DO come with a trade-off: it lacks of type checking for your component properties due to Typescript issue #4881.

So please keep it in your mind when using @Select or @Pluck. @Dispatch is safe to use though. Hopefully Typescript team will fix that issue in the near future.

NOTE: You can NOT use this keyword inside @Select() to call component's methods because this using in @Select() is not your component instance.

You can using Select, Pluck, Dispatch decorator in any component. It also works with lazy load module too. You only need to import NgrxSelectModule once to your AppModule and in TestBed.configureTestingModule when running tests.

How to contribute

  • Fork this repo
  • Add your awesome feature and include it in the top level export
  • Run git add . && yarn cz to automatic generate Angular style commit
  • Send a PR here and describe some use cases.

ROADMAP to v1

@ngrx-utils/store

  • Introduce Pluck decorator for string select
  • Select decorator support pipeable operator
  • Strong typed pluck operator
  • untilDestroy operator
  • ngLet directive
  • routerLinkMatch directive

@ngrx-utils/schematics

  • ngrx-utils schematics for union Action type and Enum.

@ngrx-utils/effects, @ngrx-utils/cli - No longer been developed.

0.7.1

6 years ago

0.7.0

6 years ago

0.6.3

6 years ago

0.6.2

6 years ago

0.6.1

6 years ago

0.6.0

6 years ago

0.5.2

6 years ago

0.5.1

6 years ago

0.5.0

6 years ago

0.3.1

6 years ago

0.3.0

6 years ago

0.2.1

6 years ago

0.1.5

6 years ago

0.1.3

6 years ago

0.1.0

6 years ago