Git Product home page Git Product logo

ngrx-actions's Issues

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.

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

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...

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 */ => { ... })

@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..

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.

License?

The project is missing a license 😉

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,
      });
    }
  };
}

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.

@Dispatch

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

Support Multiple Actions

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

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

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.

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.

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.

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.

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

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

@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!

Store DI

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

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();
    }));
  });
});

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

@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...

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.

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

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 :)

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 {
   ...
}

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[]) { ... }

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?

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

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

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.

Working Example ?

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

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.

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.