Giter Club home page Giter Club logo

dispatch-decorator's Introduction


The distribution for separation of concern between the state management and the view

NPM License

This package simplifies the dispatching process. It would be best if you didn't care about the Store service injection, as we provide a more declarative way to dispatch events out of the box.

Compatibility with Angular Versions

@ngxs-labs/dispatch-decorator Angular
4.x >= 13 < 15
5.x >= 15

📦 Install

To install the @ngxs-labs/dispatch-decorator, run the following command:

$ npm install @ngxs-labs/dispatch-decorator
# Or if you're using yarn
$ yarn add @ngxs-labs/dispatch-decorator
# Or if you're using pnpm
$ pnpm install @ngxs-labs/dispatch-decorator

🔨 Usage

Import the module into your root application module:

import { NgModule } from '@angular/core';
import { NgxsModule } from '@ngxs/store';
import { NgxsDispatchPluginModule } from '@ngxs-labs/dispatch-decorator';

@NgModule({
  imports: [NgxsModule.forRoot(states), NgxsDispatchPluginModule.forRoot()]
})
export class AppModule {}

Dispatch Decorator

@Dispatch() can be used to decorate methods and properties of your classes. Firstly let's create our state for demonstrating purposes:

import { State, Action, StateContext } from '@ngxs/store';

export class Increment {
  static readonly type = '[Counter] Increment';
}

export class Decrement {
  static readonly type = '[Counter] Decrement';
}

@State<number>({
  name: 'counter',
  defaults: 0
})
export class CounterState {
  @Action(Increment)
  increment(ctx: StateContext<number>) {
    ctx.setState(ctx.getState() + 1);
  }

  @Action(Decrement)
  decrement(ctx: StateContext<number>) {
    ctx.setState(ctx.getState() - 1);
  }
}

We are ready to try the plugin after registering our state in the NgxsModule, given the following component:

import { Component } from '@angular/core';
import { Select } from '@ngxs/store';
import { Dispatch } from '@ngxs-labs/dispatch-decorator';

import { Observable } from 'rxjs';

import { CounterState, Increment, Decrement } from './counter.state';

@Component({
  selector: 'app-root',
  template: `
    <ng-container *ngIf="counter$ | async as counter">
      <h1>{{ counter }}</h1>
    </ng-container>

    <button (click)="increment()">Increment</button>
    <button (click)="decrement()">Decrement</button>
  `
})
export class AppComponent {
  @Select(CounterState) counter$: Observable<number>;

  @Dispatch() increment = () => new Increment();

  @Dispatch() decrement = () => new Decrement();
}

You may mention that we don't have to inject the Store class to dispatch actions. The @Dispatch decorator takes care of delivering actions internally. It unwraps the result of function calls and calls store.dispatch(...).

Dispatch function can be both synchronous and asynchronous, meaning that the @Dispatch decorator can unwrap Promise and Observable. Dispatch functions are called outside of the Angular zone, which means asynchronous tasks won't notify Angular about change detection forced to be run:

export class AppComponent {
  // `ApiService` is defined somewhere
  constructor(private api: ApiService) {}

  @Dispatch()
  async setAppSchema() {
    const version = await this.api.getApiVersion();
    const schema = await this.api.getSchemaForVersion(version);
    return new SetAppSchema(schema);
  }

  // OR using lambda

  @Dispatch() setAppSchema = () =>
    this.api.getApiVersion().pipe(
      mergeMap(version => this.api.getSchemaForVersion(version)),
      map(schema => new SetAppSchema(schema))
    );
}

Note it doesn't if an arrow function or a regular class method is used.

Dispatching Multiple Actions

@Dispatch function can return arrays of actions:

export class AppComponent {
  @Dispatch() setLanguageAndNavigateHome = (language: string) => [
    new SetLanguage(language),
    new Navigate('/')
  ];
}

Canceling

@Dispatch functions can cancel currently running actions if they're called again in the middle of running actions. This is useful for canceling previous requests like in a typeahead. Given the following example:

@Component({ ... })
export class NovelsComponent {
  @Dispatch() searchNovels = (query: string) =>
    this.novelsService.getNovels(query).pipe(map(novels => new SetNovels(novels)));

