diff --git a/README.md b/README.md index ddd07c4..2a5ecff 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ -# how-to-save-and-export-an-Angular-diagram +# How to Save and Export an Angular Diagram + A quick start Angular project that shows how to save and export an Angular Diagram component. This project includes a code snippet to save and load the Diagram as a JSON string and file stream. It also includes a code snippet to export the Diagram in PNG format. + +The serialization documentation for the Syncfusion Angular Diagram component: +https://ej2.syncfusion.com/angular/documentation/diagram/serialization + +The print and export documentation for the Syncfusion Angular Diagram component: +https://ej2.syncfusion.com/angular/documentation/diagram/export + +Check out the online serialization example for the Angular Diagram component: +https://ej2.syncfusion.com/angular/demos/#/material3/diagram/serialization + +Check out the online export and print example for the Angular Diagram component: +https://ej2.syncfusion.com/angular/demos/#/material3/diagram/print-export + +Refer to this video for getting started with the Angular Diagram component: +https://www.youtube.com/watch?v=TObiS2Lj87U + +Refer to this video for creating and customizing nodes in the Angular Diagram component: +https://www.youtube.com/watch?v=OnI_SMaYLOk + +Refer to this video for adding and customizing connectors in the Angular Diagram component: +https://www.youtube.com/watch?v=fwK8CWWBwZ4 + +## Project prerequisites + +Make sure that you have the latest versions of NodeJS and Visual Studio Code in your machine before starting to work on this project. + +### How to run this application? + +To run this application, you need to clone the `how-to-save-and-export-an-Angular-diagram` repository and then open it in Visual Studio Code. Now, simply install all the necessary react packages into your current project using the `npm install` command and run your project using the `ng serve` command. diff --git a/angular.json b/angular.json new file mode 100644 index 0000000..5d52b4e --- /dev/null +++ b/angular.json @@ -0,0 +1,100 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "myangularproject": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/myangularproject", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.css", + "./node_modules/@syncfusion/ej2-material-theme/styles/material.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "myangularproject:build:production" + }, + "development": { + "browserTarget": "myangularproject:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "myangularproject:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.css", + "./node_modules/@syncfusion/ej2-material-theme/styles/material.css" + ], + "scripts": [] + } + } + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..1c41385 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "myangularproject", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "^16.0.0", + "@angular/common": "^16.0.0", + "@angular/compiler": "^16.0.0", + "@angular/core": "^16.0.0", + "@angular/forms": "^16.0.0", + "@angular/platform-browser": "^16.0.0", + "@angular/platform-browser-dynamic": "^16.0.0", + "@angular/router": "^16.0.0", + "@syncfusion/ej2-angular-buttons": "^22.2.9", + "@syncfusion/ej2-angular-diagrams": "^22.2.10", + "@syncfusion/ej2-angular-inputs": "^22.2.9", + "@syncfusion/ej2-material-theme": "*", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.13.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^16.0.4", + "@angular/cli": "~16.0.4", + "@angular/compiler-cli": "^16.0.0", + "@types/jasmine": "~4.3.0", + "jasmine-core": "~4.6.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.0.0", + "typescript": "~5.0.2" + } +} \ No newline at end of file diff --git a/src/app/app.component.css b/src/app/app.component.css new file mode 100644 index 0000000..9fdc5c1 --- /dev/null +++ b/src/app/app.component.css @@ -0,0 +1,7 @@ +.e-btn{ + margin: 5px; +} + +.e-upload.e-control-wrapper{ + display: none; +} \ No newline at end of file diff --git a/src/app/app.component.html b/src/app/app.component.html new file mode 100644 index 0000000..8dacd11 --- /dev/null +++ b/src/app/app.component.html @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts new file mode 100644 index 0000000..f7dd36d --- /dev/null +++ b/src/app/app.component.spec.ts @@ -0,0 +1,27 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(() => TestBed.configureTestingModule({ + declarations: [AppComponent] + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'myangularproject'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('myangularproject'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('.content span')?.textContent).toContain('myangularproject app is running!'); + }); +}); diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 0000000..56cb04b --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,93 @@ +import { Component, ViewEncapsulation, ViewChild } from '@angular/core'; +import { ShapeStyleModel, FlowShapeModel, PointPortModel, DiagramComponent, PrintAndExport, Diagram, IExportOptions } from '@syncfusion/ej2-angular-diagrams'; +import { AsyncSettingsModel } from '@syncfusion/ej2-angular-inputs'; +Diagram.Inject(PrintAndExport); +@Component({ + selector: 'app-root', + templateUrl: 'app.component.html', + styleUrls: ['app.component.css'], + encapsulation: ViewEncapsulation.None, +}) +export class AppComponent { + title = 'myangularproject'; + @ViewChild('diagram') diagramObj !: DiagramComponent; + public diagramData: any; + public terminator: FlowShapeModel = { type: 'Flow', shape: 'Terminator' }; + public data: FlowShapeModel = { type: 'Flow', shape: 'Data' }; + public process: FlowShapeModel = { type: 'Flow', shape: 'Process' }; + public decision: FlowShapeModel = { type: 'Flow', shape: 'Decision' }; + public basicStyle: ShapeStyleModel = { + fill: '#37909A', + strokeColor: '#37909A', + strokeWidth: 3, + }; + + public displacement: object = { x:10, y: 10} + public ports: PointPortModel[] = [ + { id:'port1', offset: {x:0.5, y:1} }, + { id:'port2', offset: {x:1, y:0.5} } + ] + + public asyncSettings: AsyncSettingsModel = { + saveUrl: 'https://aspnetmvc.syncfusion.com/services/api/uploadbox/Save' + }; + + public onSave(){ + // this.diagramData = this.diagramObj.saveDiagram(); + this.downlaod(this.diagramObj.saveDiagram()); + } + + public onPrint(){ + let printOptions: IExportOptions = {}; + printOptions.mode = 'Data'; + this.diagramObj.print(printOptions); + } + + public onExport(){ + let exportOptions: IExportOptions = {}; + exportOptions.format = 'PNG'; + exportOptions.mode = 'Download'; + this.diagramObj.exportDiagram(exportOptions); + } + + public onUploadSuccess(args: { [key: string]: Object }){ + let fileData: { [key: string]: Object } = args['file'] as { [key: string]: Object }; + let file: Blob = fileData['rawFile'] as Blob; + let reader = new FileReader(); + reader.readAsText(file); + reader.onloadend = this.loadDiagram.bind(this); + } + + public loadDiagram(event: ProgressEvent){ + let result = (event.target as FileReader).result; + if(result){ + this.diagramObj.loadDiagram(result.toString()); + } + } + + public onLoad(){ + // this.diagramObj.loadDiagram(this.diagramData); + const fileSelectWrap = document.getElementsByClassName('e-file-select-wrap')[0]; + if(fileSelectWrap){ + const button = fileSelectWrap.querySelector('button'); + if(button){ + button.click(); + } + } + } + + public downlaod(data: string){ + if((window.navigator as any).msSaveBlob){ + let blob: Blob = new Blob([data], { type: 'data:text/json;charset=utf-8,' }); + (window.navigator as any).msSaveOrOpenBlob(blob, 'Diagram.json'); + } else { + let dataStr: string = 'data:text/json;charset=utf-8,' + encodeURIComponent(data); + let a: HTMLAnchorElement = document.createElement('a'); + a.href = dataStr; + a.download = 'Diagram.json'; + document.body.appendChild(a); + a.click(); + a.remove(); + } + } +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts new file mode 100644 index 0000000..3b1eb98 --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; +import { DiagramModule } from '@syncfusion/ej2-angular-diagrams'; +import { ButtonModule } from '@syncfusion/ej2-angular-buttons'; +import { UploaderModule } from '@syncfusion/ej2-angular-inputs'; + +@NgModule({ + declarations: [ + AppComponent + ], + imports: [ + BrowserModule, + DiagramModule, + ButtonModule, + UploaderModule + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/src/assets/images/Marker.png b/src/assets/images/Marker.png new file mode 100644 index 0000000..4dda18a Binary files /dev/null and b/src/assets/images/Marker.png differ diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000..997406a Binary files /dev/null and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..b48f858 --- /dev/null +++ b/src/index.html @@ -0,0 +1,13 @@ + + + + + Myangularproject + + + + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..f43f6ed --- /dev/null +++ b/src/main.ts @@ -0,0 +1,7 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import {registerLicense} from '@syncfusion/ej2-base' +import { AppModule } from './app/app.module'; +registerLicense("Your license key"); + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..940369a --- /dev/null +++ b/src/styles.css @@ -0,0 +1,6 @@ +@import '../node_modules/@syncfusion/ej2-material-theme/styles/material.css'; +/* You can add global styles to this file, and also import other style files */ + +.btn{ + padding: 10px; +} \ No newline at end of file diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..374cc9d --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..16e680a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "resolveJsonModule": true, + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": [ + "ES2022", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000..be7e9da --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +}