Table of Contents
- Define Component Event to Emit Active Plan
- Add Selected Prop in the CoffeePlan Component
- PlanPicker notifies other CoffeePlan to unselect
- Conclusions
- Resources
- Github Repositories
On day 16, I put a border around a coffee plan when it was selected. Other coffee plans were inactive and lost the border. The CoffeePlan component emitted the active name to the PlanPicker component so that it could notify other coffee plans to remove the border.
Define Component Event to Emit Active Plan
- Vue 3 application
const emit = defineEmits<{
(e: 'selectedPlan', name: string): void
}>()
function selectPlan() {
emit('selectedPlan', props.name)
}
The CoffeePlan component defines a custom event that emits the name to the PlanPicker component.
The selectedPlan function uses the selectedPlan event to emit the plan name to the parent component. The PlanPicker component receives the active plan and can notify other plans that they are no longer active.
- SvelteKit application
<script lang="ts">
interface Props {
name: string;
selectedPlan: (name: string) => void;
}
let { name = 'Default Plan', selectedPlan }: Props = $props();
const handleSelectPlan = () => selectedPlan(name);
</script>
The CoffeePlan component extracts the selectedPlan function from $props(). The PlanPicker component must provide the prop callback for the CoffeePlan component to call in the handleSelectPlan function.
The handleSelection invokes the selectedPlan function with the plan name.
- Angular 19 application
@Component({
...
})
export class CoffeePlanComponent {
name = input('Default Plan');
selectedPlan = output<string>();
selectPlan() {
this.selectedPlan.emit(this.name());
}
}
The CoffeePlanComponent declares a selectedPlan output that emits the coffee name to the PlanPickerComponent. The name is a signal input of type string.
When the selectPlan method is invoked, the selectedPlan outputs the result of the name getter to the PlanPickerComponent.
Add Selected Prop in the CoffeePlan Component
- Vue 3 application
<script setup lang="ts">
const props = defineProps({
selected: {
type: Boolean,
default: false,
},
})
</script>
Add selected to the CoffeePlan's props. When selected is true, the plan is active and it has a border. When selected is false, the border is removed.
<template>
<div class="plan" @click="selectPlan" :class="{ 'active-plan': selected }">
<div class="description">
<span class="title"> {{ name }} </span>
</div>
</div>
</template>
The div element is bound to the active-plan class dynamically. When selected is true, the CSS class is enabled. Otherwise, the class is removed.
- SvelteKit application
<script lang="ts">
interface Props {
name: string;
selectedPlan: (name: string) => void;
selected: boolean;
}
let { name = 'Default Plan', selectedPlan, selected }: Props = $props();
const handleSelectPlan = () => selectedPlan(name);
</script>
Similarly, the selected flag is extracted from $props. Moreover, the Props interface has a selected property of type boolean.
<div onclick={handleSelectPlan} class={['plan', selected && 'active-plan']}>
<div class="description">
<span class="title"> {name} </span>
</div>
</div>
When selected is true, active-plan is part of the class list. Otherwise, the class list does not have the class
- Angular 19 application
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
@Component({
selector: 'app-coffee-plan',
template: `
<div class="plan" (click)="selectPlan()" [class]="{ 'active-plan': selected() }">
<div class="description">
<span class="title"> {{ name() }} </span>
</div>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CoffeePlanComponent {
name = input('Default Plan');
selected = input(false);
selectedPlan = output<string>();
selectPlan() {
this.selectedPlan.emit(this.name());
}
}
selected is a signal input that is initialized to false.
The class attribute of the div element is bound to an object. active-plan class is enabled when the getter function of selected returns true. Otherwise, the class is removed from the element and border is not seen.
PlanPicker notifies other CoffeePlan to unselect
- Vue 3 application
// PlanPicker
<script setup lang="ts">
const selectedPlan = ref('')
function handleSelectPlan(name: string) {
selectedPlan.value = name
}
function isSelected(plan: string) {
return selectedPlan.value === plan
}
</script>
<template>
<div class="plans">
<CoffeePlan
v-for="plan in plans"
:key="plan"
:name="plan"
:selected="isSelected(plan)"
@selectedPlan="handleSelectPlan"
>
</CoffeePlan>
</div>
</template>
When CoffeePlan emits the selectedPlan event, the handleSelectedPlan updates the selectedPlan ref. The isSelected function determines whether or not a coffee plan is selected. Then, the function assigns the result to the selected input of the CoffeePlan component. When the plan is selected, the CSS adds a border to it. Otherwise, the coffee plan does not display the border.
- SvelteKit application
<script lang="ts">
let selectedCoffeePlan = $state('');
const selectedPlan = (name: string) => (selectedCoffeePlan = name);
const isSelected = (plan: string) => selectedCoffeePlan === plan;
</script>
<div class="plans">
{#each plans as plan (plan)}
<CoffeePlan name={plan} {selectedPlan} selected={isSelected(plan)} />
{/each}
</div>
When CoffeePlan emits the selectedPlan event, the selectedPlan updates the selectedCoffeePlan rune. The isSelected function determines whether or not a coffee plan is selected. Then, the function assigns the result to the selected input of the CoffeePlan component. When the plan is selected, the CSS adds a border to it. Otherwise, the coffee plan does not display the border.
- Angular application
@Component({
selector: 'app-plan-picker',
})
export class PlanPickerComponent {
selectedPlan = signal('');
handleSelectPlan(name: string) {
this.selectedPlan.set(name);
}
isPlanSelected(planName: string): boolean {
return this.selectedPlan() === planName;
}
}
<div class="plans">
@for (plan of plans(); track plan) {
<app-coffee-plan
[name]="plan"
(selectedPlan)="handleSelectPlan($event)"
[selected]="isPlanSelected(plan)"
/>
}
</div>
When PlanPickerComponent emits the selectedPlan event, the handleSelectPlan method updates the selectedPlan signal. The isPlanSelected method determines whether or not a coffee plan is selected. Then, the method assigns the result to the selected input of the CoffeePlan component. When the plan is selected, the CSS adds a border to it. Otherwise, the coffee plan does not display the border.
Conclusions
We have successfully added new prop and component event to communicate between the CoffeePlan component and thePlanPicker component. The PlanPicker derives the value of theselected and passes it to other CoffeePlan components to enable or disable CSS class dynamically.
Top comments (0)