Git Product home page Git Product logo

ngrx-actions's Introduction

NGRX Actions

Actions/reducer utility for NGRX. It provides a handful of functions to make NGRX/Redux more Angular-tastic.

  • @Store(MyInitialState): Decorator for default state of a store.
  • @Action(...MyActionClass: Action[]): Decorator for a action function.
  • @Effect(...MyActionClass: Action[]): Decorator for a effect function.
  • ofAction(MyActionClass): Lettable operator for NGRX Effects
  • createReducer(MyStoreClass): Reducer bootstrap function
  • @Select('my.prop'): Select decorator

Inspired by redux-act and redux-actions for Redux.

See changelog for latest changes.

NOTE: I recommend checking out my latest library called NGXS.

Whats this for?

This is sugar to help reduce boilerplate when using Redux patterns. That said, here's the high level of what it provides:

  • Reducers become classes so its more logical organization
  • Automatically creates new instances so you don't have to handle spreads everywhere
  • Enables better type checking inside your actions
  • Reduces having to pass type constants by using type checking

Its dead simple (<100LOC) and you can pick and choose where you want to use it.

Getting Started

To get started, lets install the package thru npm:

npm i ngrx-actions --S

Reducers

Next, create an action just like you do with NGRX today:

export class MyAction {
  readonly type = 'My Action';
  constructor(public payload: MyObj) {}
}

then you create a class and decorate it with a Store decorator that contains the initial state for your reducer. Within that class you define methods decorated with the Action decorator with an argument of the action class you want to match it on.

import { Store, Action } from 'ngrx-actions';

@Store({
    collection: [],
    selections: [],
    loading: false
})
export class MyStore {
    @Action(Load, Refresh)
    load(state: MyState, action: Load) {
        state.loading = true;
    }

    @Action(LoadSuccess)
    loadSuccess(state: MyState, action: LoadSuccess) {
        state.collection = [...action.payload];
    }

    @Action(Selection)
    selection(state: MyState, action: Selection) {
        state.selections = [...action.payload];
    }

    @Action(DeleteSuccess)
    deleteSuccess(state: MyState, action: DeleteSuccess) {
        const idx = state.collection.findIndex(r => r.myId === action.payload);
        if (idx === -1) {
          return state;
        }
        const collection = [...state.collection];
        collection.splice(idx, 1);
        return { ...state, collection };
    }
}

You may notice, I don't return the state. Thats because if it doesn't see a state returned from the action it inspects whether the state was an object or array and automatically creates a new instance for you. If you are mutating deeply nested properties, you still need to deal with those yourself.

You can still return the state yourself and it won't mess with it. This is helpful for if the state didn't change or you have some complex logic going on. This can be seen in the deleteSuccess action.

Above you may notice, the first action has multiple action classes. Thats because the @Action decorator can accept single or multiple actions.

To hook it up to NGRX, all you have to do is call the createReducer function passing your store. Now pass the myReducer just like you would a function with a switch statement inside.

import { createReducer } from 'ngrx-actions';
export function myReducer(state, action) { return createReducer(MyStore)(state, action); }

In the above example, I return a function that returns my createReducer. This is because AoT complains stating Function expressions are not supported in decorators if we just assign the createReducer method directly. This is a known issue and other NGRX things suffer from it too.

Next, pass that to your NGRX module just like normal:

@NgModule({
   imports: [
      StoreModule.forRoot({
         pizza: pizzaReducer
      })
   ]
})
export class AppModule {}

Optionally you can also provide your store directly to the NgrxActionsModule and it will handle creating the reducer for you and also enables the ability to use DI with your stores. So rather than describing in forRoot or forFeature with StoreModule, we call them on NgrxActionsModule.

@NgModule({
   imports: [
      NgrxActionsModule.forRoot({
         pizza: PizzaStore
      })
   ],
   providers: [PizzaStore]
})
export class AppModule {}

Effects

If you want to use NGRX effects, I've created a lettable operator that will allow you to pass the action class as the argument like this:

import { ofAction } from 'ngrx-actions';

@Injectable()
export class MyEffects {
    constructor(
        private update$: Actions,
        private myService: MyService
    ) {}

    @Effect()
    Load$ = this.update$.pipe(
        ofAction(Load),
        switchMap(() => this.myService.getAll()),
        map(res => new LoadSuccess(res))
    );
}

In 3.x, we introduced a new decorator called @Effect that you can define in your store to perform async operations.

@Store({ delievered: false })
export class PizzaStore {
    constructor(private pizzaService: PizzaService) {}

    @Action(DeliverPizza)
    deliverPizza(state) {
        state.delivered = false;
    }

    @Effect(DeliverPizza)
    deliverPizzaToCustomer(state, { payload }: DeliverPizza) {
        this.pizzaService.deliver(payload);
    }
}

Effects are always run after actions.

Selects

We didn't leave out selectors, there is a Select decorator that accepts a (deep) path string. This looks like:

@Component({ ... })
export class MyComponent {
    // Functions
    @Select((state) => state.color) color$: Observable<string>;

    // Array of props
    @Select(['my', 'prop', 'color']) color$: Observable<strinv>;

    // Deeply nested properties
    @Select('my.prop.color') color$: Observable<string>;

    // Implied by the name of the member
    @Select() color: Observable<string>;

    // Remap the slice to a new object
    @Select(state => state.map(f => 'blue')) color$: Observable<string>;
}

This can help clean up your store selects. To hook it up, in the AppModule you do:

import { NgrxActionsModule } from 'ngrx-actions';

@NgModule({
    imports: [NgrxActionsModule]
})
export class AppModule {}

And you can start using it in any component. It also works with feature stores too. Note: The Select decorator has a limitation of lack of type checking due to TypeScript#4881.

Common Questions

  • What about composition? Well since it creates a normal reducer function, you can still use all the same composition fns you already use.
  • Will this work with normal Redux? While its designed for Angular and NGRX it would work perfectly fine for normal Redux. If that gets requested, I'll be happy to add better support too.
  • Do I have to rewrite my entire app to use this? No, you can use this in combination with the tranditional switch statements or whatever you are currently doing.
  • Does it support AoT? Yes but see above example for details on implementation.
  • Does this work with NGRX Dev Tools? Yes, it does.
  • How does it work with testing? Everything should work the same way but don't forget if you use the selector tool to include that in your test runner though.

Community

ngrx-actions's People

Contributors

albyrock87 avatar amcdnl avatar biggillystyle avatar isaacplmann avatar ocombe avatar sandangel 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

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

ngrx-actions's Issues

License?

The project is missing a license 😉

NgrxActionsModule nested reducers

Hello, is there a feature that would allow nesting reducers using NgrxActionModule forRoot/forFeature?

Example would look like that:

NgrxActionsModule.forRoot({
  levelA: {
    levelB: {
      myReducer: MyStore
    }
  }
})

Thank you in advance.

Unexpected state mutation in reducers

I have created a sample project that demonstrates an issue I have encountered in one work projects. I am using a meta-reducer in order to reset the store state. The expected result is to have the initial state set, but one of the boolean values in the state is unexpectedly flipped.

I provided a nearly identical second store with the traditional ngrx implementation of the reducer and this problem does not exist. I have been looking for a solution, but have not had luck.

Here is the project: https://github.com/DerekSchmid/ngrx-actions-bug

aot problem "Function expressions are not supported in decorators in 'reducer'

I cannot get it working with aot mode.

I've got a simple reducer going

import { createFeatureSelector, createSelector } from '@ngrx/store';
import { SecurityState } from './security.interfaces';
import { createReducer, Store, Action } from 'ngrx-actions';
import { Action as StoreAction } from '@ngrx/store';
import { LoginSuccess, Logout } from './security.actions';

export const initialSecurityState: SecurityState = {
  authenticated: false,
  user: undefined
};

@Store(initialSecurityState)
export class AuthStore {

  @Action(LoginSuccess)
  loginSuccess(state: SecurityState, action: LoginSuccess) {
    return {
      ...state,
      authenticated: true,
      user: action.user
    };
  }

  @Action(Logout)
  logout(state: SecurityState, action: Logout) {
    return {
      ...state,
      authenticated: false,
      user: undefined
    };
  }
}

export const securityReducer = function (state: SecurityState, action: StoreAction) {
  return createReducer<SecurityState>(AuthStore)(state, action);
};

And I've got a SecurityModule which I import from the AppModule.

import { ModuleWithProviders, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { SecurityRoutingModule } from './security-routing.module';
import { LoginPageComponent } from './components/login-page/login-page.component';
import { StoreModule } from '@ngrx/store';
import { securityReducer } from './security.reducer';
import { EffectsModule } from '@ngrx/effects';
import { SecurityEffects } from './security.effects';
import { IsAuthenticatedGuard } from './is-authenticated.guard';

@NgModule({
  imports: [
    CommonModule,
  ],
  declarations: [LoginPageComponent]
})
export class SecurityModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: RootSecurityModule,
      providers: [
        IsAuthenticatedGuard,
      ],
    };
  }
}

@NgModule({
  imports: [
    SecurityModule,
    StoreModule.forFeature('security', securityReducer),
    EffectsModule.forFeature([
      SecurityEffects
    ]),
    SecurityRoutingModule,
  ],
})
export class RootSecurityModule {}

It works without aot, but as soon as I add the --aot flag I get

ERROR in app/security/security.module.ts(32,40): Error during template compile of 'RootSecurityModule'
  Function expressions are not supported in decorators in 'securityReducer'
    'securityReducer' contains the error at app/security/security.reducer.ts(34,32)
      Consider changing the function expression into an exported function.

I've read the documentation and I'm wrapping the createReducer as suggested, but it still fails.

Angular 5.2.1
Typescript 2.6.2
ngrx-actions 2.1.1

Could it have anythinng to do with that I'm adding the reducer with StoreModule.forFeature?

Any suggestions would be greatly appreciated :)

Seralization

Reading the class's constructor name as the type, breaks:

  • serialization/deserialization of actions
  • SSR with NgRx
  • Dispatching actions from NgRx devtools

Idea could be to decorate the Actions w/ a type.

@Action('[Foo] bar')
MyActionClass { }

// cc: @MikeRyanDev

Type of ofAction mismatch after upgrade to 2.0.5

After upgrading to 2.0.5, ofAction has error: Argument of type 'typeof LoginAction' is not assignable to parameter of type 'new () => Action'.

with action class:

export class LoginAction implements Action {
  readonly type = '[Auth] Login';
  constructor(public payload: { email: string; password: string }) {}
}

This is because LoginAction class constructor has 1 parameter while type new () => Action has none.

Working Example ?

Could we please have a working example with effects and selectors ?

@Action - samples in the README and articles are incorrect?

README and article samples are looking like we can mutate state object directly and don't have to return anything from the reducer function. It doesn't seem to work...
If I do something like:

@Action(LoadAction)
  public load(state: IState, action: LoadAction) {
      state.isLoading: true,
      state.isLoaded: false,
      state.hasError: false,
      state.error: null
  }

It wouldn't work throwing exception about read-only variable modifications prohibited in the strict mode.

But if I change it to normal pure-function reducer, it will work just fine:

@Action(LoadAction)
  public load(state: IState, action: LoadAction) {
    return Object.assign({}, state, {
      isLoading: true,
      isLoaded: false,
      hasError: false,
      error: null
    });
  }

Tbh, the original idea of reducers as pure-functions is great... Sample code in the README looks like if it's a side-effect function with state mutation... if that's the case why then separate @effect from @action ? They are essentially the same...

Cannot find module 'ngrx-actions'

Hi Guys,

I just updated to version 4 and now I am unable to build the project using Angular CLI 6.0.7

I get the following error - Cannot find module 'ngrx-actions'

I have deleted node_module and downloaded everything again. Reverting back to v3.1.6 allows me to build again.

