by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jul 26 05:47
    amithegde commented #20
  • Jul 26 05:46
    amithegde commented #20
  • Jul 06 09:12
    the0rem commented #20
  • Oct 15 2019 16:45
    johannesjo commented #20
  • Sep 21 2019 04:04
    JimTheMan closed #16
  • May 04 2019 00:15
    felixunivers edited #17
  • Apr 29 2019 21:30
    felixunivers opened #17
  • Apr 10 2019 21:15
    rezord opened #16
  • Jan 31 2019 20:44
    alex-okrushko commented #1511
  • Jan 31 2019 20:21
    brandonroberts edited #1538
  • Jan 31 2019 20:20
    brandonroberts edited #1538
  • Jan 31 2019 20:18
    brandonroberts labeled #1538
  • Jan 31 2019 20:18
    brandonroberts labeled #1538
  • Jan 31 2019 20:18
    brandonroberts opened #1538
  • Jan 31 2019 12:11
    timdeschryver closed #1536
  • Jan 31 2019 12:11
    timdeschryver commented #1536
  • Jan 31 2019 12:08
    timdeschryver closed #1537
  • Jan 31 2019 12:08
    timdeschryver commented #1537
  • Jan 31 2019 11:59
    olefrank commented #1036
  • Jan 31 2019 11:59
    olefrank commented #1036
