Skip to main content

INFO 4340/5440: Responding to State Changes

Class 5: Responding to State Changes

  1. Review: Vue.js Events & Reactive Variables
  2. Conditional CSS Classes
  3. Custom Events
  4. Watchers

Review: Components

components/StarRating.vue:

<script setup>
</script>

<template>
<div>
<button></button>
<button></button>
<button></button>
<button></button>
<button></button>
</div>
</template>

<style scoped>
</style>

App.vue:

<script setup>
import StarRating from './components/StarRating.vue'
</script>

<template>
<StarRating />
</template>

Review Props

components/StarRating.vue:

<script setup>
const props = defineProps({
name: String
})
</script>

<template>
<div>
<span>{{ name }}</span>
<button></button>
<button></button>
...

App.vue:

<script setup>
import StarRating from './components/StarRating.vue'
</script>

<template>
<StarRating name="Movie Rating"/>
</template>

Review: Prop Binding

Literal values:

<StarRating name="Movie Rating"/>

Variables, Reactive Variables, Expressions:

<script setup>
import { ref } from 'vue'
const movieRating = ref('Movie Rating')
</script>

<template>
<StarRating v-bind:name="movieRating"/>
</template>
<script setup>
import { ref } from 'vue'
const movieRating = ref('Movie Rating')
</script>

<template>
<StarRating :name="movieRating"/>
</template>

Review: Conditional Rendering

components/StarRating.vue:

<script setup>
defineProps({
name: String
})
</script>

<template>
<div>
<span v-if="!!name">{{ name }}</span>
<button></button>
...

Review: Rendering Lists – v-for Directive

components/StarRating.vue:

<script setup>
defineProps({
name: String
})
const stars = [1, 2, 3, 4, 5]
</script>

<template>
<div>
<span>{{ name }}</span>
<button v-for="num in stars" :key="num"></button>
</div>
</template>

Conditional CSS Classes


React: Conditional CSS Classes

export default function StarRating({ variant = "filled" }) {
return (
<div>
{[1, 2, 3, 4, 5].map(num => (
<button key={num}
className={variant === "filled" ? "solid" : "outline"}></button>
))}
</div>
)
}

Vue.js: Conditional Class Binding

<script setup>
defineProps({
variant: {
type: String,
default: 'filled'
}
})
</script>

<template>
<div>
<button v-for="num in 5" :key="num"
:class="variant === 'filled' ? 'solid' : 'outline'">
</button>
</div>
</template>

Activity: Conditional Class Binding

Working with a peer, complete the following tasks:

  1. Open the activity repository.
  2. Conditionally render the playing CSS class on the <button> when the SoundButton component its currently playing.

Custom Events


React: Custom Events

export default function StarRating({ onRate }) {
return (
<div>
{[1, 2, 3, 4, 5].map(num => (
<button key={num} onClick={() => onRate(num)}></button>
))}
</div>
)
}
export default function App() {
return (
<StarRating onRate={(num) => console.log(`Rated ${num} stars!`)}/>
)
}

Vue.js: Custom Events

<script setup>
const emit = defineEmits(['rate'])
</script>

<template>
<div>
<button v-for="num in 5" :key="num" @click="$emit('rate', num)"></button>
</div>
</template>
<template>
<StarRating v-on:rate="(num) => console.log(`Rated ${num} stars!`)"/>
</template>

Activity: Custom Events

Working with a peer, complete the activity handout.

<script setup>
const emit = defineEmits(['rate'])
</script>

<template>
<div>
<button v-for="num in 5" :key="num" @click="$emit('rate', num)"></button>
</div>
</template>
<template>
<StarRating v-on:rate="(num) => console.log(`Rated ${num} stars!`)"/>
</template>

Activity: Play and Stop Events

In the activity repository, emit play and stop events from the SoundButton component. The parent component should listen for these events and update the currentSounds reactive variable. Hint: removeCurrentSound

<script setup>
const emit = defineEmits(['rate'])
</script>

<template>
<div>
<button v-for="num in 5" :key="num" @click="$emit('rate', num)"></button>
</div>
</template>
<template>
<StarRating v-on:rate="(num) => console.log(`Rated ${num} stars!`)"/>
</template>

Watchers


Vue.js: Watchers

Definition: A watcher is a function that observes changes to a reactive variable and executes a callback function when the variable changes.

<script setup>
import { ref, watch } from 'vue'
const rating = ref(0)

watch(rating, (newVal, oldVal) => {
console.log(`Rated ${newVal} stars!`)
})
</script>

<template>
<StarRating v-on:rate="(num) => {rating = num}"/>
</template>

Activity: Watchers

In the activity repository, add a watcher for the currentSounds reactive variable. When the variable changes, update document.title with the names of the currently playing sounds.

<script setup>
import { ref, watch } from 'vue'
const rating = ref(0)

watch(rating, (newVal, oldVal) => {
console.log(`Rated ${newVal} stars!`)
})
</script>

<template>
<StarRating v-on:rate="(num) => {rating = num}"/>
</template>

Gotcha: Deep Watching

When watching an object or array, by default, the watcher will only trigger when the reference to the object or array changes. To watch for changes within the object or array, you need to use the deep option.

<script setup>
import { ref, watch } from 'vue'
const players = ref([13, 22, 32, 48])

watch(players, (newVal) => {
console.log(`Players changed: ${newVal}`)
},
{ deep: true })
</script>

What's Next

Homework 2 (Drum Simulator) released today.