Store

Demo repo: https://code.area17.com/antoine/behaviors-state-management/

Key Concepts

Create your store with actions, mutations and an initial state:

store/index.js
import Store from '@area17/a17-helpers/src/utility/store'

const actions = {
  addItem(context, payload) {
      context.commit('addItem', payload)
  },
  clearItem(context, payload) {
      context.commit('clearItem', payload)
  },
  empty(context, payload) {
    context.commit('empty', payload)
  }
}

const mutations = {
  addItem(state, payload) {
    const newCart = state.cart
    newCart.push(payload)
    state.cart = newCart
    return state
  },
  clearItem(state, payload) {
    const newCart = state.cart
    newCart.splice(payload.index, 1)
    state.cart = newCart
    return state
  },
  empty(state, payload) {
    state.cart = []
    return state
  }
}

const initialState = {
  cart: []
}

export default new Store({ actions, mutations, initialState })

Dispatch changes:

store.dispatch('addItem', { id: '1', title: 'Product 1', price: 50 })

Subscribe to changes:

this.storeObserver = store.subscribe(this.render)

Unsubscribe to changes

this.storeObserver()

Demo Cart

Products

Product 1 - 50€


Product 2 - 200€


Product 3 - 100€

Cart

Nothing in Cart


Total : 0

This demo cart uses the following behaviors:

addToCart.js
import createBehavior from '@area17/a17-helpers/src/utility/createBehavior';
import store from '../store/index.js';

const addToCart = createBehavior(
  'addToCart',
  {
    handleClick() {
      store.dispatch('addItem', Object.assign({}, this.options))
    },
  },
  {
    init() {
      this.$node.addEventListener('click', this.handleClick, false)
    },
    destroy() {
      this.$node.removeEventListener('click', this.handleClick, false)
    },
  }
);

export default addToCart;
cart.js
import createBehavior from '@area17/a17-helpers/src/utility/createBehavior';
import store from '../store/index.js';

const cart = createBehavior(
  'cart',
  {
    renderItem(item) {
      // Simple templating
      return this.$template.innerHTML
      .replace(/%title%/gm, item.title ? item.title : '')
      .replace(/%price%/gm, item.price ? item.price : '')
      .replace(/%id%/gm, item.id ? item.id : '')
      .replace(/%img%/gm, item.img ? item.img : '')
    },
    render() {
      // Empty Cart
      if(store.state.cart.length === 0) {
        this.$node.classList.remove(this.klass)
        this.$list.innerHTML = `

${this.options.empty}

` return } // Generate cart markup this.$list.innerHTML = store.state.cart.map(item => { return this.renderItem(item) }).join('') this.$list.querySelectorAll('button').forEach((button, index) => { button.addEventListener('click', () => { store.dispatch('clearItem', { index }) }) }) // show extra markup this.$node.classList.add(this.klass) } }, { init() { this.klass = 'cart--ready' this.$template = this.getChild('template') this.$list = this.$node.firstElementChild this.storeObserver = store.subscribe(this.render) }, destroy() { this.storeObserver() // unsubscribe }, } ); export default cart;
emptyCart.js
import createBehavior from '@area17/a17-helpers/src/utility/createBehavior';
import store from '../store/index.js';

const emptyCart = createBehavior(
  'emptyCart',
  {
    handleClick() {
      store.dispatch('empty', {})
    },
  },
  {
    init() {
      this.$node.addEventListener('click', this.handleClick, false)
    },
    destroy() {
      this.$node.removeEventListener('click', this.handleClick, false)
    },
  }
);

export default emptyCart;
totCart.js
import createBehavior from '@area17/a17-helpers/src/utility/createBehavior';
import store from '../store/index.js';

const totCart = createBehavior(
  'totCart',
  {
    render() {
      this.$node.innerHTML = store.state.cart.reduce( function(a, b){
          return a + Number(b.price)
      }, 0);
    }
  },
  {
    init() {
      this.storeObserver = store.subscribe(this.render)
    },
    destroy() {
      this.storeObserver() // unsubscribe
    },
  }
);

export default totCart;