Part 6: State management with Vuex - SOLUTION

Nothing specific here. Just follow the instructions.

Step 1&2

Nothing specific here. Just follow the instructions.

// src/store/modules/hostelList.js
import * as types from '../mutation-types'
import * as hostelApi from '../../api/hostels'

// initial state
const state = {
  hostels: [],
  userSearch: '',
  onlyShowLiked: false,
  likedHostels: []
}

// getters
const getters = {
  hostelsShown: (state) => {
    const likeFilter = (hostel) => {
      if (state.onlyShowLiked) {
        return state.likedHostels.indexOf(hostel) !== -1
      }
      return true
    }

    return state.hostels
      .filter(likeFilter)
      .filter(hostel => hostel.name.toLowerCase().includes(state.userSearch.toLowerCase()))
  },
  isLikedHostel: (state) => (hostel) => {
    return state.likedHostels.indexOf(hostel) !== -1
  }
}

// actions
const actions = {
  loadHostels ({ commit }) {
    hostelApi.getAll().then(data => {
      commit(types.RECEIVE_HOSTELS, { data })
    })
  }
}

// mutations
const mutations = {
  [types.RECEIVE_HOSTELS] (state, { data }) {
    state.hostels = data
  },
  [types.TOGGLE_LIKE_HOSTEL] (state, hostel) {
    if (state.likedHostels.indexOf(hostel) === -1) {
      state.likedHostels.push(hostel)
    } else {
      state.likedHostels.splice(state.likedHostels.indexOf(hostel), 1)
    }
  },
  [types.LIKE_FILTER_CHANGE] (state) {
    if (state.onlyShowLiked === false) {
      state.onlyShowLiked = true
    } else {
      state.onlyShowLiked = false
    }
  },
  [types.TEXT_FILTER_CHANGE] (state, newSearch) {
    state.userSearch = newSearch
  }
}

export default {
  state,
  getters,
  actions,
  mutations
}

Step 3

App.vue

<template>
  <div id="app">
    <h1 class="ui center aligned header">HostelVue</h1>

    <div class="ui container">
      <div class="ui fluid menu">
        <router-link to="/list" class="item" active-class="active"><i class="grid layout icon"></i> List</router-link>
        <router-link to="/map" class="item" active-class="active"><i class="map icon"></i> Map</router-link>
        <div class="item">
          <i :class="onlyShowLiked ? 'red' : 'grey'" class="heart icon" @click="LIKE_FILTER_CHANGE" title="Only show liked hostels"></i>
        </div>
        <div class="item">
          <div class="ui transparent icon input">
            <input type="text" placeholder="Filter..." :value="userSearch" @input="updateSearchFilter">
            <i class="search link icon"></i>
          </div>
        </div>
      </div>
    </div>
    <br>

    <router-view/>
  </div>
</template>

<script>
import { mapState, mapMutations, mapActions } from 'vuex'

export default {
  name: 'app',
  computed: {
    ...mapState({
      userSearch: state => state.hostelList.userSearch,
      onlyShowLiked: state => state.hostelList.onlyShowLiked
    })
  },
  methods: {
    ...mapMutations([
      'LIKE_FILTER_CHANGE',
      'TEXT_FILTER_CHANGE'
    ]),
    ...mapActions([
      'loadHostels'
    ]),
    updateSearchFilter (e) {
      this.TEXT_FILTER_CHANGE(e.target.value)
    }
  },
  mounted () {
    this.loadHostels()
  }
}
</script>

HostelList.vue

<template>
  <div class="ui container">

    <div class="ui centered cards">
      <hostel-list-item
        v-for="(hostel, index) in hostels" :key="index"
        v-if="hostel.isActive"
        :hostel="hostel"
      ></hostel-list-item>
    </div>

  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import HostelListItem from './HostelListItem'

export default {
  components: {
    HostelListItem
  },
  computed: {
    ...mapGetters({
      hostels: 'hostelsShown'
    })
  }
}
</script>

HostelListItem.vue

<template>
  <div class="card">
    <div class="content">
      <div class="right floated meta">
        <i v-bind:class="{ red: isLikedHostel(hostel) }" class="heart icon" @click="TOGGLE_LIKE_HOSTEL(hostel)"></i>
      </div>
      <div class="header">{{ hostel.name }}</div>
      </div>
      <div class="content">
        <div class="meta">
          {{ hostel.location }}
          <i v-if="hostel.bonus.hasFreeWifi" class="wifi icon"></i>
          <i v-if="hostel.bonus.hasFreeBreakfast" class="coffee icon"></i>
          <i v-if="hostel.bonus.hasTv" class="desktop icon"></i>
          <i v-if="hostel.bonus.hasBar" class="bar icon"></i>
          <div class="right floated meta">
              {{ hostel.price.amount }}{{ hostel.price.currency }}/night
            </div>
        </div>
        <div class="description">
            {{ hostel.description }}
        </div>
      </div>
      <div v-if="hostel.availability" class="ui orange bottom attached button">
        Book now
      </div>
      <div v-else class="ui bottom attached disabled button">
        Too late!
      </div>
  </div>
</template>

<script>
import { mapGetters, mapMutations } from 'vuex'

export default {
  props: ['hostel'],
  computed: {
    ...mapGetters([
      'isLikedHostel'
    ])
  },
  methods: {
    ...mapMutations([
      'TOGGLE_LIKE_HOSTEL'
    ])
  }
}
</script>

HostelMap.vue

<template>
  <div id="map" class="map ui container">
  </div>
</template>

<script>
/* global google */
/* eslint no-new: "off" */
import { mapGetters } from 'vuex'

export default {
  data () {
    return {
      map: {},
      markers: []
    }
  },
  computed: {
    ...mapGetters({
      hostels: 'hostelsShown'
    })
  },
  // ...
}
</script>

results matching ""

    No results matching ""