  constructor(private novelsService: NovelsService) {}
}

We have to provide the cancelUncompleted option if we'd want to cancel previously uncompleted getNovels action:

@Component({ ... })
export class NovelsComponent {
  @Dispatch({ cancelUncompleted: true }) searchNovels = (query: string) =>
    this.novelsService.getNovels(query).pipe(map(novels => new SetNovels(novels)));

  constructor(private novelsService: NovelsService) {}
}

dispatch-decorator's People

Contributors

arturovt avatar eranshmil avatar greenkeeper[bot] avatar renovate-bot avatar renovate[bot] avatar splincode avatar tplk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dispatch-decorator's Issues

Problem accessing base class properties from derived Component class

Hello!

There appears to be an issue when accessing base class properties from a Dispatch handler defined in a derived Component class.

The target that is passed to the original function is the base class instead of the derived class.
https://github.com/ngxs-labs/dispatch-decorator/blob/master/src/lib/core/decorators/dispatch.ts#L75

If I change target to this, everything works as expected.

Here is a demo of the issue I'm seeing: https://stackblitz.com/edit/ngxs-starter-w9pjpj?file=app%2Fhello.component.ts

Is this a bug or expected behavior?

Thank you!

Default Dispatch method implementation

We could allow for a default dispatch method implementation if the user supplies the type to the dispatch method like so:

export class IncAction { constructor( public amount: number ) {} }

export class MyComponent {
  @Dispatch(IncAction)
  add: (amount: number) => IncAction; // note that this defines the type and not a value

  @Dispatch(IncAction)
  increaseBy(amount: number) { } // note that this defines a method with an undefined return 

}

@arturovt @splincode What do you think?

Actions dispatched using the decorator cannot be consumed as observables/promises

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Performance issue
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => https://github.com/ngxs/store/blob/master/CONTRIBUTING.md
[ ] Other... Please describe:

(not sure if behaviour already exists, is supposed to exist, or is not planned to exist)

Current behavior

There doesn't appear to be a way to wait for a dispatched action, which itself subscribes to an observable, to complete when using the @dispatch decorator, though it is possible by injecting the store.

Expected behavior

Being able to async/await on a dispatched action with asynchronous behaviour when using the dispatch decorator.

Minimal reproduction of the problem with instructions

https://stackblitz.com/edit/ngxs-dispatch-decorator-async

  1. Open the stackblitz console
  2. Click the first button. Notice that there is a delay of 1 second, then the count is updated in the page to 1 and the console logs 1
  3. Click the second button. Notice that the console immediately logs 1 and the count is not updated until a second later, so they are out of sync.

What is the motivation / use case for changing the behavior?

This behaviour works when using the standard injection style, and as we move towards using the annotation exclusively for cleaner code, I'd prefer to not have certain use cases where I must inject the store.

Environment


Libs:
- @angular/core version: latest
- @ngxs/store version: latest

Angular 11

Using this package with Angular 11 I get following warning:
npm WARN @ngxs-labs/[email protected] requires a peer of @angular/core@>=7.0.0 <10.0.0 but none is installed. You must install peer dependencies yourself.

Is this package still supported and will there be an update?

Dispatch problem after upgrading from version 4 to 5

After updating the project to Angular 17, it was also necessary to update the version of @ngxs-labs/dispatch-decorator from "4.0.2" to "5.0.0" and then I started to face usability problems. Actions that previously worked correctly have a long delay and often only trigger a click event elsewhere on the screen.

To validate that the problem was updating this plugin, I returned my application to Angular version 15.1.5 and the @ngxs-labs/dispatch-decorator version to 4.0.2 and it worked perfectly as you can see in the gif below:

evidencia issue funcionamento correto

So still in version 15.1.5 of Angular I updated the version of @ngxs-labs/dispatch-decorator to "5.0.0" and the problem starts to occur as shown in the gif below:
evidencia erro

The gifs were an example, but all actions in my application that use @dispatch() have the same behavior of not executing the action automatically, requiring a click event.

However, as the version of dispatch-decorator 4.x.x is not compatible with Angular 17, it is not an option to downgrade the version.

