Lattice Grid Buy a licence

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.

Building…
Loading a live grid…

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);