I am using Angular 6, CLI 6.0.7 and NGRX 6.0.1

Regards,
Tarek

Reduce createReducer function invocations

Currently its REALLY annoying to have to return a function with createReducer inside of it because of AoT.

I think providing our own NgrxActions.forRoot({ foo: FooStore }) would allow us to work around this and just wrap the StoreModule actions.

Need to think more though...

Perf Issue

The following code creates a new select on EVERY get. This is performance issue.

     Object.defineProperty(target, name, {
        get:() => store.select(fn),
        enumerable: true,
        configurable: true
      });

I tried to only create it once like:

      Object.defineProperty(target, name, {
        get: function() {
          if (!select) {
            select = createSelect();
          }
          return select;
        },
        enumerable: true,
        configurable: true
      });

however in real world unit tests, it started failing. I'm not sure the EXACT cause of this but I'm thinking its because the select is created statically and if u are recreating a class over and over again the select is not pointing to the new instance.

If that is true, I dont know a good way to do this because we need a hook to know when the class was created again and while we could do ngOnInit hook, it would get shaken out in AoT if the user didn't define this.

@albyrock87 - any thoughts?

New @Effect decorator

Hi there, very nice project, great job.

This is not an actual issue but more a question or a suggestion, depending on how you look at it. I am still very very green in this whole observable stuff, ngrx and etc. I have setup a project that is still relatively small and the other day i saw that you added this new @effect() decorator that is allowing me to consolidate also the effects under my store file, so to the question:

@Effect(DeliverPizza)
deliverPizzaToCustomer(state, { payload }: DeliverPizza) {
    this.pizzaService.deliver(payload);
}

What is the reasoning in providing the state parameter as first argument to the decorated method?

I find it handy to have the state supplied to the method, but most of the time in my effects I do only care about the action payload. Its not a big deal it just makes the linter to underline it. besides that I need to also type it each an every time. Maybe if it comes as a second argument it will be better, as I can simply omit the state if I don't need it.

Apparently this is not valid for the @action decorator as the reducer method would definitely need both parameters. Mentioned this, because it maybe that you wanted to be consistent with the decorators signatures.

Great work again.

Action without type not compliant with NgRx standard and not compatible/safe with AoT

feat(actions): less boilerplate! breaks compatibility with NgRx standard that requires a readonly "type" to be defined in Action types.

This is required for many reasons:

  • You can have two classes with the same name (e.g. LoadSuccess) in two different application contexts
  • AoT (minification) changes class names into "random" shorter names (e.g. LoadPizzasSuccess => ab); if two Action classes are minimized to the same name, you can have an action type conflict that leads to unknown behaviours

I'm asking to revert that change.

Thank you

Support Multiple Actions

Add the ability to support multiple actions in a store definition.

@Store({ ... })
export class MyStore {
   @Action(Action1, Action2)
   myfn() { ... }
}

No type checking for the @Select decorator

When doing something like that with ngrx:

files$: Observable<I18nFile[]> = this.store.select(getI18nFilesEntities);

I get a type check that the store will return something of type I18nFile[].

But if I change it to that:

@Select('i18nFiles.entities') files$: Observable<I18nFile[]>;

It doesn't type check that it's of type I18nFile[].

I'm not sure if it's possible to type check with decorators, but that would be nice.

Select fn this binding

If you were to write something like:

@Select(state => {
   console.log('here', this);
   return this._mapFolders(state.folders.items) 
}) items$: Observable<TreeNode[]>;

The this would refer to something else rather than the intended class. Upon attempting to resolve this in select decorator fn like:

else {
  fn = selectorOrFeature.bind(target);
}

The binding here is correctly happening but for whatever reason at runtime it is not resolved correctly. I'm wondering if this is a TypeScript compilation issue?

A suitable workaround is to use anonymous functions rather than class methods.

ActionType should be generic

current implement:
symbols.ts

export type ActionType = { new (...args: any[]): Action };

We can add generic type for ActionType like this:

export type ActionType<T extends Action = Action> = { new (...args: any[]): T };

