Table of Contents
- Create the AddCoffeePlan
- Import AddCoffeePlan to PlanPicker
- Conclusions
- Resources
- Github Repositories
On day 15, I extended the PlanPicker
component to have a AddCoffeePlan
component to add new coffee plans to the plan list. Then, the PlanPicker
component has two child components that are AddCoffeePlan
and CoffeePlan
.
Create the AddCoffeePlan
- Vue 3 application
Create a new components/AddCoffeePlan.vue
file.
<style scoped>
input {
padding: 0.5rem 0.75rem;
}
.add-plan-form {
display: flex;
align-items: center;
justify-content: space-between;
}
.add-plan-form input {
width: 70%;
border-radius: 3px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
border: 1px solid #f1f5f8;
color: #606f7b;
padding: 0.5rem 0.75rem;
box-sizing: border-box;
font-size: 1rem;
letter-spacing: 0.5px;
margin: 0.5rem 0;
}
style>
Add scoped styles to style the add coffee plan form. The script tag has scoped
attribute.
<script setup lang="ts">
import { ref } from 'vue';
const emit = defineEmits<{
(e: 'newCoffeePlan', name: string): void
}>();
const newPlan = ref('');
function addPlan() {
const trimmedPlan = newPlan.value.trim();
if (!trimmedPlan) {
return;
}
emit('newCoffeePlan', trimmedPlan);
newPlan.value = '';
}
</script>
The newPlan
ref stores the new coffee plan to be added to the coffee plan list.
The defineEmits
defines a custom newCoffeePlan
event that emits the value of newPlan
to the PlanPicker
component.
The addPlan
handles the form submit event, emits the newCoffeePlan
event, and clears newPlan
.
The template uses v-model.trim
to bind the textbox to the newPlan
ref.
The Add Plan
button is disabled unless users input at least 5 characters to the textbox.
When users submit the form, the addPlan
function validate the new coffee plan and emit the value to the parent component.
- SvelteKit application
Create a new lib/add-coffee-plan.svelte
file.
Create scoped styles in the add coffee plan form. By default, styles are scoped in a svelte component.
<style>
input {
padding: 0.5rem 0.75rem;
}
.add-plan-form {
display: flex;
align-items: center;
justify-content: space-between;
}
.add-plan-form input {
width: 70%;
border-radius: 3px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
border: 1px solid #f1f5f8;
color: #606f7b;
padding: 0.5rem 0.75rem;
box-sizing: border-box;
font-size: 1rem;
letter-spacing: 0.5px;
margin: 0.5rem 0;
}
style>
<script lang="ts">
interface Props {
addCoffeePlan: (plan: string) => void;
}
const { addCoffeePlan }: Props = $props();
let newPlan = $state('');
const addPlan = (e: SubmitEvent) => {
e.preventDefault();
const trimmedPlan = newPlan.trim();
if (!trimmedPlan) {
return;
}
addCoffeePlan(trimmedPlan);
newPlan = '';
}
</script>
The newPlan
rune stores the new coffee plan to be added to the coffee plan list.
The PlanPicker
component passes the addCoffeePlan
function to the AddCoffeePlan
component that is destructured from $props
. This function is used to emit the new coffee to the PlanPicker
component.
The addPlan
handles the following things
- listen to the form submit event,
- prevents loading the page
- notify the parent by calling the
addCoffeePlan
function - clears the rune.
The template uses bind:value
to bind the textbox to the newPlan
rune.
The Add Plan
button is disabled unless users input at least 5 characters to the textbox.
The form registers addPlan
to listen to the onsubmit
event. When users submit the form, the addPlan
uses the AddCoffeePlan
prop to pass the new value to the parent component.
- Angular 19 application
Use the Angular CLI to create a new AddCoffeePlanComponent
ng g c AddCoffeePlan
The component defines the CSS in a separate add-coffee-plan.component.css
file. Inline CSS styles are also supportted and it depends on the coding preferences.
input {
padding: 0.5rem 0.75rem;
}
.add-plan-form {
display: flex;
align-items: center;
justify-content: space-between;
}
.add-plan-form input {
width: 70%;
border-radius: 3px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
border: 1px solid #f1f5f8;
color: #606f7b;
padding: 0.5rem 0.75rem;
box-sizing: border-box;
font-size: 1rem;
letter-spacing: 0.5px;
margin: 0.5rem 0;
}
import { ChangeDetectionStrategy, Component, output, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-add-coffee-plan',
imports: [FormsModule],
template: `... inline template ...`,
styleUrl: './add-coffee-plan.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AddCoffeePlanComponent {
newPlan = signal('');
addCoffeePlan = output<string>();
addPlan() {
this.addCoffeePlan.emit(this.newPlan());
this.newPlan.set('');
}
}
The component imports FormsModule
so that ngSubmit
is available and signals can be two-way binding to the HTML elements.
The newPlan
signal stores the new coffee plan to be added to the coffee plan list.
addCoffeePlan
is an output function that emits the new value to the PlanPicker
component.
The addPlan
method handles the ngSubmit
event, prevents loading the page implicitely, emits the addCoffeePlan
custom event, and clears the newPlan
signal.
template: `
`
[(ngModel)]
two-way binding the newPlan
signal to the input. When users input new coffee plan to the text box, the newPlan
receives a new value.
The Add Plan
button uses the box syntax to bind an input to an attribute. [disabled]
is true when users input less than 5 characters to the textbox.
The ngSubmit
is a custom Angular event that fires when form submit occurs and the event handler method is addPlan
. addPlan
invokes the addCoffeePlan
output to emit the new value to the parent component.
Import AddCoffeePlan to PlanPicker
- Vue 3 application
Import AddCoffeePlan
to PlanPicker
<script setup lang="ts">
import AddCoffeePlan from './AddCoffeePlan.vue'
</script>
Use the AddCoffeePlan
in the template
class="plans">
@newCoffeePlan="(plan) => plans.push(plan)" />
The @newCoffeePlan
custom event calls an inline function to append the new plan to the coffee plan list.
- SvelteKit application
Import AddCoffeePlan
to PlanPicker
<script lang="ts">
import CoffeePlan from './coffee-plan.svelte';
</script>
Use the AddCoffeePlan
in the template
class="plans">
addCoffeePlan={(plan) => plans.push(plan)} />
The @newCoffeePlan
custom event also call an inline function to append the new plan to the coffee plan list.
- Angular 19 application
Import AddCoffeePlanComponent
to PlanPickerComponent
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { AddCoffeePlanComponent } from '../add-coffee-plan/add-coffee-plan.component';
import { CoffeePlanComponent } from '../coffee-plan/coffee-plan.component';
@Component({
selector: 'app-plan-picker',
imports: [CoffeePlanComponent, AddCoffeePlanComponent],
template: `... inline template ...`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlanPickerComponent {
plans = signal(['The Single', 'The Curious', 'The Addict', 'The Hacker']);
addPlan(name: string) {
this.plans.update((plans) => [...plans, name]);
}
}
Add the AddCoffeePlanComponent
component to the imports
array.
The addPlan
method uses the update
method of the Signal
API to append the new coffee plan to the plans
signal.
class="plans">
(addCoffeePlan)="addPlan($event)" />
The addCoffeePlan
custom event uses the special $event
to pass the new coffee plan to the addPlan
method to append the new coffee name.
Conclusions
We have successfully created an AddCoffeePlan
component that can add new coffee plan to the coffee plan list in the PlanPicker
component.
Resources
Github Repositories