Martin Chung
@mwchung24
Hi everyone. Is it possible to add data (used in multiple components) to the global store without using a component store?
new to NGRX so any guidance would be helpful :)
John McArthur
@BigBadJock
I'm having an issue with ngrx/data where I'm getting the error: entities.reduce is not a function. Not sure where I'm going wrong.
Alexander Escamilla
@alexesca
@mwchung24 you can dispatch actions from anywhere such as components, services, resolvers, guards, even factories. The global store can be accessed by any component in the application using selectors. I used to add the selectors in my component, but a better approach is creating a service with the sole purpose of getting the data from the store. The component doesn't know about the store, it only knows about this service. It makes your component/view cleaner and keeps the SRP(Single responsibility principle). This should make the first part of your question clear. Then... "...the global store without using a component store?" I believe I answered this at the beginning of my response.
@BigBadJock are you sure you entity/data is registered in the store
?
@alvipeo I hope you are using ngrx to track the routing navigation. Once this is clear, it depends on how you declared your paths and nested routes in your routing module. You should be able to pull each parameter defined in your routes/paths. {component: MyComponent, path: "concerts/edit/:concert/songs/:id"}
dev-ster
@dev-ster
How do you keep your many to many data in a ngrx store in sync when updating or deleting one entity?
Alexander Escamilla
@alexesca
@dev-ster can you explain more?
dev-ster
@dev-ster
@alexesca Sure. Imagine this strucutre.
On the product entity you would store categoryIds and on the category entity you would store productIds
If you delete on product entity it should be reflected in the category entity that still holds that product id inside the productIds array
Derek
@derekkite
@dev-ster both reducers can respond to the same action.
dev-ster
@dev-ster
Can you do that using an array of actions?
// product.reducer.ts
on([ProductActions.deleteProduct, CategoryActions.deleteCategory], (state, action) => { ...
Where do you check if the product should be deleted if a category of a certain id gets deleted?
Derek
@derekkite
in the product reducer, watch for a deletecategory, and edit the products as required. The category reducer would delete the category. Same with a delete product, the category reducer would watch for that action and edit the categories as required.
usually you would have a specific action to delete the many to many data keys
dev-ster
@dev-ster
That means I need to get all the product ids from that category and deleteMany in the product reducer correct?
Derek
@derekkite
Something like that. I don't know how you have set up your keys, but you can iterate through the entities, make a list of ids, and deleteMany.
dev-ster
@dev-ster
Sure I’ll take that approach
How do you normalize your data retrieved from the api?
Derek
@derekkite
Typically I shape the data for the view. Whatever that means, adding, deleting, selectors, ease of posting changes.
dev-ster
@dev-ster

Should .upsertMany() delete ids from a relation property inside the entity?

Imagine a product entity that has categoryIds: [1, 2, 3, 4, 5, 6, 7, 8] as property to reference the categories it belongs to.
If I load products with some other data from some endpoint I can normalize it and send it to the product reducer. This product doesn’t have all categories attached to it as the api has a limit on relations. The categoryIds in the product entity shouldn’t be remove though.
If I load the product with categoryIds: [1, 2, 3, 4, 5] the ids 6, 7, 8 will be removed after upsertMany.

Is that behavior wanted and correct in ngrx?

Derek
@derekkite
@dev-ster no it shouldn't. https://github.com/ngrx/platform/blob/master/modules/entity/src/unsorted_state_adapter.ts#L27 is what gets done.
you could have an instance of the array that is getting modified outside the reducer
Mike Herbert
@mherbert71_twitter
so I have an interesting ‘leak’, where after resetting a property (this property is a complex object) in the state, the state is correctly represented (property and it’s sub-properties), however there is an Object in memory (found using Chrome DevTools -> Memory Heap Snapshot) that still seems to be referenced. thoughts anyone? using @ngrx/store 8.6.0
Derek
@derekkite
@mherbert71_twitter if you have a reference to the object somewhere it won't get rid of it. let p = myobject means p has a reference to myobject.
dev-ster
@dev-ster
Can we combine the selectors to tidy that up?
export const selectAll = createSelectorFactory<{}, Dictionary<OrderView>>(arrayMemoizer)(
        selectEntities,
        UserSelectors.selectEntities,
        DentistSelectors.selectEntities,
        PatientSelectors.selectEntities,
        MaterialSelectors.selectEntities,
        WorkTypeSelectors.selectEntities,
        ToothColorSelectors.selectEntities,
        (
            entities: Dictionary<Order>,
            users,
            dentists,
            patients,
            materials,
            workTypes,
            toothColors
        ) => Object.values(entities).reduce((acc, entity) => {
            if ( entity.completeness === 2 ) acc.push(createOrderView(
                entity,
                users,
                dentists,
                patients,
                materials,
                workTypes,
                toothColors
            ));

            return acc;
        }, [])
    );


    export const selectById = (id: number) => createSelectorFactory<{}, OrderView>(objectMemoizer)(
        selectEntities,
        UserSelectors.selectEntities,
        DentistSelectors.selectEntities,
        PatientSelectors.selectEntities,
        MaterialSelectors.selectEntities,
        WorkTypeSelectors.selectEntities,
        ToothColorSelectors.selectEntities,
        (
            entities: Dictionary<Order>,
            users,
            dentists,
            patients,
            materials,
            workTypes,
            toothColors
        ) => createOrderView(
            entities[id],
            users,
            dentists,
            patients,
            materials,
            workTypes,
            toothColors
        )
    );
It’s redundant
Ohm0n
@thecyberd3m0n_gitlab
Hi people
I have some problems regarding writing tests to ngrx/store based chat library
  fit('should be able to create and join session', async (done) => {
    // let's create session
    dispatchers.init();

    const mainSub = selectors.mode$.subscribe((mode) => {
      if (mode === SecureChatMode.EMPTY) {
        dispatchers.setupProfile({
          name: 'test'
        });
      } else if (mode === SecureChatMode.READY) {
        dispatchers.startSession();
        const pendingSessionsSub1 = selectors.pendingSessions$.subscribe((pendingSessions: PendingSession[]) => {
          if (pendingSessions.length > 0) {
            expect(pendingSessions[0].id).toBeTruthy();
            const id = pendingSessions[0].id;
            mainSub.unsubscribe();
            pendingSessionsSub1.unsubscribe();
            TestBed.resetTestEnvironment();
            dispatchers.init();
            const secondSub = selectors.mode$.subscribe((mode) => {
              debugger;
              if (mode === SecureChatMode.EMPTY) {
                dispatchers.joinSession({ sessionId: id });
                done();
              }
            });
          }
        });
      }
    });
  });

});
That's how test looks like now. The problem is I need 2 instances of store to test creating session and joining to that session
the client who joins the session should be a new client, that has no same identity as user who creates the session
whould anybody has any idea how to test ngrx/store based chat library?
one guy on angular forum suggested that I shouldn't have global singletons and store ;)
RoboZoom
@RoboZoom
I don't have much experience in testing ngrx, still learning myself - but are you mocking the store?
Ohm0n
@thecyberd3m0n_gitlab
ok I managed to write it
no, I don't want to
it's not a atomic unit test, I want to test behaviour of whole library
my mistake was I treat it('should do something', () => {...}) as single test
so now test looks like this:

import { MasterSessionService } from './../lib/services/masterSession.service';
import { TestBed, async } from '@angular/core/testing';
import 'firebase/database';
import { SecureChatDispatchers } from '../lib/services/secure-chat.dispatchers';
import { SecureChatSelectors } from '../lib/services/secure-chat.selectors';
import { SecureChatMode } from '../lib/store/reducers/secure-chat.reducer';
import { activeSessionsAdapter } from './../lib/adapters/activeSessions.adapter';
import { messagesStateAdapter } from './../lib/adapters/decryptedMessages.adapter';
import { secureChatSessionsAdapter } from './../lib/adapters/secureChatSession.adapter';
import { configureTestingModule } from './configureTestBed';
import { PendingSession } from '../lib/models/pendingSession.model';
import { ActiveSession } from '../lib/models/activeSession.model';

fdescribe('Test secure-chat', () => {
  let dispatchers: SecureChatDispatchers;
  let selectors: SecureChatSelectors;
  let masterSessionService: MasterSessionService;
  let sessionId: string;
  beforeEach(
    configureTestingModule
  );
  beforeEach(() => {
    dispatchers = TestBed.inject<SecureChatDispatchers>(SecureChatDispatchers);
    selectors = TestBed.inject<SecureChatSelectors>(SecureChatSelectors);
    masterSessionService = TestBed.inject<MasterSessionService>(MasterSessionService);
  });

  it('should be able to create session', async (done) => {
    // let's create session
    dispatchers.init();

    const mainSub = selectors.mode$.subscribe((mode) => {
      if (mode === SecureChatMode.EMPTY) {
        dispatchers.setupProfile({
          name: 'test'
        });
      } else if (mode === SecureChatMode.READY) {
        dispatchers.startSession();
        const pendingSessionsSub1 = selectors.pendingSessions$.subscribe((pendingSessions: PendingSession[]) => {
          if (pendingSessions.length > 0) {
            expect(pendingSessions[0].id).toBeTruthy();
            sessionId = pendingSessions[0].id;
            mainSub.unsubscribe();
            pendingSessionsSub1.unsubscribe();
            done();
          }
        });
      }
    });
  });

  it('should be able to join to remembered session', async (done) => {
    dispatchers.init();
    selectors.mode$.subscribe((mode) => {
      if (mode === SecureChatMode.EMPTY) {
        selectors.activeSessions$.subscribe((activeSessions: ActiveSession[]) => {
          if (activeSessions.length > 0) {
            expect(activeSessions[0].id).toBeTruthy();
            done();
          }
        });
        dispatchers.joinSession({
          sessionId
        });
      }
    });
  });

});
first 'it' creates the session and stores id by first client. Second 'it' joins that session. beforeEach has callback 'configureTestingModule' - it means Angular will restart itself between 'it's'. So that's kind of whole workflow test (now it passes)
RoboZoom
@RoboZoom
Aren't the 'it' statements run in parallel? Are you counting on the first 'it' to be run before the second 'it'?
@thecyberd3m0n_gitlab
Ohm0n
@thecyberd3m0n_gitlab
if yes, I should disable this behaviour
but I see test are passed now
Ohm0n
@thecyberd3m0n_gitlab
no it doesn't run by parallel by default, you need to add 'parallel' into config (https://stackoverflow.com/questions/42965037/is-it-possible-to-run-parallel-tests-using-karma) but it is not desired behaviour in this case. My CI has some time and it doesn't need to be ultra fast.
RoboZoom
@RoboZoom
ah, cool
RoboZoom
@RoboZoom
I'm trying to write my first effect, but the compiler is telling me that my object exiting the ofType function is of type never
addPerson$ = createEffect(() =>
      this.actions$.pipe(
          ofType(AdminActions.AdminAction.addPerson),
          tap((x) => "Add Person Effect Called!"),
Here, x in final line is typed as (parameter) x: never
Am I doing something wrong?
RoboZoom
@RoboZoom
The follow on is that in later code, I want to access a prop