Then we can reuse it, i.e in of-action.ts

import { ActionType } from './symbols';

export function ofAction<T extends Action>(allowedType: ActionType<T>): OperatorFunction<Action, T>;

or in action.ts

export function Action(...actionsKlasses: ActionType[]) { ... }

Action options

Update Action signature to be like:

@Action(MyAction, { update: boolean });
@Action([MyAction, MyAction2], { update: boolean });
@Action('MY_ACTION')

update would be optional to spread the existing.

Can it be used with redux?

Hi Austin,

great work trying to reduce boilerplate code!
One of the projects I'm working on is still AngularJS with typescript, redux, redux-observables, reselect...

Would you maybe consider making a version for plain redux? Actions should also be POJOs instead of classes I think.

It might be enough to educate me about how to do it in a blog post or so. :)

I think that once we migrate to angular we will stick with redux and not switch to ngrx to use all the awesome middleware and tools from the redux-community (redux-persist, redux-offline...). ng2-redux would be also be a good choice I think.

Cheers,
Michael

Composing Selector: How @Select work with composing selector?

I have some selectors that composed from URL and entities, how can I convert it to use with @Select?

Can we do something like this?

export function Select(selector?: string | MemoizedSelector<any, any>): any {
  return function(target: any, name: string, descriptor: TypedPropertyDescriptor<any>): void {
    if (delete target[name] && typeof selector === 'string') {
      Object.defineProperty(target, name, {
        get: () => {
          if (!NgrxSelect.store) {
            throw new Error('NgrxSelect not connected to store!');
          }

          const fn = memoize(state => getValue(state, selector || name));
          return NgrxSelect.store.select(fn);
        },
        enumerable: true,
        configurable: true,
      });
    } else if (delete target[name] && typeof selector === 'function') {
      Object.defineProperty(target, name, {
        get: () => {
          if (!NgrxSelect.store) {
            throw new Error('NgrxSelect not connected to store!');
          }
          return NgrxSelect.store.select(selector);
        },
        enumerable: true,
        configurable: true,
      });
    }
  };
}

@Dispatch

Hello @amcdnl , is not really an issue but why not implement a dispatch decorator, it would be nice :).
Great work by the way !

Publish 4.0.1 to NPM?

Are there plans to publish version 4.0.1 to NPM? I am still only seeing 4.0.0 as being available.

Question: Unit testing with the @Select decorator

Hey @amcdnl , I'm trying to figure out the best way to unit test a service that uses the @Select decorator provided by ngrx-action. Currently I'm using the TestBed from @angular/core, but I'd like to not have to spin up an angular module in order to test the service.

Is there any way we can "mock out" the @Select decorator to avoid having to start up TestBed to test the interactions with the store?

Here is the service I'm trying to unit test:

import { Injectable } from '@angular/core';
import { Select } from 'ngrx-actions';
import { Observable } from 'rxjs/Observable';
import { first } from 'rxjs/operators';

import { Dispatch } from '../../decorators/redux/dispatch';

import { ConfigurationActions } from './configuration.actions';
import { ConfigurationSelectors } from './configuration.selectors';

@Injectable()
export class ConfigurationStateService {

  @Select(ConfigurationSelectors.apiUrl) apiUrl$: Observable<string>;
  @Select(ConfigurationSelectors.adminApiPath) adminApiPath$: Observable<string>;
  @Select(ConfigurationSelectors.httpCallTimeout) httpCallTimeout$: Observable<number>;
  @Select(ConfigurationSelectors.techDiffUnhandledExceptions) techDiffUnhandledExceptions$: Observable<boolean>;
  @Select(ConfigurationSelectors.isConfigurationSet) private isConfigurationSet$: Observable<boolean>;

  @Dispatch() private configurationRequest = ConfigurationActions.onConfigurationRequest;

  onConfigurationRequest = () => {
    this.isConfigurationSet$.pipe(
      first((isLoaded: boolean) => isLoaded === false))
      .subscribe(() => {
         this.configurationRequest();
      });
  }
}

