คู่มือนี้สร้างปฏิทินสำหรับจองห้องประชุม ประกอบด้วยไทม์ไลน์ที่มีแถวสำหรับแต่ละห้อง การจองที่ลากไปวางในตำแหน่งที่ต้องการได้ กฎที่ป้องกันไม่ให้ห้องเดียวกันถูกจองซ้ำ และไฟล์ที่เพิ่มการจองลงในปฏิทินใดก็ได้ โดยใช้ Scheduler จาก @needless-ui/angular ซึ่งใช้สัญญาอนุญาต MIT เช่นเดียวกับทุกส่วนของ Needless UI ไทม์ไลน์ทรัพยากรจึงใช้ได้ฟรีกับทุกแอป
ห้องและการจอง
ห้องคือทรัพยากร มี id มี title และข้อมูลอื่นใดก็ได้ใน data การจองคือกิจกรรมที่มีเวลาแบบ ISO ธรรมดา เช่น 2026-10-23T09:00 และมี id ของห้องอยู่ใน resource การจองหนึ่งครั้งระบุหลายห้องหรือหลายคนเป็นลิสต์ได้
แถวสำหรับแต่ละห้อง
ส่งทั้งสองอย่างให้ nui-scheduler พร้อม view="timeline" โดย dayStart และ dayEnd จำกัดวันไว้ที่เวลาเปิดทำการ snapMinutes เลื่อนการจองทีละ 15 นาที และ businessHours แรเงาช่วงเวลาที่ห้องปิด ส่วน nuiSchedulerResource วาดส่วนหัวของแต่ละแถว
<nui-scheduler
[events]="bookings()"
[resources]="rooms"
view="timeline"
dayStart="8"
dayEnd="19"
snapMinutes="15"
[businessHours]="hours"
editable
selectable
[allow]="free"
(slotSelect)="book($event)"
(eventChange)="move($event)"
(rangeChange)="range.set($event)"
>
<ng-template nuiSchedulerResource let-room>{{ room.title }}</ng-template>
</nui-scheduler>
<button nuiButton type="button" (click)="download()">Add to calendar</button>จองและย้าย
Scheduler จะไม่แก้ไขข้อมูลของคุณด้วยตัวเอง แต่จะรายงานสิ่งที่ผู้ใช้ทำ การลากผ่านแถวของห้องคือการเลือกเวลา และ slotSelect จะรายงานเวลานั้นพร้อมกับห้อง การลากการจองหรือปลายของการจองจะรายงานเวลาใหม่ใน eventChange รวมถึงห้องใหม่หากย้ายไปห้องอื่น อัปเดต signal แล้วไทม์ไลน์จะเปลี่ยนตาม
import { Component, signal } from '@angular/core';
import { NuiButton } from '@needless-ui/angular/button';
import {
NuiScheduler,
type NuiSchedulerChange,
type NuiSchedulerEvent,
NuiSchedulerResourceTemplate,
type NuiSchedulerSlot,
} from '@needless-ui/angular/scheduler';
import { downloadIcs } from './booking-ics';
@Component({
selector: 'app-bookings',
imports: [NuiButton, NuiScheduler, NuiSchedulerResourceTemplate],
templateUrl: './booking.html',
})
export class Bookings {
/** Rooms are resources; bookings are events, with their room in `resource`. */
protected readonly rooms = [
{ id: 'atlas', title: 'Atlas' },
{ id: 'boreal', title: 'Boreal' },
];
protected readonly bookings = signal<NuiSchedulerEvent[]>([]);
/** Open on weekdays from 8:00 to 19:00: other time is shaded. */
protected readonly hours = [{ days: [1, 2, 3, 4, 5], start: '08:00', end: '19:00' }];
/** The days on screen: load their bookings when it changes. */
protected readonly range = signal<{ start: string; end: string } | null>(null);
protected book({ start, end, resource }: NuiSchedulerSlot): void {
const booking = { id: crypto.randomUUID(), title: 'New booking', start, end, resource };
this.bookings.update((all) => [...all, booking]);
}
protected move({ event, start, end, resource }: NuiSchedulerChange): void {
this.bookings.update((all) =>
all.map((b) =>
b.id === event.id ? { ...b, start, end, resource: resource ?? b.resource } : b,
),
);
}
/** A room holds one booking at a time. */
protected readonly free = (time: NuiSchedulerChange | NuiSchedulerSlot): boolean => {
const id = 'event' in time ? time.event.id : null;
const room = 'event' in time ? (time.resource ?? time.event.resource) : time.resource;
return !this.bookings().some(
(b) => b.id !== id && b.resource === room && b.start < time.end && b.end > time.start,
);
};
protected download(): void {
downloadIcs(this.bookings(), 'bookings');
}
}ไม่มีการจองซ้ำ
Scheduler จะถาม allow ก่อนสร้างหรือย้ายการจอง หากคืนค่า false การลากจะแสดงเป็นสีแดงและปล่อยแล้วไม่มีอะไรเกิดขึ้น ส่วนเมื่อใช้คีย์บอร์ด Scheduler จะแจ้งว่าเวลานั้นไม่ว่าง ตัวอย่างนี้ตรวจการจองอื่นของห้องนั้น และควรตรวจซ้ำที่เซิร์ฟเวอร์ของคุณ เพราะเซิร์ฟเวอร์เป็นผู้ตัดสินสุดท้าย
โหลดเฉพาะวันที่แสดงอยู่
rangeChange รายงานวันแรกและวันสุดท้ายที่แสดง ครั้งแรกตอนเริ่มต้น และอีกครั้งทุกครั้งที่ผู้ใช้เปลี่ยนหน้า คุณจึงดึงเฉพาะการจองในช่วงนั้นได้ ด้วย httpResource หรือคำขอแบบใดก็ได้
เพิ่มลงในปฏิทิน
nuiSchedulerToIcs() เขียนการจองเป็นไฟล์ iCalendar ซึ่งนำเข้าได้ทั้งใน Google Calendar ใน Outlook และใน Apple Calendar ส่วน nuiSchedulerFromIcs() อ่านไฟล์แบบนี้กลับเข้ามา และแปลงเวลาจากเขตเวลาอื่นเป็นเขตเวลาของผู้ใช้
import { type NuiSchedulerEvent, nuiSchedulerToIcs } from '@needless-ui/angular/scheduler';
/** Saves events as an .ics file, which Google Calendar, Outlook and Apple Calendar import. */
export function downloadIcs(events: readonly NuiSchedulerEvent[], name: string): void {
const file = new Blob([nuiSchedulerToIcs(events, { name })], { type: 'text/calendar' });
const link = document.createElement('a');
link.href = URL.createObjectURL(file);
link.download = `${name}.ics`;
link.click();
URL.revokeObjectURL(link.href);
}คีย์บอร์ดและโปรแกรมอ่านหน้าจอ
ทั้งหมดนี้ใช้ได้โดยไม่ต้องใช้เมาส์ ไทม์ไลน์อยู่ในลำดับการกด Tab เพียงจุดเดียว ปุ่มลูกศรเลื่อนไปตามเวลาและห้อง Shift กับปุ่มลูกศรใช้เลือกเวลา และ Enter ใช้จองเวลานั้น เมื่ออยู่ที่การจอง Alt กับปุ่มลูกศรจะย้ายการจองไปยังเวลาหรือห้องอื่น การจองทุกรายการเป็นปุ่มที่มีชื่อตามหัวเรื่อง ห้อง วัน และเวลา
สำหรับห้องที่วางเคียงข้างกันในหนึ่งวัน การจองที่เกิดซ้ำ และตัวเลือกอื่น ๆ ทั้งหมด ดูได้ที่หน้าของ Scheduler และหน้า API