Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement items list using observable store pattern #12

Closed
timofeysie opened this issue Dec 15, 2019 · 0 comments
Closed

Implement items list using observable store pattern #12

timofeysie opened this issue Dec 15, 2019 · 0 comments
Assignees
Labels
good first issue Good for newcomers

Comments

@timofeysie
Copy link
Owner

A full feature directory with the observable state and presenter/container pattern contains sub-directories called presenter/containers/services/types sub-folders as described in this article:
https://georgebyte.com/scalable-angular-app-architecture/

There is a good code example of a feature directory can be found here

For our project, we can use a simpler naming convention to eliminate the sub-directories.
The categories directory can be the start of a feature director

  1. create a items directory inside (with the observable state and presenter/container patterns)
  2. create a service with a RxJs subject
  3. create a container that uses the service to get the list of items
  4. create a presentation component to display data from the container via @Input/@output
  5. create a view class to sync with the state store via the router url

The directory structure can look like this:

├──categories/
│   ├── items/
│   │   ├── components/
│   │   │   ├── items.component.html
│   │   │   ├── items.component.scss
│   │   │   └── items.component.ts
│   │   ├── container/
│   │   │   ├── items.container.html
│   │   │   ├── items.container.scss
│   │   │   └── items.container.ts
│   │   ├── view
│   │   │   ├── items.view.html
│   │   │   ├── items.view.scss
│   │   │   └── items.view.ts
│   │   ├── items.endpoint.ts
│   │   ├── items.store.state.ts
│   │   ├── items.store.ts
│   │   └── item-type.ts
│   ├── categories-component.html
│   ├── categories-component.scss
│   ├── categories-component.ts
│   ├── categories-component.spec
│   ├── categories-routing.module.ts
│   └── categories.module.ts

The contents of the component and container directories are easy to determine.
However, how to implement the view and store classes as recommended from the various articles on the observable store pattern is more challenging.

This pattern involves features such as:

A dispatcher
dispatched events
initial state
application state observable
reducer function
the scan operator
the share operator

Another article about RxJs & functional reactive programming and building the app using the concepts of Redux and the single, but implementing it in Rxjs is found here

These examples are of a to do list.

How to use the application state observable section

Action Dispatcher (event bus) triggers events, and want some part of the application to be able to subscribe to the actions that it emits using an RxJs Subject

The dispatcher to be injected anywhere on the application needs an injection name. Let's start by creating such name using an OpaqueToken:
export const dispatcher = new OpaqueToken("dispatcher");

@NgModule({
...
    providers: [
        provide(dispatcher, {useValue: new Subject()})
    ]
})
export class AppModule {}

(or like this)

provide(state, 
            { useFactory: applicationStateFactory, 
              deps: [new Inject(initialState), new Inject(dispatcher)]
            })

when the system gets asked for dispatcher, this Subject will get injected.

constructor(@Inject(dispatcher) private dispatcher: Observer) {
    ...
}

dispatch events

this.dispatcher.next(new DeleteTodoAction(todo));

initial state for the app:

provide(initialState, {useValue: {todos: List([]), uiState: initialUiState}}),

The application state observable can be built as follows:

function applicationStateFactory(initialState, actions: Observable<Action>): Observable<ApplicationState> { ...  }

reducer function for all todo-related actions:
taking a state and an action, calculate the next state of the todo list
le. Here we have the reducer function for all todo-related actions:

function calculateTodos(state: List, action) {
    if (!state) {
        return List([]);
    }
    if (action instanceof  LoadTodosAction) {
        return List(action.todos);
    }
    else if (action instanceof AddTodoAction) {
        return state.push(action.newTodo);
    }
    else if (action instanceof ToggleTodoAction) {
        return toggleTodo(state, action);
    }
    else if (action instanceof DeleteTodoAction) {
        let index = state.findIndex((todo) => todo.id === action.todo.id);
        return state.delete(index);
    }
    else { return state; }
}

The scan operator takes a stream and creates a new stream that provides the output of a reduce function over time.

let appStateObservable = actions.scan( (state: ApplicationState, action) => {
   let newState: ApplicationState = {
       todos: calculateTodos(state.todos, action),
       uiState: calculateUiState(state.uiState, action)
   };
   return newState;
} , initialState);

Avoiding reducer functions from being called multiple times for one action using the share operator:

return appStateObservable.share();
@timofeysie timofeysie added the good first issue Good for newcomers label Dec 15, 2019
dhananjaya90 added a commit that referenced this issue Dec 18, 2019
dhananjaya90 added a commit that referenced this issue Dec 19, 2019
dhananjaya90 added a commit that referenced this issue Dec 20, 2019
dhananjaya90 added a commit that referenced this issue Dec 25, 2019
dhananjaya90 added a commit that referenced this issue Jan 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants