Skip to content

Bang Tips and Tricks

Chau Tran edited this page Jan 20, 2022 · 3 revisions

Global states

ngx-bang is designed (and required) to be used mainly as Local State Management due to how we need ChangeDetectorRef to invalidate our snapshot.

Technical wise, putting state() in a global Injectable (eg: providedIn: 'root') isn't that different from putting it in a local Injectable. However, the following rules should apply:

  • Never read from Global ngx-bang on the template.
  • Never use state from Global ngx-bang with *stateful
  • Derive local component states (view models) from Global ngx-bang with derive() then use derived with *stateful
@Injectable({providedIn: 'root'})
export class AuthStore {
    state = state<{ user: User | null }>({user: null});
}

@Component({
    template: `
        <ng-container *stateful="navbarState; let snapshot">
            {{snapshot.username}}
        </ng-container>
    `
})
export class NavbarComponent {
    navbarState = derive({
        username: get => get(this.authStore.state).user?.name
    });

    constructor(private authStore: AuthStore) {
    }
}

Work with Array

ngx-bang works with both mutability and immutability. If you prefer to use mutability approach, you should build your APIs in a way that work with Index rather than a Unique ID

@Component({
    template: `
        <todo-list 
            [todos]="snapshot.todos" 
            (toggleComplete)="onToggle($event)"
        ></todo-list>
    `
})
export class TodoComponent {
    state = state<{todos: Todo[]}>({todos: []});
    
    //        👇 use index here instead of some unique ID
    onToggle(index: number) {
        this.state.todos[index].completed = !snapshot(this.state).todos[index].completed
    }
}

Composing and Split States

You can split StateProxy from an original StateProxy

interface State {
    products: Product[];
    selectedProducts: Record<string, Product>;
}

const state = state<ProductState>({ 
    products: [],
    selectedProducts: {}
});

const productsState = state.products; // StateProxy<Product>[];
const selectedProductsState = state.selectedProducts; // StateProxy<Record<string, Product>>;

You can also combine them

interface ProductState {
    products: Product[];
    selectedProducts: Record<string, Product>;
}

const productsState = state<ProductState['products']>([]);
const selectedProductsState = state<ProductState['selectedProducts']>({});

const state = state({
    products: productsState as ProductState['products'], 
    selectedProducts: selectedProductsState as ProductState['selectedProducts']
});

Resetting states

Sometimes you would like to reset the StateProxy to its initial state. This is not straightforward because StateProxy needs to be reconstructed in the case of reset.

It is best to create a utility function with some cloneDeep logic.

const initialObj = {
  text: 'hello',
  arr: [1, 2, 3],
  obj: { a: 'b' },
}

const state = state(initialObj);

const reset = () => {
  const resetObj = _.cloneDeep(initialObj);
  Object.keys(resetObj).forEach((key) => {
    state[key] = resetObj[key]
  });
}
Clone this wiki locally