demo D277
Open a Microsoft Project plan in Angular
Read a Project .xml straight into a schedule, tasks, links, progress and people intact, drawn in the browser, and write it back out again, all with nothing sent to a server
importMSPDI · exportMSPDI
This reads a Microsoft Project XML file straight into a schedule, tasks, links, progress and people intact, draws it in the browser, and writes it back out, all with nothing sent to a server. It lets you open and save real Project plans in the page, so existing plans are not locked away in one tool.
This is the Angular version. A standalone component takes the whole configuration through one config input, surfaces grid events as outputs, and exposes the live grid on a getter for anything the inputs do not cover.
The configuration
import { Component, ViewChild, ElementRef, AfterViewInit, OnDestroy } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { createGantt, importMSPDI, exportMSPDI } from '@toclocoinc/lattice-grid/modules/gantt';
// mountSplit and the file round-trip are both outside what <lattice-gantt>
// covers, so this stays an imperative build against a ViewChild'd container.
@Component({
selector: 'app-root',
standalone: true,
template:
'<div style="margin-bottom:10px">' +
' <button (click)="save()">Save as Project .xml</button> <span>{{ status }}</span>' +
'</div>' +
'<div #plan style="height:420px;overflow:auto"></div>',
})
export class AppComponent implements AfterViewInit, OnDestroy {
@ViewChild('plan') planRef!: ElementRef<HTMLElement>;
private gantt: any;
private model: any;
status = '';
ngAfterViewInit() {
// importMSPDI reads a Project .xml into a { tasks, dependencies,
// resources, projectStart, calendar } model: the task tree, typed
// dependency links with their lag, per-task progress and the people on
// each task.
const xml = ''; // the text of a Project .xml document
this.model = importMSPDI(xml);
this.status = 'Read ' + this.model.tasks.length + ' tasks and ' + this.model.dependencies.length + ' links.';
this.gantt = createGantt({
tasks: this.model.tasks, dependencies: this.model.dependencies, resources: this.model.resources,
projectStart: this.model.projectStart, calendar: 'weekends', autoSchedule: true,
});
this.gantt.mountSplit(this.planRef.nativeElement, {
gridWidth: 300, rowHeight: 34, height: 420, zoom: 'day',
nonWorking: 'weekends', showArrows: true, showProgress: true,
});
}
save() {
// Write the plan back out as a Project file, so a round-trip keeps the
// tasks, typed links and assignments intact. gantt.toMSPDI() is the same
// over the plan.
const out = exportMSPDI({
tasks: this.model.tasks, dependencies: this.model.dependencies,
schedule: this.gantt.schedule, resources: this.model.resources,
});
const url = URL.createObjectURL(new Blob([out], { type: 'application/xml' }));
const a = document.createElement('a');
a.href = url; a.download = 'plan.xml'; a.click();
URL.revokeObjectURL(url);
}
ngOnDestroy() { this.gantt?.destroy(); }
}
bootstrapApplication(AppComponent);