Skip to content

Instantly share code, notes, and snippets.

@chrisvfritz

chrisvfritz/.md Secret

Last active July 24, 2019 00:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisvfritz/d36e2402b4f684fd420399fe3d1611c6 to your computer and use it in GitHub Desktop.
Save chrisvfritz/d36e2402b4f684fd420399fe3d1611c6 to your computer and use it in GitHub Desktop.

Teach the create option from the very beginning, replacing data/computed/methods:

new Vue({
  el: '#app',
  create: {
    message: 'Vue'
  }
})

Demonstrate how create adds new properties to the instance, which can be accessed in other options on this and in templates:

new Vue({
  el: '#app',
  create: {
    count: 1
  },
  onCreated() {
    console.log('Count is: ' + this.count)
  }
})
<div id="app">
  Count is: {{ count }}
</div>

The first time we demonstrate adding a method, just add it to the create object, which accesses other properties by assigning the Vue instance to a variable:

const app = new Vue({
  el: '#app',
  create: {
    message: 'Hello Vue.js!',
    reverseMessage() {
      app.message = app.message
        .split('')
        .reverse()
        .join('')
    }
  }
})

Show how to create computed properties using Vue.computed, accessing other properties the same way we did with a method:

const app = new Vue({
  el: '#app',
  create: {
    message: 'Hello',
    reversedMessage: Vue.computed(() =>
      app.message
        .split('')
        .reverse()
        .join('')
    )
  }
})

Introduce complex example with watch, which we demonstrate as an option (we don't need to go into using Vue.watch inside create, since it will be useful for organizational purposes far less often):

const app = new Vue({
  el: '#app',
  create: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!',
    getAnswer: _.debounce(() => {
      if (app.question.indexOf('?') === -1) {
        app.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      app.answer = 'Thinking...'
      axios
        .get('https://yesno.wtf/api')
        .then(response => {
          app.answer = _.capitalize(response.data.answer)
        })
        .catch(error => {
          app.answer = 'Error! Could not reach the API. ' + error
        })
    }), 500)
  },
  watch: {
    question(newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.getAnswer()
    }
  }
})

When we introduce components, we show that create has to be a function, just as we do currently for data:

Vue.component('button-counter', {
  create() {
    return {
      count: 0
    }
  },
  template: `
    <button v-on:click="count++">
      You clicked me {{ count }} times.
    </button>
  `
})

Demonstrate accessing props from the create function when we introduce them:

Vue.component('button-counter', {
  props: ['initialCount'],
  create(props) {
    return {
      count: props.initialCount
    }
  },
  template: `
    <button v-on:click="count++">
      You clicked me {{ count }} times.
    </button>
  `
})

Explain how the result of Vue.component is a component definition, not an instance, because components are reusable and can have many instances. That's why in create, we access other properties from computed properties and functions by assigning the object to a state variable:

Vue.component('button-counter', {
  props: ['initialCount'],
  create(props) {
    const state = {
      count: props.initialCount,
      countIncrease: Vue.computed(
        () => state.count - props.initialCount
      ),
      incrementCount() {
        state.count++
      }
    }

    return state
  },
  template: `
    <button v-on:click="incrementCount">
      You clicked me {{ count }}
      ({{ initialCount }} + {{ countIncrease }})
      times.
    </button>
  `
})

Demonstrate how the other options on a component are the same:

Vue.component('button-counter', {
  props: ['initialCount'],
  create(props) {
    return {
      count: props.initialCount
    }
  },
  onCreated() {
    console.log('The count is: ' + this.count)
  },
  watch: {
    count(newCount, oldCount) {
      console.log('The count changed!')
    }
  },
  template: `
    <button v-on:click="count++">
      You clicked me {{ count }} times.
    </button>
  `
})

===

DONE WITH ESSENTIALS

===

Demonstrate that some contextual information is available on the context object in the create function and under this.$context in other options:

Vue.component('button-counter', {
  create(props, context) {
    return {
      map: context.parent.map
    }
  },
  onCreated() {
    console.log(this.$context.parent.map)
  },
  template: '...'
})
Vue.component('some-component', {
  create(props, context) {
    return {
      fooPlusBar: Vue.computed(
        () => context.root.foo + context.root.bar
      )
    }
  },
  onMounted() {
    this.$context.root.baz()
  },
  template: '...'
})

const root = new Vue({
  el: '#app',
  create: {
    foo: 1,
    bar: Vue.computed(() => root.foo * 2),
    baz() {
      // ...
    }
  }
})
Vue.component('username-input', {
  create(props, context) {
    return {
      focus() {
        context.refs.input.focus()
      }
    }
  },
  onCreated() {
    console.log(this.$context.refs.input)
  },
  template: '...'
})

Demonstrate advanced composition by splitting up features into reusable functions, explaining the concept of bindings, reactive objects, when/why each are important to know about, and how to use the standalone function versions of other options such as watch, onCreated, etc:

export default getResults => {
  const state = reactive({
    query: '',
    results: [],
    run() {
      state.results = []
      return getResults(state.query).then(results => {
        state.results = results
      })
    }
  })

  return state
})
import { computed } from 'vue'
import orderBy from 'lodash/orderBy'

export default ({ input, options }) => {
  const state = reactive({
    input,
    options,
    selectedOptionIndex: 0,
    output: computed(() => {
      const selectedOption = state.options[state.selectedOptionIndex]
      return orderBy(state.input, ...selectedOption)
    })
  })

  return state
})
<script>
import createSearch from '@features/search'
import createSorting from '@features/sorting'
import axios from 'axios'
import { computed } from 'vue'

export default {
  create() {
    const productSearch = createSearch({
      getResults: query =>
        axios
          .get('/api/products', { params: { query } })
          .then(response => response.data.products)
    })

    const resultSorting = createSorting({
      input: computed(() => productSearch.results),
      options: [['averageReviewScore', 'desc'], ['price', 'asc']]
    })

    return { productSearch, resultSorting }
  }
}
</script>

<template>
  <input
    v-model="productSearch.query"
    @keydown.enter="productSearch.run"
  />
  <template v-if="resultSorting.output.length > 0">
    <SortingOptions
      v-model="resultSorting.selectedOptionIndex"
      :options="resultSorting.options"
    />
    <ProductList :products="resultSorting.output" />
  </template>
</template>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment