Named Cell Template

SpreadJS supports named cell templates, which allow you to create reusable cell configuration templates that combine style, conditional formatting, cell states, and data validation. You can register a template in a workbook, create one from an existing cell, and apply it to ranges across worksheets.

Introduction The Named Cell Templates feature allows you to define reusable cell configuration templates that can be applied to cells or ranges. Each template can contain style, conditional formats, data validations, and cell states. You can access the named cell templates manager through the workbook instance: Add a Template Use add(name, template) to register a new template: Create Template from Cell Use createFromCell(name, sheet, row, col, options) to create a template from an existing cell's configuration: Apply Template Use sheet.applyNamedCellTemplate(name, address) to apply a template to a range: Or use cellRange.applyNamedCellTemplate(name): Manage Templates Template Structure The template object follows the INamedCellTemplate interface:
import { Component, NgModule, NgZone, enableProdMode } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { SpreadSheetsModule } from '@mescius/spread-sheets-angular'; import { DEFAULT_CREATE_OPTIONS, DEFAULT_TARGET_RANGE, applyTemplateToRange, clearNamedTemplates, createTemplateFromSelection, getOrderedRegisteredTemplateNames, initializeNamedCellTemplateDemo, removeNamedTemplate, syncSelectionTargetRange } from './demo-logic'; import './styles.css'; @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { constructor(private ngZone: NgZone) { } spread: any = null; selectionCleanup: any = null; hostStyle = { width: '100%', height: '100%' }; templateNames: string[] = []; selectedTemplate = ''; selectedListTemplate = ''; targetRange = DEFAULT_TARGET_RANGE; newTemplateName = ''; createOptions = Object.assign({}, DEFAULT_CREATE_OPTIONS); initSpread(event: any) { this.spread = event && event.spread ? event.spread : event; initializeNamedCellTemplateDemo(this.spread); this.refreshTemplateState(getOrderedRegisteredTemplateNames(this.spread)); if (this.selectionCleanup) { this.selectionCleanup(); } this.selectionCleanup = syncSelectionTargetRange(this.spread, (value: string) => { this.ngZone.run(() => { this.targetRange = value; }); }); } refreshTemplateState(names: string[]) { this.templateNames = names; this.selectedTemplate = names.indexOf(this.selectedTemplate) !== -1 ? this.selectedTemplate : (names[0] || ''); this.selectedListTemplate = names.indexOf(this.selectedListTemplate) !== -1 ? this.selectedListTemplate : (names[0] || ''); } withSpread(action: (spread: any) => void) { if (!this.spread) { return; } try { action(this.spread); } catch (error: any) { window.alert(error.message); } } applyTemplate() { this.withSpread((spread: any) => { applyTemplateToRange(spread, this.selectedTemplate, this.targetRange); }); } createTemplate() { this.withSpread((spread: any) => { this.refreshTemplateState(createTemplateFromSelection(spread, this.newTemplateName, this.createOptions)); this.newTemplateName = ''; }); } removeTemplate() { this.withSpread((spread: any) => { this.refreshTemplateState(removeNamedTemplate(spread, this.selectedListTemplate)); }); } clearTemplates() { this.withSpread((spread: any) => { this.refreshTemplateState(clearNamedTemplates(spread)); }); } } @NgModule({ imports: [BrowserModule, SpreadSheetsModule, FormsModule], declarations: [AppComponent], exports: [AppComponent], bootstrap: [AppComponent] }) export class AppModule { } enableProdMode(); 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"> <!-- 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> <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 class="sample-tutorial"> <div class="sample-container"> <gc-spread-sheets [hostStyle]="hostStyle" (workbookInitialized)="initSpread($event)"> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> <gc-worksheet></gc-worksheet> </gc-spread-sheets> </div> <div class="options-container"> <div class="info-tip">The left side displays preset named templates. Switch to the Playground sheet to apply templates.</div> <div class="section-title">Apply Template</div> <div class="option-row"> <label for="templateSelect">Template:</label> <select id="templateSelect" [(ngModel)]="selectedTemplate"> <option *ngFor="let name of templateNames" [value]="name">{{ name }}</option> </select> </div> <div class="option-row"> <label for="targetRange">Target Range:</label> <input id="targetRange" type="text" [(ngModel)]="targetRange" placeholder="e.g. A1:A10" /> </div> <div class="option-row"> <input type="button" id="applyBtn" value="Apply Template" (click)="applyTemplate()" /> </div> <div class="section-title">Create From Cell</div> <div class="option-row"> <label for="newTemplateName">Template Name:</label> <input id="newTemplateName" type="text" [(ngModel)]="newTemplateName" placeholder="Enter template name" /> </div> <div class="option-row"> <label>Options:</label> <div class="checkbox-row"> <input id="optStyle" type="checkbox" [(ngModel)]="createOptions.style" /> <label for="optStyle">Style</label> </div> <div class="checkbox-row"> <input id="optConditionalFormat" type="checkbox" [(ngModel)]="createOptions.conditionalFormat" /> <label for="optConditionalFormat">Conditional Format</label> </div> <div class="checkbox-row"> <input id="optDataValidation" type="checkbox" [(ngModel)]="createOptions.dataValidation" /> <label for="optDataValidation">Data Validation</label> </div> <div class="checkbox-row"> <input id="optCellState" type="checkbox" [(ngModel)]="createOptions.cellState" /> <label for="optCellState">Cell State</label> </div> </div> <div class="option-row"> <input type="button" id="createFromCellBtn" value="Create From Cell" (click)="createTemplate()" /> </div> <div class="section-title">Template Management</div> <div class="option-row"> <label for="templateList">Registered Templates:</label> <select id="templateList" size="12" [(ngModel)]="selectedListTemplate"> <option *ngFor="let name of templateNames" [value]="name">{{ name }}</option> </select> </div> <div class="option-row"> <input type="button" id="removeBtn" value="Remove Selected" (click)="removeTemplate()" /> <input type="button" id="clearBtn" value="Clear All" (click)="clearTemplates()" /> </div> </div> </div>
html, body { height: 100%; margin: 0; } body { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } .sample-tutorial { position: relative; display: flex; height: 100%; overflow: hidden; } .sample-container { width: calc(100% - 320px); height: 100%; overflow: hidden; } .options-container { width: 320px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; border-left: 1px solid #e5e5e5; } .info-tip { background: #e7f3ff; border-left: 3px solid #2196f3; border-radius: 4px; padding: 8px 10px; font-size: 13px; color: #333; line-height: 1.4; margin-bottom: 8px; } .section-title { font-weight: bold; font-size: 13px; border-bottom: 1px solid #ddd; padding-bottom: 4px; margin-top: 16px; margin-bottom: 8px; } .section-title:first-child { margin-top: 0; } .option-row { font-size: 14px; padding: 2px 0; margin-top: 6px; } .option-row label { display: block; margin-bottom: 4px; } .checkbox-row { margin: 4px 0; } .checkbox-row label { display: inline; margin-left: 4px; } input[type=text] { width: 100%; box-sizing: border-box; padding: 4px 6px; } select { width: 100%; padding: 4px; } input[type=button] { margin-top: 6px; display: block; width: 100%; padding: 6px 8px; cursor: pointer; }
(function (global) { System.config({ transpiler: 'ts', typescriptOptions: { tsconfig: true }, meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/', 'cdn:': 'https://cdn.mescius.io/demoapps/packages/spreadjs/19.1.0-master-2026-05-10-2130/' }, // 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': 'cdn:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-shapes': 'cdn:@mescius/spread-sheets-shapes/index.js', '@mescius/spread-sheets-slicers': 'cdn:@mescius/spread-sheets-slicers/index.js', '@mescius/spread-sheets-charts': 'cdn:@mescius/spread-sheets-charts/index.js', '@mescius/spread-sheets-angular': 'cdn:@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);