Task Operations

You can add, remove, indent, outdent, link, or unlink tasks. Try performing the tasks in the demo below to see how they change the GanttSheet chart.

Add Task You can add tasks in different ways: addTasks (data: number | ITaskData | ITaskData[] = 1, level?: number): GC.Spread.Sheets.GanttSheet.Task[]; insertTasks (taskNumber: number, data: number | ITaskData | ITaskData[] = 1, level?: number): GC.Spread.Sheets.GanttSheet.Task[]; Remove Task Tasks can also be removed in different ways: removeTasks (taskNumbers: number | number[]): void; removeTasksByRange (taskNumber: number, count = 1): void; Indent/Outdent Task You can also indent tasks with specific API: indentTasks (taskNumbers: number | number[]): void; indentTasksByRange (taskNumber: number, count = 1): void; outdentTasks (taskNumbers: number | number[]): void; outdentTasksByRange (taskNumber: number, count = 1): void;
/*REPLACE_MARKER*/ /*DO NOT DELETE THESE COMMENTS*/ import { Component, NgModule, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { SpreadSheetsModule } from '@mescius/spread-sheets-angular'; import GC from '@mescius/spread-sheets'; import "@mescius/spread-sheets-tablesheet"; import "@mescius/spread-sheets-ganttsheet"; import './styles.css'; declare var SplitView: any; @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { spread: GC.Spread.Sheets.Workbook; ganttSheet: GC.Spread.Sheets.GanttSheet; hostStyle = { width: '100%', height: '100%' }; initSpread($event: any) { this.spread = $event.spread; let spread = this.spread; spread.suspendPaint(); spread.clearSheets(); this.initDataSource(spread); this.initGanttSheet(spread); spread.resumePaint(); initSplitView(spread); } initDataSource(spread) { var tableName = "Gantt_Id"; var baseApiUrl = getBaseApiUrl(); var apiUrl = baseApiUrl + "/" + tableName; var dataManager = spread.dataManager(); var myTable = dataManager.addTable("myTable", { batch: true, remote: { read: { url: apiUrl }, batch: { url: apiUrl + "Collection" } }, schema: { hierarchy: { type: "Parent", column: "parentId" }, columns: { id: { isPrimaryKey: true }, taskNumber: { dataType: "rowOrder" } } } }); this.myTable = myTable; } initGanttSheet(spread) { var ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet); this.ganttSheet = ganttSheet; var view = this.myTable.addView("ganttView", [ { value: "id", caption: "Id", width: 40 }, { value: "taskNumber", caption: "NO.", width: 60 }, { value: "name", caption: "Task Name", width: 200 }, { value: "duration", caption: "Duration", width: 90 }, { value: "predecessors", caption: "Predecessors", width: 120 } ]); view.fetch().then(function() { ganttSheet.bindGanttView(view); }); } addTask() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var selections = ganttSheet.getSelections(); var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length) var rowCount = selections.map(r => r.rowCount).reduce((pre,current) => {return pre + current}); var taskData = createTaskDataList(rowCount, () => ({ name: "<New Task>" })); project.insertTasksByRow(insertedRow, taskData); } addSummary() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var selectedRange = ganttSheet.getSelections()[0]; var insertedRow = selectedRange.row; var insertedTaskNumbers = ganttSheet.getTaskByRow(insertedRow).taskNumber || project.count; var selectedTasks = getSelectedRowIndexes(ganttSheet).map((row) => ganttSheet.getTaskByRow(row)).filter((row) => !!row); if (selectedTasks.length == 0) { project.insertTasks(insertedTaskNumbers, [{ name: "<New Summary Task>" }, { name: "<New Task>" }]); project.indentTasks(insertedTaskNumbers + 1); } else { project.insertTasks(insertedTaskNumbers, { name: "<New Summary Task>" }, selectedTasks[0].level); project.indentTasks(selectedTasks.map((t) => t.taskNumber)); } ganttSheet.setSelection(insertedRow, -1, 1, -1); } addMilestone() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var selections = ganttSheet.getSelections(); var rowCount = selections.map(r => r.rowCount).reduce((pre,current) => {return pre + current}); var insertedRow = Math.min(...selections.map(r => r.row), project.tasks.length); var taskData = createTaskDataList(rowCount, () => ({ name: "<New Milestone>", duration: 0 })); project.insertTasksByRow(insertedRow, taskData); ganttSheet.setSelection(insertedRow, -1, rowCount, -1); } deleteTask() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(ganttSheet); project.removeTasks(rowIds); } indentTask() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(ganttSheet); project.indentTasks(rowIds); } outdentTask() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var rowIds = getSelectedTaskNumbers(ganttSheet); project.outdentTasks(rowIds); } linkTask() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var links = []; var selections = ganttSheet.getSelections(); var previous = -1; for (var range of selections) { if (previous != -1) { links.push([previous, range.row]); } for (var row = range.row + 1; row < range.row + range.rowCount; row++) { links.push([row - 1, row]); } previous = range.row + range.rowCount - 1; } if (links.length <= 0) { return; } project.suspendSchedule(); for (var link of links) { var [fromTaskNumber, toTaskNumber] = link; try { project.addDependency({fromTaskNumber, toTaskNumber}); } catch (e) { } } project.resumeSchedule(); } unlinkTask() { var ganttSheet = this.ganttSheet; var project = ganttSheet.project; var taskNumbers = getSelectedTaskNumbers(ganttSheet); var dependencies = []; if (taskNumbers.length == 1) { var task = project.getTaskByRow(taskNumbers[0]); dependencies = [...task.predecessorDependencies, ...task.successorDependencies]; } else { var taskNumberSet = {}; for (var taskNumber of taskNumbers) { taskNumberSet[taskNumber] = true; } dependencies = project.dependencies.filter(dp => taskNumberSet[dp.from.taskNumber] && taskNumberSet[dp.to.taskNumber]); } if (dependencies.length >= 1) { project.removeDependency(dependencies); } } submitChanges() { this.ganttSheet.submitChanges(); } cancelChanges() { this.ganttSheet.cancelChanges(); } } function createTaskDataList(count, initializer) { var array = new Array(count); for (var index = 0; index < count; index++) { array[index] = initializer(); } return array; } function getSelectedRowIndexes(ganttSheet) { var rows = []; var selections = ganttSheet.getSelections(); for (var range of selections) { for (var row = range.row; row < range.row + range.rowCount; row++) { rows.push(row); } } return rows; } function getSelectedTaskNumbers(ganttSheet) { return getSelectedRowIndexes(ganttSheet).map(r => ganttSheet.getTaskByRow(r).taskNumber).filter((taskNumber) => !isNaN(taskNumber)); } function initSplitView(spread) { var host = document.getElementById("split-view"); var content = host.getElementsByClassName("split-content")[0]; var panel = host.getElementsByClassName("split-panel")[0]; new SplitView({ host: host, content: content, panel: panel, refreshContent: function() { spread.refresh(); } }); } function getBaseApiUrl() { return window.location.href.match(/http.+spreadjs\/demos\//)[0] + 'server/api'; } @NgModule({ imports: [BrowserModule, SpreadSheetsModule], declarations: [AppComponent], exports: [AppComponent], bootstrap: [AppComponent] }) export class AppModule {} enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule);
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/angular/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/spread/source/splitView/splitView.css"> <!-- Polyfills --> <script src="$DEMOROOT$/en/angular/node_modules/core-js/client/shim.min.js"></script> <script src="$DEMOROOT$/en/angular/node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="$DEMOROOT$/en/angular/node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <!-- plugins --> <script src="$DEMOROOT$/spread/source/splitView/SplitView.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.import('@angular/compiler'); System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('$DEMOROOT$/en/lib/angular/license.ts'); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<div id="split-view" class="sample-tutorial"> <gc-spread-sheets class="sample-spreadsheets split-content" [hostStyle]="hostStyle" (workbookInitialized)="initSpread($event)"></gc-spread-sheets> <div class="options-container split-panel"> <div class="option-row option-title"> Operate the selected tasks. </div> <div class="option-block"> <div class="option-row"> <input type="button" id="add-task" class="option-button" value="Add Task" (click)="addTask()" /> </div> <div class="option-row"> <input type="button" id="add-summary" class="option-button" value="Add Summary" (click)="addSummary()" /> </div> <div class="option-row"> <input type="button" id="add-milestone" class="option-button" value="Add Milestone" (click)="addMilestone()" /> </div> </div> <div class="option-block"> <div class="option-row"> <input type="button" id="delete-task" class="option-button" value="Delete" (click)="deleteTask()" /> </div> </div> <div class="option-block"> <div class="option-row"> <input type="button" id="indent-task" class="option-button" value="Indent" (click)="indentTask()" /> </div> <div class="option-row"> <input type="button" id="outdent-task" class="option-button" value="Outdent" (click)="outdentTask()" /> </div> </div> <div class="option-block"> <div class="option-row"> <input type="button" id="link-task" class="option-button" value="Link" (click)="linkTask()" /> </div> <div class="option-row"> <input type="button" id="unlink-task" class="option-button" value="Unlink" (click)="unlinkTask()" /> </div> </div> <div class="option-block"> <div class="option-info">* Save or abort current ganttsheet changes.</div> <div class="option-row"> <input type="button" id="submit-changes" class="option-button" value="Submit Changes" (click)="submitChanges()" /> </div> <div class="option-row"> <input type="button" id="cancel-changes" class="option-button" value="Cancel Changes" (click)="cancelChanges()" /> </div> </div> </div> </div>
.options-container { float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; box-shadow: inset 0px 0 4px 0 rgba(0,0,0,0.4); } .option-block { background: #fff; padding: 8px; margin: 12px 0; border-radius: 4px; border: 1px dashed #82bc00; box-shadow: 0px 0 6px 0 rgba(0,0,0,0.1); } .option-block.toggle { border: 1px dotted #f7a711; } .option-row { font-size: 14px; box-sizing: border-box; padding: 4px 0; } .option-title { font-weight: bold; color: #656565; } .option-info { font-size: 12px; color: #919191; margin-top: 6px; font-weight: normal; } .option-info.valid { color: #82bc00; } .option-info.toggle { color: #f7a711; } .option-button { width: 100%; padding: 0; line-height: 20px; background: #82bc00; color: #fff; transition: 0.3s; cursor: pointer; outline: none; border-radius: 4px; box-sizing: border-box; box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3); border: none; } .option-button:hover { background: #82bc00; color: #fff; box-shadow: 0 3px 8px 0 rgba(0,0,0,0.4); } .option-checkbox { background: #fff; border: 1px dashed #f7a711; color: #f7a711; padding: 2px 4px; transition: 0.3s; box-sizing: border-box; cursor: pointer; } .option-checkbox.active { color: #fff; background: #f7a711; box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3); border-radius: 4px; } .selection-box { position: relative; } .selection-box > select { text-align: left; width: 100%; height: 20px; padding: 0; line-height: 20px; background: transparent; border: none; border-bottom: 2px solid #656565; color: #656565; transition: 0.3s; cursor: pointer; outline: none; box-sizing: border-box; } .selection-box > select > option { background: white; } .selection-box > select:focus { border-bottom: 2px solid #82bc00; color: #82bc00; box-shadow: 0 2px 6px 0 rgba(0,0,0,0.3); } .selection-box > label { position: absolute; cursor: pointer; font-size: 12px; color: #fff; background: #656565; padding: 0 4px; right: 0; top: 6px; box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3); } .input-box { position: relative; } .input-box > input[type=text] { width: 100%; background: transparent; border: none; color: #656565; border-bottom: 2px solid #656565; outline: none; box-sizing: border-box; transition: 0.3s; } .input-box > input[type=text]:focus { color: #82bc00; border-bottom: 2px solid #82bc00; } .input-box > label { cursor: pointer; position: absolute; right: 0; top: 5px; font-size: 12px; color: #fff; background: #656565; padding: 0 4px; box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3); }
(function (global) { System.config({ transpiler: 'ts', typescriptOptions: { tsconfig: true }, meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'core-js': 'npm:core-js/client/shim.min.js', 'zone': 'npm:zone.js/fesm2015/zone.min.js', 'rxjs': 'npm:rxjs/dist/bundles/rxjs.umd.min.js', '@angular/core': 'npm:@angular/core/fesm2022', '@angular/common': 'npm:@angular/common/fesm2022/common.mjs', '@angular/compiler': 'npm:@angular/compiler/fesm2022/compiler.mjs', '@angular/platform-browser': 'npm:@angular/platform-browser/fesm2022/platform-browser.mjs', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/fesm2022/platform-browser-dynamic.mjs', '@angular/common/http': 'npm:@angular/common/fesm2022/http.mjs', '@angular/router': 'npm:@angular/router/fesm2022/router.mjs', '@angular/forms': 'npm:@angular/forms/fesm2022/forms.mjs', 'jszip': 'npm:jszip/dist/jszip.min.js', 'typescript': 'npm:typescript/lib/typescript.js', 'ts': './plugin.js', 'tslib':'npm:tslib/tslib.js', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js', '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-tablesheet': 'npm:@mescius/spread-sheets-tablesheet/index.js', '@mescius/spread-sheets-ganttsheet': 'npm:@mescius/spread-sheets-ganttsheet/index.js', '@mescius/spread-sheets-angular': 'npm:@mescius/spread-sheets-angular/fesm2020/mescius-spread-sheets-angular.mjs', '@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'ts' }, rxjs: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, "node_modules/@angular": { defaultExtension: 'mjs' }, "@mescius/spread-sheets-angular": { defaultExtension: 'mjs' }, '@angular/core': { defaultExtension: 'mjs', main: 'core.mjs' } } }); })(this);