Multiple Resource Assignment

This sample configures a SchedulerResourceDescriptor with allowMultiple enabled so one event can be assigned to multiple resources.

FlexScheduler API Reference | SchedulerDefinition API Reference

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; // import { CollectionView } from '@mescius/wijmo'; import * as wjcScheduler from '@mescius/wijmo.scheduler'; import { getDisplayDate, getEvents, getSpecialists } from './data'; // const avatarUrls = { 'Maya Chen': 'resources/avatars/maya-chen.png', 'Noah Reed': 'resources/avatars/noah-reed.png', 'Priya Shah': 'resources/avatars/priya-shah.png', 'Liam Brooks': 'resources/avatars/liam-brooks.png', }; document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', init) : init(); function init() { const specialists = getSpecialists(); const specialistMap = new Map(specialists.map((person) => [person.id, person])); renderLegend(specialists); new wjcScheduler.FlexScheduler('#theScheduler', { itemsSource: new CollectionView(getEvents()), resources: [ { fieldExpr: 'specialistIds', label: 'Specialists', dataSource: specialists, allowMultiple: true, valueExpr: 'id', displayExpr: 'name', backgroundColorExpr: 'color', foreColorExpr: 'textColor', }, ], definition: getDefinition((ctx, element) => renderEvent(ctx, element, specialistMap)), displayDate: getDisplayDate(), workHours: [{ start: '08:00', end: '18:00' }], toolbarViewItems: [{ name: 'Day', key: wjcScheduler.SchedulerBuiltInViewItem.Day }], }); } function getDefinition(itemTemplate) { return { key: 'multiAssignment', rows: { bindings: [ { type: wjcScheduler.SchedulerAxisBindingType.Time, unit: wjcScheduler.SchedulerTimeUnit.Hour, visible: (args) => args.hour >= 8 && args.hour < 18, }, ], }, columns: { bindings: [ { type: wjcScheduler.SchedulerAxisBindingType.Resource, fieldExpr: 'specialistIds', }, ], }, cellSemantic: wjcScheduler.SchedulerCellSemantic.Timed, itemTemplate, }; } function renderEvent(ctx, element, specialists) { const item = ctx.item; const header = document.createElement('span'); const icon = document.createElement('i'); const avatars = document.createElement('span'); header.className = 'assignment-title'; icon.className = `bi ${getTypeIcon(item.type)}`; icon.setAttribute('aria-hidden', 'true'); header.append(icon, document.createTextNode(item.subject)); avatars.className = 'event-avatars'; item.specialistIds.forEach((id) => { const person = specialists.get(id); const avatar = document.createElement('span'); avatar.className = 'event-avatar'; avatar.title = `${person.name}, ${person.role}`; appendAvatarImage(avatar, person); avatars.appendChild(avatar); }); element.textContent = ''; element.append(header, avatars); return null; } function getTypeIcon(type) { switch (type) { case 'installation': return 'bi-router'; case 'review': return 'bi-clipboard-check'; case 'handoff': return 'bi-person-check'; } } function renderLegend(specialists) { const legend = document.querySelector('#peopleLegend'); legend.replaceChildren(...specialists.map((person) => { const item = document.createElement('li'); const avatar = document.createElement('span'); avatar.className = 'person-avatar'; avatar.style.backgroundColor = person.color; appendAvatarImage(avatar, person); item.append(avatar, document.createTextNode(person.name)); return item; })); } function appendAvatarImage(element, person) { const image = document.createElement('img'); image.src = avatarUrls[person.name] || ''; image.alt = person.name; image.addEventListener('error', () => { element.textContent = person.initials; }, { once: true }); element.appendChild(image); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo Scheduler Multi-assignment</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/systemjs@0.21.5/dist/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script>System.import('./src/app');</script> </head> <body> <main class="container-fluid"> <div class="assignment-toolbar"> <div> <strong><i class="bi bi-people-fill" aria-hidden="true"></i> Shared assignments</strong> <span>One event can be assigned to multiple specialists.</span> </div> <ul id="peopleLegend" class="people-legend" aria-label="Specialists"></ul> </div> <div id="theScheduler"></div> </main> </body> </html>
function date(hour, minute = 0) { return new Date(2026, 4, 13, hour, minute); } export function getDisplayDate() { return date(9); } export function getSpecialists() { return [ { id: 1, name: 'Maya Chen', initials: 'MC', role: 'Network', color: '#2563eb', textColor: '#ffffff' }, { id: 2, name: 'Noah Reed', initials: 'NR', role: 'Field ops', color: '#0f766e', textColor: '#ffffff' }, { id: 3, name: 'Priya Shah', initials: 'PS', role: 'Customer', color: '#7c3aed', textColor: '#ffffff' }, { id: 4, name: 'Liam Brooks', initials: 'LB', role: 'Safety', color: '#a3460a', textColor: '#ffffff' }, ]; } export function getEvents() { return [ { id: 1, subject: 'Branch Installation', start: date(8, 30), end: date(10), specialistIds: [1, 2], type: 'installation', }, { id: 2, subject: 'Safety Review', start: date(10, 30), end: date(12), specialistIds: [2, 4], type: 'review' }, { id: 3, subject: 'Customer Handoff', start: date(13), end: date(14, 30), specialistIds: [1, 3], type: 'handoff', }, { id: 4, subject: 'Commissioning', start: date(15), end: date(16, 30), specialistIds: [1, 2, 3], type: 'installation', }, ]; }
body { color: #1f2937; font-size: 15px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; user-select: none; margin: 0; } .container-fluid { box-sizing: border-box; margin: 0; padding: 5px; } .assignment-toolbar { display: flex; align-items: center; gap: 10px 20px; min-height: 54px; margin-bottom: 6px; padding: 6px 10px; border: 1px solid #dbe2ea; background: #f8fafc; } .assignment-toolbar > div { display: flex; flex-direction: column; gap: 2px; } .assignment-toolbar strong { display: flex; align-items: center; gap: 7px; } .assignment-toolbar > div > span { color: #64748b; font-size: 12px; } .people-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0 0 0 auto; padding: 0; list-style: none; } .people-legend li { display: flex; align-items: center; gap: 6px; color: #475569; font-size: 12px; } .person-avatar, .event-avatar { display: inline-grid; place-items: center; border-radius: 50%; color: #ffffff; font-weight: 700; } .person-avatar { width: 25px; height: 25px; font-size: 10px; } .person-avatar img, .event-avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; } .assignment-title { display: flex; align-items: center; gap: 5px; overflow: hidden; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; } .event-avatars { display: flex; margin-top: 4px; padding-left: 5px; } .event-avatar { width: 22px; height: 22px; margin-left: -5px; border: 2px solid rgb(255 255 255 / 80%); background: rgb(15 23 42 / 45%); font-size: 8px; } #theScheduler { height: calc(100vh - 62px); min-height: 600px; } @media (max-width: 760px) { .assignment-toolbar { align-items: flex-start; flex-direction: column; } .people-legend { margin-left: 0; } #theScheduler { height: 680px; min-height: 680px; } } input, textarea, [contenteditable="true"] { user-select: text; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true, }, meta: { '*.css': { loader: 'css' }, }, paths: { // paths serve as alias 'npm:': 'node_modules/', 'cdn:': 'https://cdn.mescius.io/demoapps/packages/wijmojs/5.20262.60-rc/', 'pcdn:': 'https://cdn.jsdelivr.net/npm/', }, // map tells the System loader where to look for things map: { '@mescius/wijmo': 'cdn:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'cdn:@mescius/wijmo.input/index.js', '@mescius/wijmo.scheduler': 'cdn:@mescius/wijmo.scheduler/index.js', '@mescius/wijmo.styles': 'cdn:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'cdn:@mescius/wijmo.cultures', jszip: 'pcdn:jszip@3.8.0/dist/jszip.js', 'bootstrap.css': 'pcdn:bootstrap@5.3.8/dist/css/bootstrap.min.css', css: 'pcdn:systemjs-plugin-css@0.1.37/css.js', 'plugin-babel': 'pcdn:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'pcdn:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', }, packages: { src: { defaultExtension: 'js', }, node_modules: { defaultExtension: 'js', }, }, }); })(this);