On day 24, I create an Alert Bar component to show or hide the close button, apply a new style to the alerts, and change their direction.
Create a Close SVG Icon
Vue 3 application
Create an icons/CloseIcon.vue
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="#0F1729"></path>
</svg>
</template>
Display the close icon in the alert component
<script setup lang="ts">
import CloseIcon from '@/icons/CloseIcon.vue'
</script>
<template>
... omitted to save space ...
<div>
<button class="btn btn-sm btn-primary" title="Close button" @click="closeAlert">
<CloseIcon />
</button>
</div>
</template>
Import the CloseIcon
and add the <CloseIcon>
within the <template>
tags to render the close icon on the button.
SvelteKit application
Create a lib/icons/close-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="#0F1729"></path>
</svg>
Display the close icon in the alert component
<script lang="ts">
import CloseIcon from '$lib/icons/close-icon.svelte'
</script>
... omitted to save space ...
<div>
<button class="btn btn-sm btn-primary" title="Close button" onclick={closeAlert}>
<CloseIcon />
</button>
</div>
Import the CloseIcon
and add the <CloseIcon />
in the HTML template to render the close icon on the button.
Angular 20 application
Create a CloseIconComponent
@Component({
selector: 'app-close-icon',
template: `
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="#0F1729"></path>
</svg>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CloseIconComponent {}
Display the close icon in the alert component
import { NgComponentOutlet } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';
import { AlertType } from '../alert.type';
import { CloseIconComponent, ErrorIconComponent, InfoIconComponent, SuccessIconComponent, WarningIconComponent } from '../icons/icon.component';
@Component({
selector: 'app-alert',
imports: [NgComponentOutlet, CloseIconComponent],
template: `
... omitted to save space ...
<div>
<button class="btn btn-sm btn-primary" title="Close button" (click)="closeAlert()">
<app-close-icon />
</button>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {}
Import the CloseIconComponent
and register it to the imports
array of the @Component
decorator.
In the inline template, use the app-close-icon
selector to render the close icon on the button.
Add Show/Hide checkbox to the AlertBar Component
Vue 3 application
Since Vue 3.4, the recommended approach of two-way data binding is using the defineModel
macro.
<script setup lang="ts">
const hasCloseButton = defineModel<boolean>('hasCloseButton', { default: true })
</script>
Declare a defineModel<boolean>
and assign the value to the hasCloseButton
ref. { default: true }
means the default value of the ref is true The checkbox is checked and the alert component should show a close button.
<template>
<div>
<p class="mb-[0.75rem]">
<span>Has close button? </span>
<input type="checkbox" class="mr-[0.5rem]" v-model="hasCloseButton" />
</p>
</div>
</template>
In the template, the v-model
directive is added to the checkbox input and binded to hasCloseButton
. The hasCloseButton
enables 2-way data binding and passes the value up to the parent component, which is the AlertList
component.
SvelteKit application
In Svelte 5, two-way binding is done with the $bindable
function. Moreover, $bindable
must be used with $props
together. We will update the Prop
type to add hasCloseButton
and destructure the property from the $prop()
call.
type Props = {
hasCloseButton: boolean;
}
let {
hasCloseButton = $bindable(),
}: Props = $props();
<div>
<p class="mb-[0.75rem]">
<span>Has close button?</span>
<input type="checkbox" class="mr-[0.5rem]" bind:checked={hasCloseButton} />
</p>
</div>
In the template, the bind:checked
attribute of the checkbox is binded to hasCloseButton
. The hasCloseButton
enables 2-way data binding and passes the value up to the parent component, which is the AlertList
component.
Angular 20 application
In Angular, two-way binding is achieved by the the model
Writable signal.
import { ChangeDetectionStrategy, Component, input, model } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-alert-bar',
imports: [],
template: `
<div>
<p class="mb-[0.75rem]">
<span>Has close button? </span>
<input type="checkbox" class="mr-[0.5rem]" [(ngModel)]="hasCloseButton" />
</select>
</p>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertBarComponent {
hasCloseButton = model<boolean>(true);
}
Declare a model<boolean>(true)
and assign the value to the hasCloseButton
signal. The default value of model
is true, so the checkbox is checked and the alert component should show a close button.
Pass hasCloseButton up to the AlertList Component
Vue 3 application
<script setup lang="ts">
import { computed, ref } from 'vue';
const hasCloseButton = ref(true)
</script>
Declare a hasCloseButton
ref in the AlertList
component to receive the value from the child.
<template>
<h2>Alert Components (Vue ver.)</h2>
<AlertBar
v-model:hasCloseButton="hasCloseButton"
/>
</template>
A Vue component allows multiple v-model
bindings.
v-model:hasCloseButton="hasCloseButton"
binds the child's hasCloseButton
to the hasCloseButton
ref.
SvelteKit application
<script lang="ts">
import AlertBar from './alert-bar.svelte';
let hasCloseButton = $state(true);
</script>
Declare a hasCloseButton
rune in the AlertList
component to receive the value from the child.
<AlertBar {configs}
bind:hasCloseButton={hasCloseButton}
/>
bind:hasCloseButton={hasCloseButton}
means the AlertList
component listens to what the hasCloseButton
prop of the AlerBar
component has to say.
Angular 20 application
import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
import { AlertBarComponent } from '../alert-bar/alert-bar.component';
@Component({
selector: 'app-alert-list',
imports: [AlertBarComponent],
template: `
<app-alert-bar
[(hasCloseButton)]="hasCloseButton"
/>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertListComponent {
hasCloseButton = signal<boolean>(true);
}
Declare a hasCloseButton
signal in the AlertList
component to receive the value from the child.
Use banana-in-the-box syntax, [(expression)]
, to perform two-way binding for the AlertList
and AlertBar
components.
Show/Hide Close Button in the Alert Component
Vue 3 application
const hasCloseButton = ref(true)
const alertConfig = computed(() => ({
hasCloseButton: hasCloseButton.value,
}))
Define a alertConfig
computed ref to create an object to store the value of hasCloseButton.value
.
<template>
<Alert v-for="{ type, message } in alerts"
class="mb-[0.75rem]"
:key="type"
:type="type"
:alertConfig="alertConfig">
{{ message }}
</Alert>
</template>
Pass the alertConfig
to the alertConfig
prop of the Alert
component.
<script setup lang="ts">
import { computed, ref } from 'vue'
import CloseIcon from '@/icons/CloseIcon.vue'
type Props = {
alertConfig: {
hasCloseButton: boolean
},
type: string
}
const { type, alertConfig } = defineProps<Props>()
... omit the template codes ...
</script>
In the Alert
component, add the new alertConfig
property to the Prop
type. Destructure the alertConfig
from the defineProps
macro.
<template>
<div role="alert" :class="alertClasses" v-if="!closed">
<component :is="icon" />
<span><slot></slot></span>
<div v-if="alertConfig.hasCloseButton">
<button class="btn btn-sm btn-primary" alt="Close button" @click="closeAlert">
<CloseIcon />
</button>
</div>
</div>
</template>
If alertConfig.hasCloseButton
is true, the close button will be shown. Otherwise, it is hidden.
SvelteKit application
<script lang="ts">
import AlertBar from './alert-bar.svelte';
import Alert from './alert.svelte';
import type { AlertMessage } from './alert.type';
let hasCloseButton = $state(true);
</script>
Declare a hasCloseButton
rune in the AlertList
component to receive the value from the child.
{#each alerts as alert (alert.type) }
<Alert {alert} {hasCloseButton} />
{/each}
Pass the hasCloseButton
rune to the hasCloseButton
prop of the Alert
component.
<AlertBar {configs}
bind:hasCloseButton={hasCloseButton}
/>
bind:hasCloseButton={hasCloseButton}
means the AlertList
component listens to what the hasCloseButton
prop of the AlertBar
component has to say.
<script lang="ts">
type Props = {
... other properties ...
hasCloseButton: boolean;
}
const {
hasCloseButton,
}: Props = $props();
</script>
In the Alert
component, add a hasCloseButton
property to the Prop
type and destructure the property from the object.
{#if !closed}
<div role="alert" class={alertClasses}>
... omit the template code...
{#if hasCloseButton}
<div>
<button class="btn btn-sm btn-primary" title="Close button" onclick={closeAlert}>
<CloseIcon />
</button>
</div>
{/if}
</div>
{/if}
If hasCloseButton
is true, the button is rendered. Otherwise, the button is hidden.
Angular 20 application
import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
import { AlertBarComponent } from '../alert-bar/alert-bar.component';
@Component({
selector: 'app-alert-list',
imports: [AlertBarComponent],
template: `
<app-alert-bar
[(hasCloseButton)]="hasCloseButton"
/>
@for (alert of alerts(); track alert.type) {
<app-alert [type]="alert.type"
[alertConfig]="alertConfig()"
>
{{ alert.message }}
</app-alert>
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertListComponent {
hasCloseButton = signal<boolean>(true);
alertConfig = computed(() => ({
hasCloseButton: this.hasCloseButton(),
}));
}
Declare a hasCloseButton
signal in the AlertList
component to receive the value from the child.
Use banana-in-the-box syntax, [(expression)]
, to perform two-way binding for the AlertList
and AlertBar
components.
Define a alertConfig
computed signal to create an object to store the value of the hasCloseButton
signal.
Assign the value of the alertConfig
computed signal to the input signal of AlertComponent
.
import { NgComponentOutlet } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';
import { AlertType } from '../alert.type';
import { CloseIconComponent, ErrorIconComponent, InfoIconComponent, SuccessIconComponent, WarningIconComponent } from '../icons/icon.component';
@Component({
selector: 'app-alert',
imports: [NgComponentOutlet, CloseIconComponent],
template: `
@if (!closed()) {
... omit the template codes ...
@if (alertConfig().hasCloseButton) {
<div>
<button class="btn btn-sm btn-primary" title="Close button" (click)="closeAlert()">
<app-close-icon />
</button>
</div>
}
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {
type = input.required<AlertType>();
alertConfig = input.required<{
hasCloseButton: boolean
}>();
}
In the AlertComponent
, declare an alertConfig
required input signal.
If alertConfig().hasCloseButton
is true, the button is rendered. Otherwise, the button is hidden.
Next, we will repeat the same procedure to add style and direction dropdown lists to change the styling of the alert component.
Add Alert Style and Direction dropdown lists
Vue 3 application
<script setup lang="ts">
type Props = {
config: {
styleLabel: string
styles: { text: string, value: string }[]
directionLabel: string
directions: { text: string, value: string }[]
},
}
const props = defineProps<Props>()
const { config } = props
</script>
Declare a Prop in the AlertBar
component to accept the style label, style dropdown list, direction label, and direction dropdown list.
const style = defineModel<string>('style', { default: 'color' })
const direction = defineModel<string>('direction', { default: 'horizontal' })
Declare style
and direction
ref for two-way data binding. The default value of style
is 'color' and the default value of direction
is 'horizontal'.
<template>
<div>
<p class="mb-[0.75rem]">
<span>{{ config.styleLabel }} </span>
<select class="select select-info mr-[0.5rem]" v-model="style">
<option v-for="{value, text} in config.styles" :key="value" :value="value">
{{ text }}
</option>
</select>
<span>{{ config.directionLabel }} </span>
<select class="select select-info mr-[0.5rem]" v-model="direction">
<option v-for="{ value, text } in config.directions" :key="value" :value="value">
{{ text }}
</option>
</select>
</p>
</div>
</template>
Use v-for
to iterate config.styles
and config.directions
to populate the option items of the select dropdown.
SvelteKit application
<script lang="ts">
import { capitalize } from './capitalize';
import OpenIcon from './icons/open-icon.svelte';
type Props = {
configs: {
styleLabel: string
styles: { text: string, value: string }[]
directionLabel: string
directions: { text: string, value: string }[]
};
hasCloseButton: string;
style: string;
direction: string;
}
let {
hasCloseButton = $bindable(),
style = $bindable(),
direction = $bindable(),
closedNotifications = $bindable(),
configs
}: Props = $props();
</script>
Add configs
, style
and direction
to the Props
type.
Destructure style
and direction
from $props()
and bind them to the AlertList
component using $bindable
.
<p class="mb-[0.75rem]">
<span>{ configs.styleLabel } </span> { style }
<select class="select select-info mr-[0.5rem]" bind:value={style}>
{#each configs.styles as s (s.value) }
<option value={s.value}>
{ s.text }
</option>
{/each}
</select>
<span>{ configs.directionLabel } </span>
<select class="select select-info mr-[0.5rem]" bind:value={direction}>
{#each configs.directions as d (d.value)}
<option value={d.value}>
{ d.text }
</option>
{/each}
</select>
</p>
bind:value={style}
binds style
rune to the style dropdown. bind:value={direction}
bind the direction
rune to the direction dropdown.
Use #each
to iterate the styles
and directions
lists to populate the dropdown lists.
Angula 20 application
@Component({
selector: 'app-alert-bar',
imports: [FormsModule],
template: `... inline template explained below ...`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertBarComponent {
config = input.required<{
styleLabel: string
styles: { text: string, value: string }[]
directionLabel: string
directions: { text: string, value: string }[]
}>();
hasCloseButton = model<boolean>(true);
style = model<string>('color');
direction = model<string>('horizontal');
}
The AlertBarComponent
declares a required input signal for the labels and dropdown lists.
Similarly, style
and direction
are declared model
signals for two-way binding.
The component also imports FormsModule
to bind the model
to the [(ngModel)]
in the inline template.
<div>
@let c = config();
<p class="mb-[0.75rem]">
<span>{{ c.styleLabel }} </span>
<select class="select select-info mr-[0.5rem]" [(ngModel)]="style">
@for (style of c.styles; track style.value) {
<option [ngValue]="style.value">
{{ style.text }}
</option>
}
</select>
<span>{{ c.directionLabel }} </span>
<select class="select select-info mr-[0.5rem]" [(ngModel)]="direction">
@for (direction of c.directions; track direction.value) {
<option [ngValue]="direction.value">
{{ direction.text }}
</option>
}
</select>
</p>
</div>
[(ngModel)]="style"
binds style
to the style dropdown. Similarly, [(ngModel)]="direction"
binds direction
to the direction dropdown.
The @for
loop iterates the styles and directions lists to display the value/text items.
Perform two-way binding
Vue 3 Application
The config
ref defines the labels and items of the style and direction.
<script setup lang="ts">
import type { AlertType } from '@/types/alert-type';
import { computed, ref } from 'vue';
import Alert from './Alert.vue';
import AlertBar from './AlertBar.vue';
const hasCloseButton = ref(true)
const style = ref('color');
const direction = ref('horizontal')
const alertConfig = computed(() => ({
style: style.value,
direction: direction.value,
hasCloseButton: hasCloseButton.value,
}))
const config = ref({
styleLabel: "Alert styles:",
styles: [
{ text: 'Default', value: 'color' },
{ text: 'Soft', value: 'soft' },
{ text: 'Outline', value: 'outline' },
{ text: 'Dash', value: 'dash' }
],
directionLabel: "Alert direction:",
directions: [
{ text: 'Horizontal', value: 'horizontal' },
{ text: 'Vertical', value: 'vertical' },
]
})
</script>
Similar to hasCloseButton
, style
and direction
pass the values from the AlertBar
component to the AlertList
component. Then, the alertConfig
computed ref returns the value of style
and direction
. The alertConfig
passes the new values to style the Alert
component.
<AlertBar
:config="config"
v-model:hasCloseButton="hasCloseButton"
v-model:style="style"
v-model:direction="direction"
/>
The AlertBar
component receives the config
prop. The v-model:style="style"
binds the style
ref of the AlertBar
component to style
ref of the AlertList
component. Similarly, v-model:direction="direction"
binds the direction
ref of the AlertBar
component to the direction
ref of the AlertList
component.
SvelteKit Application
The labels and items of style and direction are defined in the config
rune.
<script lang="ts">
import AlertBar from './alert-bar.svelte';
import Alert from './alert.svelte';
const configs = $state(... same object...);
let hasCloseButton = $state(true);
let style = $state('color');
let direction = $state('horizontal');
</script>
Similar to hasCloseButton
, add style
and direction
runes to the AlertList
component.
<AlertBar {configs}
bind:hasCloseButton={hasCloseButton}
bind:style={style}
bind:direction={direction}
/>
The AlertBar
component receives the config
prop to populate the dropdown. The AlertBar
uses the bind
syntax to pass the style
and direction
to the AlertList
component.
{#each filteredNotification as alert (alert.type) }
<Alert {alert} {alertMessage} {notifyClosed} {hasCloseButton} {style} {direction} />
{/each}
Pass the new prop values, style
and direction
, to the Alert
component.
Angular 20 Application
The labels and items of style and direction are defined in the config
signal.
import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
import { AlertComponent } from '../alert/alert.component';
import { AlertBarComponent } from '../alert-bar/alert-bar.component';
@Component({
selector: 'app-alert-list',
imports: [AlertComponent, AlertBarComponent],
template: `...inline template shown below ...`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertListComponent {
style = signal<string>('color');
direction = signal<string>('horizontal');
hasCloseButton = signal<boolean>(true);
config = signal({... same object ... });
alertConfig = computed(() => ({
hasCloseButton: this.hasCloseButton(),
style: this.style(),
direction: this.direction()
}));
}
Similar to hasCloseButton
, add style
and direction
signal to the AlertList
component. The alertConfig
computed signal returns the value of the style
and direction
signals.
<app-alert-bar
[config]="config()"
[(style)]="style"
[(direction)]="direction"
[(hasCloseButton)]="hasCloseButton"
/>
The AlertBar
component receives the config
input to populate the dropdown. The AlertBar
uses the banana-in-the-box syntax [(expression)]
to bind the style
and direction
models of the AlertBar
component to the style
and direction
signals of the AlertList
component.
[(style)]="style"
- The first style
is the style
model of the AlertBarComponent
and the second style
is the style
signal of the AlertListComponent
.
[(direction)]="direction"
- The first direction
is the direction
model of the AlertBarComponent
and the second direction
is the direction
signal of the AlertListComponent
.
Apply Style and Direction to Alert Component
Vue 3 Application
type Props = {
alertConfig: {
hasCloseButton: boolean
style: string
direction: string
},
type: string
}
const { type, alertConfig } = defineProps<Props>()
In the Props
type of the Alert
component, add style
and direction
to the alertConfig
property.
const alertColor = computed(() => ({
info: 'alert-info',
warning: 'alert-warning',
error: 'alert-error',
success: 'alert-success'
}[type]))
const alertStyle = computed(() => ({
color: '',
dash: 'alert-dash',
soft: 'alert-soft',
outline: 'alert-outline'
}[alertConfig.style]))
const alertDirection = computed(() => ({
horizontal: 'alert-horizontal',
vertical: 'alert-vertical',
}[alertConfig.direction]))
const alertClasses = computed(() => `alert ${alertColor.value} ${alertStyle.value} ${alertDirection.value}`)
Create the alertStyle
computed ref to derive the style class. Create the alertDirection
computed ref to derive the direction class.
The alertClasses
computed ref concatenates the style classes of the alert component.
<template>
<div role="alert" :class="alertClasses" v-if="!closed">
<!-- HTML button -->
</div>
</template>
The alertClasses
binds the new styles to the class
attribute to change the direction, border style and color.
SvelteKit Application
type Props = {
hasCloseButton: boolean;
style: string;
direction: string;
}
const {
hasCloseButton,
direction,
style
}: Props = $props();
In the Props
type of the Alert
component, add style
and direction
properties.
const alertColor = $derived.by(() => ({
info: 'alert-info',
success: 'alert-success',
warning: 'alert-warning',
error: 'alert-error',
}[alert.type]));
const alertDirection = $derived.by(() => ({
horizontal: 'alert-horizontal',
vertical: 'alert-vertical',
}[direction]));
const alertStyle = $derived.by(() => ({
color: '',
soft: 'alert-soft',
outline: 'alert-outline',
dash: 'alert-dash',
}[style]));
const alertClasses = $derived(`alert ${alertColor} ${alertDirection} ${alertStyle} mb-[0.75rem]`);
Create the alertStyle
derived rune to derive the style class. Create the alertDirection
derived rune to derive the direction class.
The alertClasses
derived rune concatenates the style classes of the alert component.
{#if !closed}
<div role="alert" class={alertClasses}>
<!-- HTML button -->
</div>
{/if}
The alertClasses
binds the new styles to the class
attribute to change the direction, border style and color.
Angular 20 Application
@Component({
selector: 'app-alert',
imports: [NgComponentOutlet, CloseIconComponent],
template: `... inline template ...`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {
type = input.required<AlertType>();
alertConfig = input.required<{
hasCloseButton: boolean
style: string
direction: string
}>();
alertColor = computed(() => {
return {
info: 'alert-info',
warning: 'alert-warning',
error: 'alert-error',
success: 'alert-success'
}[this.type()]
});
alertStyle = computed(() => {
return {
color: '',
dash: 'alert-dash',
soft: 'alert-soft',
outline: 'alert-outline'
}[this.alertConfig().style]
});
alertDirection = computed(() => {
return {
horizontal: 'alert-horizontal',
vertical: 'alert-vertical',
}[this.alertConfig().direction]
});
alertClasses = computed(() => `alert ${this.alertColor()} ${this.alertStyle()} ${this.alertDirection()}`);
}
Add style
and direction
to the alertConfig
required input signal.
Create the alertStyle
computed signal to derive the style class. Create the alertDirection
computed signal to derive the direction class.
The alertClasses
computed signal concatenates the style classes of the alert component.
@if (!closed()) {
<div role="alert" class="mb-[0.75rem]" [class]="alertClasses()">
<!-- HTML button -->
</div>
}
The alertClasses
binds the new styles to the class
attribute to change the direction, border style and color.
Now, users can select values in the AlertBar
component to show/hide the close button, change the border style, direction, and color of the alerts.
Github Repositories
- Vue 3: https://github.com/railsstudent/vue-alert-component/blob/main/src/components/AlertBar.vue
- Svelte 5: https://github.com/railsstudent/svelte-alert-component
- Angular 20: https://github.com/railsstudent/angular-alert-component
Github Pages
- Vue 3: https://railsstudent.github.io/vue-alert-component
- Svelte 5: https://railsstudent.github.io/svelte-alert-component
- Angular 20: https://railsstudent.github.io/angular-alert-component
Resources
- Vue v-model:https://vuejs.org/guide/components/v-model
- Svelte 5 \$bindable: https://svelte.dev/docs/svelte/\$bindable
- Angular model: https://angular.dev/api/core/model
Resources
- Vue multiple v-model bindings: https://vuejs.org/guide/components/v-model#multiple-v-model-bindings
- Svelte \$bindable: https://svelte.dev/docs/svelte/\$bindable
- Angular model: https://angular.dev/api/core/model
Top comments (0)