Here is the spec testing that service:

import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { Store, StoreModule } from '@ngrx/store';
import { NgrxActionsModule } from 'ngrx-actions';

import { NgSelect } from '../../decorators/redux/ng-select';
import { ConfigurationState } from '../configuration/models/configuration-state.interface';

import { ConfigurationStateService } from './configuration-state.service';
import { configurationReducer } from './configuration.reducer';
import { ApplicationConfiguration } from './models/application-configuration.interface';
import { OnConfigurationReceived } from './models/on-configuration-received.interface';
import { OnConfigurationRequested } from './models/on-configuration-requested.interface';
import { OnConfigurationResetRequested } from './models/on-configuration-reset-requested.interface';

let store: Store<ConfigurationState>;
let ngSelect: NgSelect;
let appConfig: ApplicationConfiguration;

const initializeDependencies: () => void = () => {
  TestBed.configureTestingModule({
    imports: [
      NgrxActionsModule,
      StoreModule.forRoot({ configurationState: configurationReducer})
    ],
    providers: [
      NgSelect,
      ConfigurationStateService
    ]
  });
  store = TestBed.get(Store);
  ngSelect = TestBed.get(NgSelect);
  ngSelect.connect(store);
};

const createComponent: () => ConfigurationStateService = () => {
  const service = TestBed.get(ConfigurationStateService);
  return service;
};

describe('State Service: Configuration: ', () => {

  beforeEach(() => {
    initializeDependencies();
  });

  describe('with isConfigurationSet = false', () => {
    beforeEach(() => {
      store.dispatch(new OnConfigurationResetRequested());
    });

    it('should dispatch a configuration request', fakeAsync(() => {

      spyOn(store, 'dispatch').and.callThrough();

      createComponent().onConfigurationRequest();
      tick();
      const action = new OnConfigurationRequested();
      expect(store.dispatch).toHaveBeenCalledWith(action);
    }));
  });

  describe('with isConfigurationSet = true', () => {
    beforeEach(() => {
      appConfig = {
        adminApiPath: 'xyz',
        techDiffUnhandledExceptions: true,
        apiUrl: 'zyx',
        httpCallTimeout: 0
      };
      store.dispatch(new OnConfigurationReceived(appConfig));
    });

    it('should not dispatch a configuration request', fakeAsync(() => {
      spyOn(store, 'dispatch').and.callThrough();
      createComponent().onConfigurationRequest();
      tick();
      expect(store.dispatch).not.toHaveBeenCalled();
    }));
  });
});

Return Store initial state to be used in the tests

How can I return the initial state of the reducer(@Store) to be used in the following test?

describe('UNKNOWN action', () => {
   test('should return the initial state', () => {
      const action = {} as any;
      const state = reducer(InitialState, action);
      expect(state).toBe(InitialState);
    });
  });

Because I had the idea to go on creating InitialState and passing it to the Store that way.
(With this I have still got the InitialState Constant to use in the tests)

export const InitialState: LoginState = {
  error: null,
  pending: false
};


@Store(InitialState)
export class LoginStore {
   ...
}

NgrxActionsModule.forFeature - not working?

When I import with forRoot call everything seem to work fine:

NgrxActionsModule.forRoot({ myStore: MyStore })

But when I try to import with forFeature call instead, it breaks:

NgrxActionsModule.forFeature('myFeature', { myStore: MyStore })

RxJs 6 - Property 'pipe' does not exist in type 'Actions<Action>'

I am looking at upgrading out project to RxJs 6. Going through the code, I have made all changes that were having compile issues. However, every single Effect I have is showing the same error. Prior to RxJs 6 (up to and including 5.5.10), all worked as expected.

@Injectable()
export class WidgetSearchEffects {
    @Effect()
    detectWidgetDataRequested$ = this.actions$.pipe(
        ofAction(OnWidgetDataRequested),
        mergeMap((action: OnWidgetDataRequested) => this.widgetSearchService.searchById(action.payload)
          .pipe(map((data: WidgetSearchResult) => WidgetSearchActions.onWidgetDataReceipt(data)))),
        catchError(error => this.onError(error))
    );

    constructor(
        private actions$: Actions,
        private widgetSearchService: WidgetSearchService
    ) {}

    private onError = (error: Error) => {
        return of(WidgetSearchActions.serverError(error));
    }
}

I get the same error on every effect:

Property 'pipe' does not exist on type 'Actions<Action>'.

Any ideas of where I went wrong or is this a compatibility issue with ngrx-actions and RxJs 6

Better infer type checking ofAction

Can you consider changing return type OperatorFunction<Action, Action> to OperatorFunction<Action, T>?

): OperatorFunction<Action, Action>;

Then user could use like this

@Effect()
getFooBar$ = this.actions$.pipe(
    ofAction<GetFoo | GetBar>(
      GetFoo, GetBar
    ),
    map(action /* action has infer type GetFoo | GetBar */ => { ... })

Import issues with typescript (using Vscode insiders and typescript 2.7.0 dev build)

Hi,

Firstly, thanks very much for this library.

I have a problem when using Vscode insider and importing items from your library.

When importing from 'ngrx-actions' typescript reports it cannot find the export. However, when importing form 'ngrx-actions/public_api' everything work fine.

Older versions of typescript dont have a problem but cannot correctly indent the arguments of the pipe method.

Regards,
Tarek

NGRX 7.x Upgrade

Version 7.x of the ngrx plattform is out. And this package needs an update. I can provide a pull request for the update if you want.

Prevent or warn when an action class is used multiple times in a store

When an action class is used in multiple action decorators on the same store, only one of the decorated methods is being called for this action. While the limitation itself makes sense, an error or warning would make it easier to detect bugs related to this.

Example:

@Store({
})
export class ExampleStore {
  @Action(ExampleAction)
  load(state: AppState, action: ExampleAction) {
    alert('Message 1');
  }

  @Action(ExampleAction)
  load(state: AppState, action: ExampleAction) {
    alert('Message 2');
  }
}

Only "Message 2" will be shown when ExampleAction is dispatched.

My experience with decorators in Typescript is limited, but if it's okay, I would try myself on a PR for this.

@Effect() decorator does not work

@Effect(MyAction) decorator is not working, effect functions are never called.
Using @ngrx/effects @Effect() works as expected.

Versions:
"@angular/core": "~7.0.0", "@ngrx/store": "^6.1.2", "@ngrx/effects": "^6.1.2", "ngrx-actions": "^4.0.0",

Example code

@Store(initialAppState)
export class AppStore {
    @Action(TestAction)
    test(state: AppState, action: TestAction) {
        state.test = 1;
        // this runs after dispatch
    }

    @Action(TestComplete)
    complete(state: AppState) {
        state.test = 2;
        // never runs
    }

    @Effect(TestAction)
    testEffect() {
         //never runs
        return of(new TestComplete());
    }
 }

I have tried this with every combination of EffectsModule.forRoot() i could think of, passing empty array, array containing the store class, no EffectsModule import, etc..

Store DI

If I were to implement #33 it would be possible to DI.

@Select decorator improvement

Hey guys!

I'm using @select decorator for ngrx-actions and it works pretty fine. Although, when the app and the store are pretty big, it's always very often the probability that the selector which is actually a string, to be wrong types.

Ex:
let's suppose we have: @select("myStore.myPieceOfStore") public pieceOfStore$: Observable;

It's very often a probability like 'myStore.myPieceOfStore' string to be wrong typed and we are not going to have any errors to tell us this.

Do you think we improve this?

Thanks!

Map Decorator

Selects are awesome but they dont provide a way to map values from one object to another. For instance if i were to write:

@Select(state => state.map(i => 'foo')) foo$;

It would run constantly because the compare results is not the same. I want a way that I can select something from the store and then map it into a completely different object.

Potentially API could be very similar just using a @Map decorator. Could look at using a distinctUntilChanged rx operator and then performing the map.

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.