It is important to highlight that tests were carried out in different environments and not all of them were able to be reproduced in the same way. In some cases it works correctly in Chrome, while in Firefox it happens more frequently and the other way around too.
Tests carried out locally and in environments published in AWS.


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[x] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => https://github.com/ngxs/store/blob/master/CONTRIBUTING.md
[ ] Other... Please describe:

Current behavior

@dispatch() waiting for click event

Expected behavior

@dispatch() should follow the flow without the need for manual on-screen interaction

Minimal reproduction of the problem with instructions

Dispatch actions from the example above
image
image

image

image
image

Environment


Libs:
- @angular/core version: 15.1.5 and 17.1.3
- @ngxs/store version: 3.7.3 and 3.8.2


Browser:
- [x] Chrome (desktop) version 125.0.6422.141
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [x] Firefox version 127.0.1
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: 18.19.1 
- Platform: Linux

Others:

@Dispatch() not loading

Hi

Both lines of code are not working it not loading on the screen . If I put this .store.dispatch(... ) in the ngOnInit() it works
@dispatch() documentState = () => new GetDocumentState();
@dispatch() finYear = () => new GetFinancialYear();

Here is my state Management
import { State, Action, StateContext, Selector } from '@ngxs/store';
import { DocumentStateStateModel } from '../models/documentstate-state-model';
import { DocumentStateService } from '../services/document-state.service';
import { GetDocumentState } from './action';

@State({
name: 'getDocumentState',
defaults: {
DocumentStateLst: null,
ErrorMessage: null,
}
})

export class DocumentStateState {
constructor(private documentStateService: DocumentStateService) {
}

@selector()
static getDocumentStateLst(state: DocumentStateStateModel) {
return state.DocumentStateLst;
}

@action(GetDocumentState)
private async getDocumentState(ctx: StateContext, action: GetDocumentState) {
try {
const documentStateLst = await this.documentStateService.getDocumentStates().toPromise();
ctx.patchState({
DocumentStateLst: documentStateLst
});
} catch (error) {
ctx.patchState({
ErrorMessage: error.message
});
}
}
}

here my component

export class FilterTemplateComponent implements OnInit {

constructor(private fb: FormBuilder) { }

@input() perMonth: boolean;
filterTemplateForm: FormGroup;
sectionFilter: SectionFilter = new SectionFilter();

@select(DocumentStateState.getDocumentStateLst)
documentStateLst$: Observable<DocumentState[]>;

@select(FinancialYearState.GetFinancialYearLst)
financialYearLst$: Observable<FinancialYear[]>;

@select(ReportingPeriodState.GetReportingPeriodLst)
reportingPeriodLst$: Observable<ReportingPeriod[]>;

@dispatch() documentState = () => new GetDocumentState();
@dispatch() finYear = () => new GetFinancialYear();

ngOnInit() {
this.filterTemplateForm = this.fb.group({
documentstateId: ['', [Validators.required]],
financialYearId: ['', [Validators.required]],
reportingPeriodId: ['']
});
}
onFinancialYearselectionChange(event: MatSelectChange) {
this.filterTemplateForm.get('reportingPeriodId').setValidators([Validators.required]);
this.filterTemplateForm.get('reportingPeriodId').updateValueAndValidity();
this.GetReportingPeriod(event.value);
}

onResetClick() {
}

onFilterClick() {
console.log( {... this.sectionFilter, ... this.filterTemplateForm.getRawValue(), addButton: false});
}

@dispatch()
private GetReportingPeriod(finYearId: string):
GetReportingPeriod { return new GetReportingPeriod({financialYearId: finYearId}); }

}

here my html

      Document Status {{documentState.stateName}} Please choose a Document Status            Financial Year {{financialYear.reportingPeriodName}} Please choose a Financial Year            Reporting Period {{reportingPeriod.reportingPeriodName}} Please choose a Reporting Period            Apply Filters            Reset Filters           
      <button id="btnAdd" class="btnSectionButton" mat-raised-button color="primary">Add</button>
  </div>

Cannot find name 'unknown'.

Hi,

I have some problem during launch my app.

I use @ngxs/store : 3.4.2 and @ngxs-labs/dispatch-decorator : 1.1.0 and Angular: 6.1.3
When i serve the app i have this error

ERROR in node_modules/@ngxs-labs/dispatch-decorator/lib/core/actions/actions.d.ts(4,41): error TS2304: Cannot find name 'unknown'.

This is the actions.d.ts file

/**
 * This class is used as a default action when the user doesn't pass any custom action as an argument
 */
export declare class DispatchAction<T = unknown> {
    payload?: T | undefined;
    /**
     * Action type
     */
    static type: string | null;
    /**
     * Creates DispatchAction instance
     *
     * @param payload - Data to dispatch
     */
    constructor(payload?: T | undefined);
}

So, for solving it during my dev, i have replaced unknow by any.

How can solve this without changing the code in node_modules ?

Thx

NGXS dispatch-decorator plugin not working with withLatestFromOperator

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => https://github.com/ngxs/store/blob/master/CONTRIBUTING.md
[X ] Other... Please describe: Cannot find a way to make withLatestFromOperataor with @Dispatch methods

Current behavior

withLatestFrom() returns a nulll value after calling decorated method.

this.route.paramMap.pipe(
tap((params: ParamMap) => {
this.facade.initializeForm();
}),
switchMap((params: ParamMap) => {
const id = +params.get('id');
return of(this.facade.viewOrder(id)); // This is a decorated function with @dispatch
}),
withLatestFrom(this.facade.currentlyViewedDelivery$),

).subscribe((delivery) => {
console.log('DELIVERY', delivery);
});

Expected behavior

withLatestFrom should return the latest value.

Minimal reproduction of the problem with instructions

What is the motivation / use case for changing the behavior?

Environment


Libs:
- @angular/core version: "~8.2.9",
- @ngxs/store version: "^3.5.1",


Browser:
- [X ] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: node:12.13.0-alpine (docker)
- Platform:  Linux

Others:

Calling dispatch decorator

Hi there currently I am dispatching a call on a selectedchange event as below

private GetReportingPeriod(finYearId: string) {
this.store.dispatch(new GetReportingPeriod( { financialYearId: finYearId}));
}

How do I used the dispatch decorator so that i dont need to inject store any more

Angular 12

Could someone release a new version with the updated dependencies for Angular 12?

The observables end prematurely

When using the Dispatch operator with an asynchronous operation using Observables, this executes the action and ends, so if I use a timer or an interval it works only once.
Example:

D18hgsAWsAEsrrt

The biggest problem would be that if I execute the function multiples times, it should cancel the previous observable

Angular 13

Hello,

Is there any ETA to release a version compatible with Angular 13 dependencies?

If not, would be possible to release a new version for it?

thanks!

Package @ngxs-labs/dispatch-decorator has an incompatible peer dependency to @angular/core (requires >=12.0.0 < 13.0.0 , would install 13.2.1)

HMR breaks when calling dispatch on destroy

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => https://github.com/ngxs/store/blob/master/CONTRIBUTING.md
[ ] Other... Please describe:

Current behavior

Hi, i faced with issue

  • hmr enabled in project
  • used dispatch action in destroy hook to clean/reset something
  • on module replacement, i have an error:

[HMR] Update failed: Error: NG0205: Injector has already been destroyed.
    at R3Injector.assertNotDestroyed (http://localhost:4200/vendor.js:42569:19)
    at R3Injector.get (http://localhost:4200/vendor.js:42497:14)
    at NgModuleRef.get (http://localhost:4200/vendor.js:53097:33)
    at Object.get (http://localhost:4200/vendor.js:52774:35)
    at lookupTokenUsingModuleInjector (http://localhost:4200/vendor.js:34491:39)
    at getOrCreateInjectable (http://localhost:4200/vendor.js:34603:12)
    at NodeInjector.get (http://localhost:4200/vendor.js:34778:16)
    at localInject (http://localhost:4200/main.js:503:32)
    at CounterFacade.wrapped (http://localhost:4200/main.js:374:109)
    at CounterComponent.ngOnDestroy (http://localhost:4200/main.js:221:28)

Reason: dispatch decorator tried to inject Store via destroyed R3Injector and failed on it

Expected behavior

Dispatch decorator skip action in dev mode with enabled hmr on module reloading stage

Environment


Libs:
- @angular/core version: 13.2.3
- @ngxs/store version: 3.7.6

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.