├── demo ├── src │ ├── assets │ │ └── .gitkeep │ ├── app │ │ ├── app.component.scss │ │ ├── app.component.html │ │ ├── CustomComponent.ts │ │ ├── app.module.ts │ │ ├── app.component.spec.ts │ │ └── app.component.ts │ ├── favicon.ico │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── styles.scss │ ├── main.ts │ ├── index.html │ ├── test.ts │ └── polyfills.ts ├── e2e │ ├── tsconfig.json │ ├── src │ │ ├── app.po.ts │ │ └── app.e2e-spec.ts │ └── protractor.conf.js ├── tsconfig.app.json ├── .editorconfig ├── tsconfig.spec.json ├── .browserslistrc ├── tsconfig.json ├── .gitignore ├── README.md ├── karma.conf.js ├── package.json ├── tslint.json └── angular.json ├── projects └── angular-material-formio │ ├── src │ ├── lib │ │ ├── config.ts │ │ ├── components │ │ │ ├── textarea │ │ │ │ ├── textarea.component.css │ │ │ │ └── textarea.component.ts │ │ │ ├── editgrid │ │ │ │ ├── editgrid.component.css │ │ │ │ ├── editgrid.component.html │ │ │ │ └── editgrid.component.ts │ │ │ ├── label │ │ │ │ ├── label.component.css │ │ │ │ ├── label.component.ts │ │ │ │ └── label.component.html │ │ │ ├── table │ │ │ │ ├── table.component.css │ │ │ │ └── table.component.ts │ │ │ ├── content │ │ │ │ └── content.component.ts │ │ │ ├── hidden │ │ │ │ └── hidden.component.ts │ │ │ ├── url │ │ │ │ └── url.component.ts │ │ │ ├── Webform.ts │ │ │ ├── email │ │ │ │ └── email.component.ts │ │ │ ├── password │ │ │ │ └── password.component.ts │ │ │ ├── phonenumber │ │ │ │ └── phonenumber.component.ts │ │ │ ├── container │ │ │ │ └── container.component.ts │ │ │ ├── button │ │ │ │ ├── button.component.html │ │ │ │ ├── button.component.css │ │ │ │ └── button.component.ts │ │ │ ├── currency │ │ │ │ └── currency.component.ts │ │ │ ├── html │ │ │ │ └── html.component.ts │ │ │ ├── well │ │ │ │ └── well.component.ts │ │ │ ├── formio-form-field │ │ │ │ ├── formio-form-field.component.css │ │ │ │ ├── formio-form-field.component.ts │ │ │ │ └── formio-form-field.component.html │ │ │ ├── tabs │ │ │ │ └── tabs.component.ts │ │ │ ├── fieldset │ │ │ │ └── fieldset.component.ts │ │ │ ├── Base.ts │ │ │ ├── MaterialNestedComponent.ts │ │ │ ├── checkbox │ │ │ │ └── checkbox.component.ts │ │ │ ├── panel │ │ │ │ └── panel.component.ts │ │ │ ├── columns │ │ │ │ └── columns.component.ts │ │ │ ├── number │ │ │ │ └── number.component.ts │ │ │ ├── radio │ │ │ │ └── radio.component.ts │ │ │ ├── signature │ │ │ │ └── signature.component.ts │ │ │ ├── selectboxes │ │ │ │ └── selectboxes.component.ts │ │ │ ├── tags │ │ │ │ └── tags.component.ts │ │ │ ├── calendar │ │ │ │ └── calendar.component.ts │ │ │ ├── formio.wizard.ts │ │ │ ├── select │ │ │ │ └── select.component.ts │ │ │ ├── textfield │ │ │ │ └── textfield.component.ts │ │ │ ├── survey │ │ │ │ └── survey.component.ts │ │ │ ├── day │ │ │ │ └── day.component.ts │ │ │ ├── time │ │ │ │ └── time.component.ts │ │ │ ├── MaterialComponent.ts │ │ │ ├── index.ts │ │ │ ├── datagrid │ │ │ │ └── datagrid.component.ts │ │ │ └── date │ │ │ │ └── date.component.ts │ │ ├── const │ │ │ ├── ButtonsSizes.ts │ │ │ ├── LabelPositions.ts │ │ │ └── ButtonsThemes.ts │ │ ├── renderer.ts │ │ ├── FormioControl.ts │ │ ├── directives │ │ │ └── mask.directive.ts │ │ ├── formio.component.ts │ │ └── angular-material-formio.module.ts │ └── public-api.ts │ ├── tsconfig.lib.prod.json │ ├── ng-package.json │ ├── tslint.json │ ├── tsconfig.spec.json │ ├── package.json │ ├── tsconfig.lib.json │ ├── karma.conf.js │ └── README.md ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── LICENSE ├── angular.json ├── package.json ├── README.md └── tslint.json /demo/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/formio/angular-material/HEAD/demo/src/favicon.ico -------------------------------------------------------------------------------- /demo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | verticalSpace: '5px' 3 | }; 4 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/textarea/textarea.component.css: -------------------------------------------------------------------------------- 1 | .mat-input-element { 2 | min-height: 16px; 3 | } -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/editgrid/editgrid.component.css: -------------------------------------------------------------------------------- 1 | :host .delete-button { 2 | margin-left: auto; 3 | order: 2; 4 | } 5 | -------------------------------------------------------------------------------- /demo/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "~@angular/material/prebuilt-themes/indigo-pink.css"; 3 | -------------------------------------------------------------------------------- /projects/angular-material-formio/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "angularCompilerOptions": { 4 | "enableIvy": false 5 | } 6 | } -------------------------------------------------------------------------------- /projects/angular-material-formio/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of angular-material-formio 3 | */ 4 | export * from './lib/angular-material-formio.module'; 5 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/const/ButtonsSizes.ts: -------------------------------------------------------------------------------- 1 | export enum ButtonsSizes { 2 | EXTRA_SMALL = 'xs', 3 | SMALL = 'sm', 4 | MEDIUM = 'md', 5 | LARGE = 'lg' 6 | } 7 | -------------------------------------------------------------------------------- /projects/angular-material-formio/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | }, 7 | "whitelistedNonPeerDependencies": [] 8 | } 9 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/const/LabelPositions.ts: -------------------------------------------------------------------------------- 1 | export enum LabelPositions { 2 | RIGHT_RIGHT = 'right-right', 3 | RIGHT_LEFT = 'right-left', 4 | LEFT_LEFT = 'left-left', 5 | LEFT_RIGHT = 'left-right', 6 | BOTTOM = 'bottom', 7 | TOP = 'top', 8 | } 9 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/label/label.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | pointer-events: all; 3 | display: block; 4 | } 5 | 6 | .required-star { 7 | margin-left: 0.2rem; 8 | color: red; 9 | font-size: 0.8rem; 10 | vertical-align: top; 11 | } 12 | -------------------------------------------------------------------------------- /demo/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": ["node"] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /demo/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /demo/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /projects/angular-material-formio/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "mat", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "mat", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/angular-material-formio/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /demo/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/label/label.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | // tslint:disable-next-line:component-selector 5 | selector: 'span[matFormioLabel]', 6 | templateUrl: './label.component.html', 7 | styleUrls: ['./label.component.css'] 8 | }) 9 | export class LabelComponent { 10 | @Input() instance; 11 | } 12 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/const/ButtonsThemes.ts: -------------------------------------------------------------------------------- 1 | export enum ButtonsThemes { 2 | PRIMARY = 'primary', 3 | SECONDARY = 'secondary', 4 | INFO = 'info', 5 | WARNING = 'warning', 6 | DANGER = 'danger', 7 | SUCCESS = 'success' 8 | } 9 | 10 | export enum AngularButtonsThemes { 11 | WARN = 'warn', 12 | PRIMARY = 'primary', 13 | BASIC = 'basic', 14 | ACCENT = 'accent' 15 | } 16 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/label/label.component.html: -------------------------------------------------------------------------------- 1 | 2 | {{ instance.component.label }}* 3 | 6 | info 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularMaterialFormio 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/table/table.component.css: -------------------------------------------------------------------------------- 1 | .mat-table.is-bordered { 2 | border: 1px solid #ddd; 3 | } 4 | 5 | .mat-row.is-hover:hover { 6 | background-color: #f5f5f5; 7 | } 8 | 9 | .mat-cell { 10 | padding: 8px; 11 | } 12 | 13 | .mat-cell.is-condensed { 14 | padding: 3px; 15 | } 16 | 17 | .mat-row.is-striped { 18 | background-color: #eee; 19 | } 20 | 21 | .mat-cell.center { 22 | text-align: center; 23 | } 24 | 25 | .mat-cell.right { 26 | text-align: right; 27 | } 28 | -------------------------------------------------------------------------------- /demo/src/app/CustomComponent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file demonstrates how to create a custom component in Angular Material 3 | */ 4 | import { Components, MaterialComponent, registerComponent } from '@formio/angular-material'; 5 | import { Component } from '@angular/core'; 6 | @Component({ 7 | template:'My Custom Component' 8 | }) 9 | export class MaterialCustomComponent extends MaterialComponent { 10 | // Custom Material logic goes here. 11 | } 12 | registerComponent('my-custom-component', MaterialCustomComponent); 13 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/content/content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import ContentComponent from 'formiojs/components/content/Content.js'; 4 | @Component({ 5 | selector: 'mat-formio-content', 6 | template: `
` 7 | }) 8 | export class MaterialContentComponent extends MaterialComponent {} 9 | ContentComponent.MaterialComponent = MaterialContentComponent; 10 | export { ContentComponent }; 11 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/hidden/hidden.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import HiddenComponent from 'formiojs/components/hidden/Hidden.js'; 4 | @Component({ 5 | selector: 'mat-formio-hidden', 6 | template: `` 7 | }) 8 | export class MaterialHiddenComponent extends MaterialComponent {} 9 | HiddenComponent.MaterialComponent = MaterialHiddenComponent; 10 | export { HiddenComponent }; 11 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/url/url.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialTextfieldComponent, TEXTFIELD_TEMPLATE } from '../textfield/textfield.component'; 3 | import UrlComponent from 'formiojs/components/url/Url.js'; 4 | @Component({ 5 | selector: 'mat-formio-url', 6 | template: TEXTFIELD_TEMPLATE 7 | }) 8 | export class MaterialUrlComponent extends MaterialTextfieldComponent { 9 | public inputType = 'url'; 10 | } 11 | UrlComponent.MaterialComponent = MaterialUrlComponent; 12 | export { UrlComponent }; 13 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/Webform.ts: -------------------------------------------------------------------------------- 1 | import Webform from 'formiojs/Webform.js'; 2 | Webform.prototype.redraw = function() { 3 | return this.render(); 4 | }; 5 | Webform.prototype.clear = function() { 6 | const viewContainer = this.viewContainer ? this.viewContainer() : null; 7 | if (viewContainer) { 8 | viewContainer.clear(); 9 | } 10 | }; 11 | Webform.prototype.render = function() { 12 | if (this.viewContainer && this.viewContainer()) { 13 | this.clear(); 14 | this.renderComponents(); 15 | this.setValue(this._submission); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/email/email.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialTextfieldComponent, TEXTFIELD_TEMPLATE } from '../textfield/textfield.component'; 3 | import EmailComponent from 'formiojs/components/email/Email.js'; 4 | @Component({ 5 | selector: 'mat-formio-email', 6 | template: TEXTFIELD_TEMPLATE 7 | }) 8 | export class MaterialEmailComponent extends MaterialTextfieldComponent { 9 | public inputType = 'email'; 10 | } 11 | EmailComponent.MaterialComponent = MaterialEmailComponent; 12 | export { EmailComponent }; 13 | -------------------------------------------------------------------------------- /projects/angular-material-formio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@formio/angular-material", 3 | "version": "2.0.0", 4 | "peerDependencies": { 5 | "@angular/cdk": "^10.0.0 | ^11.0.0", 6 | "@angular/common": "^10.0.0 | ^11.0.0", 7 | "@angular/core": "^10.0.0 | ^11.0.0", 8 | "@angular/flex-layout": "^10.0.0 | ^11.0.0", 9 | "@angular/forms": "^10.0.0 | ^11.0.0", 10 | "@angular/material": "^10.0.0 | ^11.0.0", 11 | "@formio/angular": "^5.0.0-rc.4", 12 | "formiojs": "^4.12.4-rc.3" 13 | }, 14 | "dependencies": { 15 | "tslib": "^2.0.0" 16 | }, 17 | "main": "src/public-api.ts" 18 | } 19 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/password/password.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialTextfieldComponent, TEXTFIELD_TEMPLATE } from '../textfield/textfield.component'; 3 | import PasswordComponent from 'formiojs/components/password/Password.js'; 4 | @Component({ 5 | selector: 'mat-formio-password', 6 | template: TEXTFIELD_TEMPLATE 7 | }) 8 | export class MaterialPasswordComponent extends MaterialTextfieldComponent { 9 | public inputType = 'password'; 10 | } 11 | PasswordComponent.MaterialComponent = MaterialPasswordComponent; 12 | export { PasswordComponent }; 13 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/renderer.ts: -------------------------------------------------------------------------------- 1 | const Components = require('formiojs/components/Components').default; 2 | import './components/Webform'; 3 | import { getComponents, registerComponent } from './components'; 4 | const Formio = require('formiojs/Formio').default; 5 | const Form = require('formiojs/Form').default; 6 | const Utils = require('formiojs/utils').default; 7 | 8 | function initRenderer() { 9 | Components.setComponents(getComponents()); 10 | Formio.Components = Components; 11 | Formio.Templates = {}; 12 | } 13 | 14 | export { Form, Utils, Components, Formio, initRenderer, registerComponent }; 15 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/phonenumber/phonenumber.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialTextfieldComponent, TEXTFIELD_TEMPLATE } from '../textfield/textfield.component'; 3 | import PhoneNumberComponent from 'formiojs/components/phonenumber/PhoneNumber.js'; 4 | @Component({ 5 | selector: 'mat-formio-phonenumber', 6 | template: TEXTFIELD_TEMPLATE 7 | }) 8 | export class MaterialPhoneNumberComponent extends MaterialTextfieldComponent { 9 | public inputType = 'text'; 10 | } 11 | PhoneNumberComponent.MaterialComponent = MaterialPhoneNumberComponent; 12 | export { PhoneNumberComponent }; 13 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/container/container.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 3 | import ContainerComponent from 'formiojs/components/container/Container.js'; 4 | @Component({ 5 | selector: 'mat-formio-container', 6 | template: ` 7 |
8 | 9 |
` 10 | }) 11 | export class MaterialContainerComponent extends MaterialNestedComponent { } 12 | ContainerComponent.MaterialComponent = MaterialContainerComponent; 13 | export { ContainerComponent }; 14 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "es2020", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "enableIvy": false, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/angular-material-formio/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": ["node"], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "enableIvy": false, 16 | "skipTemplateCodegen": true, 17 | "strictMetadataEmit": true, 18 | "fullTemplateTypeCheck": true, 19 | "strictInjectionParameters": true, 20 | "enableResourceInlining": true 21 | }, 22 | "exclude": [ 23 | "src/test.ts", 24 | "**/*.spec.ts" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/button/button.component.html: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /demo/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /demo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /demo/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to angular-material-formio!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /demo/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { AppComponent } from './app.component'; 5 | import { MatFormioModule } from '@formio/angular-material'; 6 | import 'hammerjs'; 7 | import './CustomComponent'; 8 | import { MaterialCustomComponent } from './CustomComponent'; 9 | 10 | @NgModule({ 11 | declarations: [ 12 | AppComponent, 13 | MaterialCustomComponent 14 | ], 15 | imports: [ 16 | BrowserModule, 17 | BrowserAnimationsModule, 18 | MatFormioModule 19 | ], 20 | providers: [], 21 | bootstrap: [AppComponent], 22 | entryComponents: [MaterialCustomComponent] 23 | }) 24 | export class AppModule { } 25 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | node_modules 3 | 4 | # compiled output 5 | /dist 6 | /tmp 7 | /out-tsc 8 | # Only exists if Bazel was run 9 | /bazel-out 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # profiling files 15 | chrome-profiler-events.json 16 | speed-measure-plugin.json 17 | 18 | # IDEs and editors 19 | /.idea 20 | .project 21 | .classpath 22 | .c9/ 23 | *.launch 24 | .settings/ 25 | *.sublime-workspace 26 | 27 | # IDE - VSCode 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | .history/* 34 | 35 | # misc 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/currency/currency.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { TEXTFIELD_TEMPLATE } from '../textfield/textfield.component'; 3 | import { MaterialNumberComponent } from '../number/number.component'; 4 | import CurrencyComponent from 'formiojs/components/currency/Currency.js'; 5 | import _ from 'lodash'; 6 | 7 | @Component({ 8 | selector: 'mat-formio-currency', 9 | template: TEXTFIELD_TEMPLATE 10 | }) 11 | export class MaterialCurrencyComponent extends MaterialNumberComponent { 12 | public inputType = 'text'; 13 | 14 | onChange() { 15 | const newValue = _.isNil(this.getValue()) ? '' : this.getValue(); 16 | this.instance.updateValue(newValue, {modified: true}); 17 | } 18 | } 19 | CurrencyComponent.MaterialComponent = MaterialCurrencyComponent; 20 | export { CurrencyComponent }; 21 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/html/html.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import HtmlComponent from 'formiojs/components/html/HTML.js'; 4 | 5 | @Component({ 6 | selector: 'mat-formio-html', 7 | template: `
` 8 | }) 9 | export class MaterialHtmlComponent extends MaterialComponent implements AfterViewInit { 10 | @ViewChild('htmlBody') htmlBody: ElementRef; 11 | 12 | ngAfterViewInit() { 13 | super.ngAfterViewInit(); 14 | if (this.instance.component.refreshOnChange) { 15 | this.instance.checkRefreshOn = () => { 16 | this.htmlBody.nativeElement.innerHTML = this.instance.renderContent(); 17 | }; 18 | } 19 | } 20 | } 21 | HtmlComponent.MaterialComponent = MaterialHtmlComponent; 22 | export { HtmlComponent }; 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "es2020", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ], 21 | "paths": { 22 | "angular-material-formio": [ 23 | "dist/angular-material-formio" 24 | ], 25 | "angular-material-formio/*": [ 26 | "dist/angular-material-formio/*" 27 | ] 28 | } 29 | }, 30 | "angularCompilerOptions": { 31 | "enableIvy": false, 32 | "fullTemplateTypeCheck": true, 33 | "strictInjectionParameters": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/well/well.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import WellComponent from 'formiojs/components/well/Well.js'; 3 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 4 | @Component({ 5 | selector: 'mat-formio-well', 6 | template: ` 7 | 8 | 15 | 16 | 17 | 18 | `, 19 | styles: [] 20 | }) 21 | export class MaterialWellComponent extends MaterialNestedComponent {} 22 | WellComponent.MaterialComponent = MaterialWellComponent; 23 | export { WellComponent }; 24 | -------------------------------------------------------------------------------- /demo/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/button/button.component.css: -------------------------------------------------------------------------------- 1 | @keyframes spin { 2 | from { 3 | transform:rotate(0deg); 4 | } 5 | to { 6 | transform:rotate(360deg); 7 | } 8 | } 9 | 10 | :host .mat-icon-spin { 11 | animation-name: spin; 12 | animation-duration: 1000ms; 13 | animation-iteration-count: infinite; 14 | animation-timing-function: linear; 15 | } 16 | 17 | .mat-formio-theme-success { 18 | background-color: rgb(99, 214, 68) !important; 19 | } 20 | 21 | .mat-formio-theme-info { 22 | background-color: rgb(73, 217, 244) !important; 23 | } 24 | 25 | .mat-formio-button-sm { 26 | font-size: 14px; 27 | padding: 8px 12px; 28 | } 29 | 30 | .mat-formio-button-xs { 31 | font-size: 14px; 32 | padding: 4px 6px; 33 | } 34 | 35 | .mat-formio-button-md { 36 | font-size: 15px; 37 | padding: 8px 12px; 38 | } 39 | 40 | .mat-formio-button-lg { 41 | font-size: 19px; 42 | padding: 16px 20px; 43 | } 44 | 45 | .mat-formio-button-block { 46 | display: block; 47 | width: 100%; 48 | } 49 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/formio-form-field/formio-form-field.component.css: -------------------------------------------------------------------------------- 1 | .mat-formio-component-description { 2 | display: block; 3 | grid-area: description; 4 | } 5 | 6 | .mat-formio-component { 7 | grid-area: component; 8 | } 9 | 10 | .mat-formio-label { 11 | grid-area: label; 12 | } 13 | 14 | .mat-formio-label.mat-formio-label-align-right { 15 | margin-left: auto; 16 | } 17 | 18 | .mat-formio-label.mat-formio-label-align-left { 19 | margin-right: auto; 20 | } 21 | 22 | .mat-formio-component-wrapper.mat-formio-label-left, 23 | .mat-formio-component-wrapper.mat-formio-label-right { 24 | display: grid; 25 | grid-template-columns: auto auto; 26 | grid-column-gap: 1em; 27 | } 28 | 29 | .mat-formio-component-wrapper.mat-formio-label-right { 30 | grid-template-areas: 'component label' 31 | 'description label'; 32 | } 33 | 34 | .mat-formio-component-wrapper.mat-formio-label-left { 35 | grid-template-areas: 'label component' 36 | 'label description'; 37 | } 38 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/tabs/tabs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 3 | import TabsComponent from 'formiojs/components/tabs/Tabs.js'; 4 | 5 | @Component({ 6 | selector: 'mat-formio-tabs', 7 | template: ` 8 | 9 | 10 |
11 | 12 |
13 |
14 |
15 | ` 16 | }) 17 | export class MaterialTabsComponent extends MaterialNestedComponent { 18 | setInstance(instance: any) { 19 | super.setInstance(instance); 20 | instance.viewContainer = (component) => { 21 | return this.viewContainers ? this.viewContainers[component.tab] : null; 22 | }; 23 | } 24 | } 25 | TabsComponent.MaterialComponent = MaterialTabsComponent; 26 | export { TabsComponent }; 27 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/fieldset/fieldset.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import FieldsetComponent from 'formiojs/components/fieldset/Fieldset.js'; 3 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 4 | @Component({ 5 | selector: 'mat-formio-fieldset', 6 | template: ` 7 |
8 | 9 | {{ instance.component.legend }} 10 | 14 | info 15 | 16 | 17 |
18 | 19 |
20 |
21 | `, 22 | styles: [] 23 | }) 24 | export class MaterialFieldsetComponent extends MaterialNestedComponent {} 25 | FieldsetComponent.MaterialComponent = MaterialFieldsetComponent; 26 | export { FieldsetComponent }; 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Form.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # AngularMaterialFormio 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/Base.ts: -------------------------------------------------------------------------------- 1 | import FormioComponent from 'formiojs/components/_classes/component/Component.js'; 2 | 3 | export default (() => { 4 | const beforeSubmit = FormioComponent.prototype.beforeSubmit; 5 | FormioComponent.prototype.beforeSubmit = function(...args) { 6 | if (this.materialComponent) { 7 | this.materialComponent.beforeSubmit(); 8 | } 9 | return beforeSubmit.call(this, ...args); 10 | }; 11 | 12 | Object.defineProperty(FormioComponent.prototype, 'disabled', { 13 | set(disabled) { 14 | this._disabled = disabled; 15 | if (this.materialComponent) { 16 | this.materialComponent.setDisabled(disabled); 17 | } 18 | } 19 | }); 20 | 21 | Object.defineProperty(FormioComponent.prototype, 'visible', { 22 | set(visible) { 23 | if (this._visible !== visible) { 24 | this._visible = visible; 25 | this.clearOnHide(); 26 | this.redraw(); 27 | } 28 | if (this.materialComponent) { 29 | this.materialComponent.setVisible(visible); 30 | } 31 | } 32 | }); 33 | return FormioComponent; 34 | })(); 35 | 36 | -------------------------------------------------------------------------------- /demo/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/angular-material-formio'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /demo/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'angular-material-formio'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('angular-material-formio'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-material-formio!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/angular-material-formio/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/angular-material-formio'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/MaterialNestedComponent.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, QueryList, ViewChildren, ViewContainerRef } from '@angular/core'; 2 | import { MaterialComponent } from './MaterialComponent'; 3 | 4 | @Component({ 5 | selector: 'mat-formio-nested', 6 | template: '' 7 | }) 8 | export class MaterialNestedComponent extends MaterialComponent implements AfterViewInit { 9 | public viewContainers; 10 | @ViewChildren('components', {read: ViewContainerRef}) components: QueryList; 11 | setInstance(instance: any) { 12 | instance.viewContainer = () => { 13 | return this.viewContainers ? this.viewContainers[0] : null; 14 | }; 15 | super.setInstance(instance); 16 | } 17 | 18 | renderComponents() { 19 | if (this.instance.renderComponents) { 20 | this.instance.renderComponents(); 21 | } 22 | } 23 | 24 | render() { 25 | this.viewContainers = this.components.toArray(); 26 | this.renderComponents(); 27 | this.ref.detectChanges(); 28 | } 29 | 30 | ngAfterViewInit() { 31 | this.components.changes.subscribe(() => this.render()); 32 | this.render(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /projects/angular-material-formio/README.md: -------------------------------------------------------------------------------- 1 | # AngularMaterialFormio 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.1. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project angular-material-formio` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project angular-material-formio`. 8 | > Note: Don't forget to add `--project angular-material-formio` or else it will be added to the default project in your `angular.json` file. 9 | 10 | ## Build 11 | 12 | Run `ng build angular-material-formio` to build the project. The build artifacts will be stored in the `dist/` directory. 13 | 14 | ## Publishing 15 | 16 | After building your library with `ng build angular-material-formio`, go to the dist folder `cd dist/angular-material-formio` and run `npm publish`. 17 | 18 | ## Running unit tests 19 | 20 | Run `ng test angular-material-formio` to execute the unit tests via [Karma](https://karma-runner.github.io). 21 | 22 | ## Further help 23 | 24 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 25 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/checkbox/checkbox.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import CheckboxComponent from 'formiojs/components/checkbox/Checkbox.js'; 4 | import _ from 'lodash'; 5 | 6 | @Component({ 7 | selector: 'mat-formio-checkbox', 8 | template: ` 9 | 12 | 13 | info 15 | 16 | 17 | 18 | {{ instance.component.description }} 19 | 20 | {{ instance.error.message }} 21 | `, 22 | styles:['::ng-deep .mat-checkbox.validation-error .mat-checkbox-frame {border-color: red; }'] 23 | }) 24 | export class MaterialCheckboxComponent extends MaterialComponent { 25 | getValue() { 26 | return _.isNil(this.control.value) ? '' : this.control.value; 27 | } 28 | } 29 | CheckboxComponent.MaterialComponent = MaterialCheckboxComponent; 30 | export { CheckboxComponent }; 31 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/panel/panel.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 3 | import PanelComponent from 'formiojs/components/panel/Panel.js'; 4 | @Component({ 5 | selector: 'mat-formio-panel', 6 | template: ` 7 | 8 | 9 | {{ instance.component.title }} 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | {{ instance.component.title }} 21 | 22 | 23 | 24 | 25 | `, 26 | styles: [ 27 | ':host { margin-bottom: 1em; }' 28 | ] 29 | }) 30 | export class MaterialPanelComponent extends MaterialNestedComponent {} 31 | PanelComponent.MaterialComponent = MaterialPanelComponent; 32 | export { PanelComponent }; 33 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/FormioControl.ts: -------------------------------------------------------------------------------- 1 | import { FormControl, ValidationErrors } from '@angular/forms'; 2 | import unescape from 'lodash/unescape'; 3 | 4 | // @dynamic 5 | export class FormioControl extends FormControl { 6 | public instance: any; 7 | 8 | static customValidator(control: FormioControl): Promise { 9 | return new Promise((resolve) => { 10 | if (control.instance) { 11 | control.instance.validateResolve = resolve; 12 | } else { 13 | resolve(null); 14 | } 15 | }); 16 | } 17 | 18 | constructor(...args) { 19 | super(args[0], [], [FormioControl.customValidator.bind(FormioControl)]); 20 | } 21 | 22 | setInstance(instance: any) { 23 | this.instance = instance; 24 | const setCustomValidity = instance.setCustomValidity; 25 | instance.setCustomValidity = (message: any, dirty, external, isWarning = false) => { 26 | let decodedMessage = message; 27 | if (Array.isArray(message)) { 28 | decodedMessage = message.map(msg => Object.assign(msg, { message: unescape(msg.message) })); 29 | } 30 | else if (message) { 31 | decodedMessage = unescape(message); 32 | } 33 | 34 | setCustomValidity.call(instance, decodedMessage, dirty, external, isWarning); 35 | if (instance.validateResolve) { 36 | instance.validateResolve(decodedMessage ? {custom: true} : null); 37 | } 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/directives/mask.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, forwardRef, Input } from '@angular/core'; 2 | import { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input'; 3 | import { NG_VALUE_ACCESSOR } from '@angular/forms'; 4 | 5 | @Directive({ 6 | selector: 'input[matMask]', 7 | providers: [ 8 | {provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: MaskDirective}, 9 | { 10 | provide: NG_VALUE_ACCESSOR, 11 | useExisting: forwardRef(() => MaskDirective), 12 | multi: true, 13 | } 14 | ] 15 | }) 16 | export class MaskDirective { 17 | @Input('matMask') format: (value: string) => string; 18 | private _value: string | null; 19 | 20 | constructor(private elementRef: ElementRef) {} 21 | 22 | 23 | get value(): string | null { 24 | return this._value; 25 | } 26 | 27 | @Input('value') 28 | set value(value: string | null) { 29 | this._value = value; 30 | this.formatValue(value); 31 | } 32 | 33 | private formatValue(value: string | null) { 34 | if (value !== null) { 35 | this.elementRef.nativeElement.value = this.format(value); 36 | } 37 | else { 38 | this.elementRef.nativeElement.value = ''; 39 | } 40 | } 41 | 42 | _onChange(value: any): void { 43 | } 44 | 45 | writeValue(value: any) { 46 | this._value = value; 47 | this.formatValue(this._value); // format Value 48 | } 49 | 50 | registerOnChange(fn: (value: any) => void) { 51 | this._onChange = fn; 52 | } 53 | 54 | registerOnTouched() { 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/formio-form-field/formio-form-field.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, TemplateRef } from '@angular/core'; 2 | import { LabelPositions } from '../../const/LabelPositions'; 3 | 4 | @Component({ 5 | selector: 'mat-formio-form-field', 6 | templateUrl: './formio-form-field.component.html', 7 | styleUrls: ['./formio-form-field.component.css'] 8 | }) 9 | export class FormioFormFieldComponent { 10 | private _instance; 11 | public labelPositions = LabelPositions; 12 | @Input() labelTemplate: TemplateRef; 13 | @Input() renderTopLabel = false; 14 | @Input() showDescription = true; 15 | @Input() renderElementOnly = false; 16 | 17 | @Input('instance') 18 | set instance(instance) { 19 | this._instance = instance; 20 | if (instance) { 21 | this.componentTemplateContext = {$implicit: this.hasLabel(['top'])}; 22 | } 23 | } 24 | 25 | get instance() { 26 | return this._instance; 27 | } 28 | 29 | @Input() componentTemplate: TemplateRef; 30 | componentTemplateContext; 31 | 32 | hasLabel(labelPositions?: string[]) { 33 | const { component } = this.instance; 34 | const hasNoLabel = !component.label || component.hideLabel; 35 | const labelPositionIsNotSpecified = !labelPositions || 36 | !labelPositions.length || 37 | !component.labelPosition; 38 | 39 | if (hasNoLabel || labelPositionIsNotSpecified || this.renderElementOnly) { 40 | return false; 41 | } 42 | 43 | if (labelPositions.includes(component.labelPosition)) { 44 | return true; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/columns/columns.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 3 | import ColumnsComponent from 'formiojs/components/columns/Columns.js'; 4 | 5 | @Component({ 6 | selector: 'mat-formio-columns', 7 | template: ` 8 |
15 |
20 | 21 |
22 |
23 | `, 24 | styles: [] 25 | }) 26 | export class MaterialColumnsComponent extends MaterialNestedComponent { 27 | public flexGap = 0.5; 28 | public totalSpace = 0; 29 | setInstance(instance: any) { 30 | this.totalSpace = 100 - ((instance.component.columns.length - 1) * this.flexGap); 31 | super.setInstance(instance); 32 | instance.viewContainer = (component) => { 33 | return this.viewContainers ? this.viewContainers[component.column] : null; 34 | }; 35 | } 36 | 37 | flexWidth(column, index) { 38 | if (index >= (this.instance.component.columns.length - 1)) { 39 | return Math.ceil(((parseFloat(column.width) / 12) * this.totalSpace)) + '%'; 40 | } else { 41 | return Math.floor(((parseFloat(column.width) / 12) * this.totalSpace)) + '%'; 42 | } 43 | } 44 | } 45 | ColumnsComponent.MaterialComponent = MaterialColumnsComponent; 46 | export { ColumnsComponent }; 47 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-material-formio": { 7 | "projectType": "library", 8 | "root": "projects/angular-material-formio", 9 | "sourceRoot": "projects/angular-material-formio/src", 10 | "prefix": "mat", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-ng-packagr:build", 14 | "options": { 15 | "tsConfig": "projects/angular-material-formio/tsconfig.lib.json", 16 | "project": "projects/angular-material-formio/ng-package.json" 17 | } 18 | , "configurations": { 19 | "production": { 20 | "tsConfig": "projects/angular-material-formio/tsconfig.lib.prod.json" 21 | } 22 | } 23 | }, 24 | "test": { 25 | "builder": "@angular-devkit/build-angular:karma", 26 | "options": { 27 | "main": "projects/angular-material-formio/src/test.ts", 28 | "tsConfig": "projects/angular-material-formio/tsconfig.spec.json", 29 | "karmaConfig": "projects/angular-material-formio/karma.conf.js" 30 | } 31 | }, 32 | "lint": { 33 | "builder": "@angular-devkit/build-angular:tslint", 34 | "options": { 35 | "tsConfig": [ 36 | "projects/angular-material-formio/tsconfig.lib.json", 37 | "projects/angular-material-formio/tsconfig.spec.json" 38 | ], 39 | "exclude": [ 40 | "**/node_modules/**" 41 | ] 42 | } 43 | } 44 | } 45 | }}, 46 | "defaultProject": "angular-material-formio" 47 | } 48 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/number/number.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Renderer2 } from '@angular/core'; 2 | import { MaterialTextfieldComponent, TEXTFIELD_TEMPLATE } from '../textfield/textfield.component'; 3 | import NumberComponent from 'formiojs/components/number/Number.js'; 4 | import _ from 'lodash'; 5 | 6 | @Component({ 7 | selector: 'mat-formio-number', 8 | template: TEXTFIELD_TEMPLATE 9 | }) 10 | export class MaterialNumberComponent extends MaterialTextfieldComponent implements AfterViewInit { 11 | public inputType = 'text'; 12 | 13 | constructor(public element: ElementRef, public ref: ChangeDetectorRef, private renderer: Renderer2) { 14 | super(element, ref); 15 | } 16 | 17 | ngAfterViewInit() { 18 | super.ngAfterViewInit(); 19 | if (this.instance) { 20 | const { instance } = this; 21 | 22 | this.renderer.listen(this.input.nativeElement, 'blur', () => { 23 | let value = instance.parseValue(this.control.value); 24 | value = instance.formatValue(value); 25 | value = instance.getValueAsString(value); 26 | this.control.setValue(value); 27 | }); 28 | 29 | } 30 | } 31 | 32 | getValue() { 33 | let value = this.control.value; 34 | if (value && this.instance) { 35 | value = value.replace(this.instance.prefix, ''); 36 | return !_.isNil(value) && value !== '' ? this.instance.parseNumber(value) : value; 37 | } 38 | return value; 39 | } 40 | 41 | setValue(value) { 42 | if (this.instance) { 43 | const { instance } = this; 44 | value = instance.formatValue(instance.parseValue(value)); 45 | } 46 | else { 47 | value = value.toString(); 48 | } 49 | 50 | return super.setValue(value); 51 | } 52 | 53 | onChange() { 54 | super.onChange(true); 55 | } 56 | } 57 | NumberComponent.MaterialComponent = MaterialNumberComponent; 58 | export { NumberComponent }; 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@formio/angular-material", 3 | "version": "2.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": false, 13 | "peerDependencies": { 14 | "@formio/angular": "^5.0.0-rc.4", 15 | "formiojs": "^4.12.4-rc.3", 16 | "@angular/cdk": "^10.0.0 | ^11.0.0", 17 | "@angular/flex-layout": "^10.0.0 | ^11.0.0", 18 | "@angular/material": "^10.0.0 | ^11.0.0" 19 | }, 20 | "dependencies": { 21 | "tslib": "^2.0.0" 22 | }, 23 | "devDependencies": { 24 | "@angular-devkit/build-angular": "^0.1100.4", 25 | "@angular-devkit/build-ng-packagr": "~0.1002.0", 26 | "@angular/animations": "^11.0.4", 27 | "@angular/cdk": "^11.0.3", 28 | "@angular/cli": "^11.0.4", 29 | "@angular/common": "^11.0.4", 30 | "@angular/compiler": "^11.0.4", 31 | "@angular/compiler-cli": "^11.0.4", 32 | "@angular/core": "^11.0.4", 33 | "@angular/elements": "^11.0.4", 34 | "@angular/flex-layout": "^10.0.0-beta.32", 35 | "@angular/forms": "^11.0.4", 36 | "@angular/language-service": "^11.0.4", 37 | "@angular/material": "^11.0.3", 38 | "@angular/platform-browser": "^11.0.4", 39 | "@formio/angular": "^5.0.0-rc.4", 40 | "@types/jasmine": "^3.6.2", 41 | "@types/jasminewd2": "^2.0.8", 42 | "@types/node": "^14.14.14", 43 | "codelyzer": "^6.0.1", 44 | "formiojs": "^4.12.4-rc.3", 45 | "jasmine-core": "^3.6.0", 46 | "jasmine-spec-reporter": "^6.0.0", 47 | "karma": "^5.2.3", 48 | "karma-chrome-launcher": "~3.1.0", 49 | "karma-coverage-istanbul-reporter": "~3.0.2", 50 | "karma-jasmine": "~4.0.0", 51 | "karma-jasmine-html-reporter": "^1.5.0", 52 | "ng-packagr": "^11.0.3", 53 | "protractor": "~7.0.0", 54 | "rxjs": "^6.6.3", 55 | "ts-node": "^9.1.1", 56 | "tslint": "^6.1.3", 57 | "typescript": "~4.0.5", 58 | "zone.js": "^0.11.3" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent { 9 | form: any = { 10 | components: [ 11 | {type: 'my-custom-component'}, 12 | { 13 | type: 'textfield', 14 | label: 'First Name', 15 | key: 'firstName', 16 | validate: { 17 | required: true 18 | } 19 | }, 20 | { 21 | type: 'textfield', 22 | label: 'Last Name', 23 | key: 'lastName', 24 | validate: { 25 | required: true 26 | } 27 | }, 28 | { 29 | type: 'select', 30 | key: 'favoriteColor', 31 | label: 'Favorite Color', 32 | defaultValue: 'orange', 33 | dataSrc: 'values', 34 | data: { 35 | values: [ 36 | { 37 | label: 'Apple', 38 | value: 'apple' 39 | }, 40 | { 41 | label: 'Banana', 42 | value: 'banana' 43 | }, 44 | { 45 | label: 'Orange', 46 | value: 'orange' 47 | } 48 | ] 49 | } 50 | }, 51 | { 52 | type: 'email', 53 | label: 'Email', 54 | key: 'email' 55 | }, 56 | { 57 | type: 'phoneNumber', 58 | label: 'Phone Number', 59 | key: 'phoneNumber' 60 | }, 61 | { 62 | type: 'currency', 63 | label: 'Salary', 64 | key: 'salary' 65 | }, 66 | { 67 | key: 'submit', 68 | type: 'button', 69 | disableOnInvalid: true, 70 | action: 'submit', 71 | label: 'Submit' 72 | } 73 | ] 74 | }; 75 | submission: any = { 76 | data: { 77 | firstName: 'Joe', 78 | lastName: 'Smith', 79 | email: 'joe@example.com', 80 | favoriteColor: 'banana', 81 | phoneNumber: '(123) 456-7890', 82 | salary: 45000 83 | } 84 | }; 85 | onSubmit(submission) { 86 | console.log(submission); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/radio/radio.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import RadioComponent from 'formiojs/components/radio/Radio.js'; 4 | 5 | @Component({ 6 | selector: 'mat-formio-radio', 7 | template: ` 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 22 | 28 | {{ option.label }} 29 | 30 | {{ instance.error.message }} 31 | 32 |
33 |
34 | ` 35 | }) 36 | export class MaterialRadioComponent extends MaterialComponent { 37 | getLayout() { 38 | return this.instance.component.inline ? 'row' : 'column'; 39 | } 40 | 41 | isRadioChecked(option) { 42 | return option.value === this.instance.dataValue; 43 | } 44 | 45 | clearValue(event, option) { 46 | if (this.isRadioChecked(option)) { 47 | event.preventDefault(); 48 | this.deselectValue(); 49 | } 50 | } 51 | 52 | deselectValue() { 53 | this.instance.updateValue(null, { 54 | modified: true, 55 | }); 56 | } 57 | } 58 | RadioComponent.MaterialComponent = MaterialRadioComponent; 59 | export { RadioComponent }; 60 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/textarea/textarea.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import TextAreaComponent from 'formiojs/components/textarea/TextArea.js'; 4 | import isNil from 'lodash/isNil'; 5 | 6 | @Component({ 7 | selector: 'mat-formio-textarea', 8 | styleUrls: ['./textarea.component.css'], 9 | template: ` 10 | 11 | 12 | 17 | 18 | 19 | 20 | {{ instance.component.prefix }}  21 | 29 | {{ instance.component.suffix }} 30 | {{ instance.error.message }} 31 | 32 | 33 | ` 34 | }) 35 | export class MaterialTextareaComponent extends MaterialComponent implements AfterViewInit { 36 | @ViewChild('textarea') textarea: ElementRef; 37 | 38 | ngAfterViewInit() { 39 | // Attach the element so the wysiwyg will work. 40 | this.instance.attachElement(this.textarea.nativeElement); 41 | } 42 | 43 | getValue() { 44 | return isNil(this.control.value) ? '' : this.control.value; 45 | } 46 | } 47 | TextAreaComponent.MaterialComponent = MaterialTextareaComponent; 48 | export { TextAreaComponent }; 49 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/table/table.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 3 | import TableComponent from 'formiojs/components/table/Table.js'; 4 | 5 | @Component({ 6 | selector: 'mat-formio-table', 7 | styleUrls: [ './table.component.css' ], 8 | template: ` 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 29 | 36 | 37 | 38 |
15 | {{ instance.t(header) }} 16 |
34 | 35 |
39 | ` 40 | }) 41 | export class MaterialTableComponent extends MaterialNestedComponent { 42 | 43 | setInstance(instance: any) { 44 | super.setInstance(instance); 45 | instance.viewContainer = (component) => { 46 | return this.viewContainers ? 47 | this.viewContainers[(component.tableRow * this.instance.component.numCols) + component.tableColumn] : 48 | null; 49 | }; 50 | } 51 | 52 | getTableColClasses() { 53 | if (!this.instance) { 54 | return; 55 | } 56 | const {condensed, cellAlignment} = this.instance.component; 57 | return { 58 | 'is-condensed': condensed, 59 | ...(cellAlignment && {[cellAlignment]: true}) 60 | } 61 | } 62 | } 63 | TableComponent.MaterialComponent = MaterialTableComponent; 64 | export { TableComponent }; 65 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/signature/signature.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import SignatureComponent from 'formiojs/components/signature/Signature.js'; 4 | @Component({ 5 | selector: 'mat-formio-signature', 6 | template: ` 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 |
24 | 28 | 29 |
30 | 37 |
38 |
39 | ` 40 | }) 41 | export class MaterialSignatureComponent extends MaterialComponent implements AfterViewInit { 42 | @ViewChild('signature') signatureElement: ElementRef; 43 | 44 | renderComponents() { 45 | if (this.signatureElement) { 46 | this.instance.attach(this.signatureElement.nativeElement); 47 | } 48 | } 49 | 50 | ngAfterViewInit() { 51 | this.renderComponents(); 52 | } 53 | } 54 | SignatureComponent.MaterialComponent = MaterialSignatureComponent; 55 | export { SignatureComponent }; 56 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/editgrid/editgrid.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This repository is now considered legacy and no longer supported. Please take a look at our recent repositories and help documentation at the following links. 2 | - https://help.form.io 3 | - https://github.com/formio/formio.js 4 | - https://github.com/formio/formio 5 | - https://github.com/formio/react 6 | - https://github.com/formio/angular 7 | - https://github.com/formio/vue 8 | 9 | # Dynamic JSON Powered forms for Angular Material 10 | 11 | This library introduces the powerful Form.io JSON forms into the Angular Material framework. 12 | 13 | ## Installation 14 | 15 | To install this library into your application, you will need to run the following. 16 | 17 | ``` 18 | npm install formiojs @formio/angular @formio/angular-material 19 | ``` 20 | 21 | Then, you will need to add the following to your codebase. 22 | 23 | ***src/app/app.module.ts*** 24 | ```ts 25 | import { MatFormioModule } from '@formio/angular-material'; 26 | 27 | @NgModule({ 28 | declarations: [ 29 | AppComponent 30 | ], 31 | imports: [ 32 | BrowserModule, 33 | BrowserAnimationsModule, 34 | MatFormioModule 35 | ], 36 | providers: [], 37 | bootstrap: [AppComponent] 38 | }) 39 | export class AppModule { } 40 | ``` 41 | 42 | ## Using this library 43 | This library can be used to render dynamic JSON powered forms within any Angular Material application. This uses the exact same syntax as the `````` component within the [Angular Form.io Library](https://github.com/formio/angular-formio). The only difference is that you will use the `````` directive instead. 44 | 45 | For example, the following will render a dynamic JSON form within your application. 46 | 47 | ```html 48 | 49 | ``` 50 | 51 | You can also use this to render JSON forms as follows. 52 | 53 | ```html 54 | 74 | ``` 75 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-material-formio-demo", 3 | "version": "2.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^11.0.5", 15 | "@angular/cdk": "^11.0.3", 16 | "@angular/common": "^11.0.5", 17 | "@angular/compiler": "^11.0.5", 18 | "@angular/core": "^11.0.5", 19 | "@angular/elements": "^11.0.5", 20 | "@angular/flex-layout": "^10.0.0-beta.32", 21 | "@angular/forms": "^11.0.5", 22 | "@angular/material": "^11.0.3", 23 | "@angular/platform-browser": "^11.0.5", 24 | "@angular/platform-browser-dynamic": "^11.0.5", 25 | "@angular/router": "^11.0.5", 26 | "@formio/angular-material": "^2.0.0", 27 | "@formio/angular": "^5.0.0-rc.4", 28 | "formiojs": "^4.12.3", 29 | "hammerjs": "^2.0.8", 30 | "rxjs": "^6.6.3", 31 | "tslib": "^2.0.0", 32 | "zone.js": "^0.11.3" 33 | }, 34 | "devDependencies": { 35 | "@angular-devkit/build-angular": "^0.1100.4", 36 | "@angular/cli": "^11.0.4", 37 | "@angular/compiler-cli": "^11.0.5", 38 | "@angular/language-service": "^11.0.5", 39 | "@types/jasmine": "^3.6.2", 40 | "@types/jasminewd2": "^2.0.8", 41 | "@types/node": "^14.14.14", 42 | "codelyzer": "^6.0.1", 43 | "jasmine-core": "^3.6.0", 44 | "jasmine-spec-reporter": "^6.0.0", 45 | "karma": "^5.2.3", 46 | "karma-chrome-launcher": "~3.1.0", 47 | "karma-coverage-istanbul-reporter": "~3.0.2", 48 | "karma-jasmine": "~4.0.0", 49 | "karma-jasmine-html-reporter": "^1.5.0", 50 | "protractor": "~7.0.0", 51 | "ts-node": "^9.1.1", 52 | "tslint": "^6.1.3", 53 | "typescript": "~4.0.5" 54 | }, 55 | "description": "This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.0.", 56 | "main": "karma.conf.js", 57 | "repository": { 58 | "type": "git", 59 | "url": "git+https://github.com/formio/angular-material-formio-demo.git" 60 | }, 61 | "author": "", 62 | "license": "MIT", 63 | "bugs": { 64 | "url": "https://github.com/formio/angular-material-formio-demo/issues" 65 | }, 66 | "homepage": "https://github.com/formio/angular-material-formio-demo#readme" 67 | } 68 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/formio-form-field/formio-form-field.component.html: -------------------------------------------------------------------------------- 1 |
7 | 14 | 15 | 16 | 17 |
18 | 19 | 32 | 33 | 34 | 35 | 36 | {{ instance.component.description }} 37 | 38 |
39 | 40 |
41 |
42 |
43 | 44 | 45 | 46 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/selectboxes/selectboxes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialRadioComponent } from '../radio/radio.component'; 3 | import SelectBoxesComponent from 'formiojs/components/selectboxes/SelectBoxes.js'; 4 | @Component({ 5 | selector: 'mat-formio-selectboxes', 6 | template: ` 7 | 8 | 9 |
10 | 11 | 12 | 13 |
18 | 24 | {{ option.label }} 25 | 26 | {{ instance.error.message }} 27 |
28 |
29 |
30 | ` 31 | }) 32 | export class MaterialSelectBoxesComponent extends MaterialRadioComponent { 33 | public value: any = {}; 34 | public disabled = false; 35 | 36 | setInstance(instance) { 37 | instance.component.values.forEach((option) => { 38 | this.value[option.value] = false; 39 | }); 40 | super.setInstance(instance); 41 | } 42 | 43 | getValue() { 44 | return this.value; 45 | } 46 | 47 | setValue(value) { 48 | const normalizedValue = this.instance.normalizeValue(value); 49 | for (const prop in normalizedValue) { 50 | if (normalizedValue.hasOwnProperty(prop)) { 51 | this.value[prop] = normalizedValue[prop]; 52 | } 53 | } 54 | } 55 | 56 | setDisabled(disabled) { 57 | this.disabled = !!disabled; 58 | } 59 | 60 | onChange() { 61 | this.instance.updateValue(this.getValue(), {modified: true}); 62 | this.instance.triggerChange({modified: true}); 63 | } 64 | } 65 | SelectBoxesComponent.MaterialComponent = MaterialSelectBoxesComponent; 66 | export { SelectBoxesComponent }; 67 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/tags/tags.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { COMMA, ENTER } from '@angular/cdk/keycodes'; 3 | import TagsComponent from 'formiojs/components/tags/Tags.js'; 4 | import { MaterialComponent } from '../MaterialComponent'; 5 | import { MatChipInputEvent } from '@angular/material/chips'; 6 | 7 | @Component({ 8 | selector: 'mat-formio-tags', 9 | template: ` 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | {{tag}} 25 | cancel 26 | 27 | 28 | 34 | 35 | 36 | 37 | ` 38 | }) 39 | export class MaterialTagsComponent extends MaterialComponent { 40 | readonly separatorKeysCodes: number[] = [ENTER, COMMA]; 41 | tags: string[] = []; 42 | add(event: MatChipInputEvent): void { 43 | const input = event.input; 44 | const value = event.value; 45 | if ((value || '').trim()) { 46 | this.tags.push(value.trim()); 47 | } 48 | if (input) { 49 | input.value = ''; 50 | } 51 | this.onChange(); 52 | } 53 | 54 | remove(index): void { 55 | if (index >= 0 && index < this.tags.length) { 56 | this.tags.splice(index, 1); 57 | } 58 | this.onChange(); 59 | } 60 | 61 | getValue() { 62 | return (this.instance.component.storeas === 'string') ? this.tags.join(this.instance.delimiter) : this.tags; 63 | } 64 | 65 | setValue(value) { 66 | if (typeof value === 'string') { 67 | value = value.split(this.instance.delimiter); 68 | } 69 | if (value && !Array.isArray(value)) { 70 | value = [value]; 71 | } 72 | this.tags = value; 73 | } 74 | } 75 | (TagsComponent as any).MaterialComponent = MaterialTagsComponent; 76 | export { TagsComponent }; 77 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/formio.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Optional, ChangeDetectorRef, ViewContainerRef, ViewChild, ComponentFactoryResolver, NgZone, Input, Output } from '@angular/core'; 2 | import { FormioAppConfig, FormioBaseComponent } from '@formio/angular'; 3 | import { Form } from './renderer'; 4 | import { get } from 'lodash'; 5 | @Component({ 6 | selector: 'mat-formio', 7 | styles: [ 8 | `.alert-danger { 9 | color: #721c24; 10 | background-color: #f8d7da; 11 | border-color: #f5c6cb; 12 | } 13 | .alert-success { 14 | color: #155724; 15 | background-color: #d4edda; 16 | border-color: #c3e6cb; 17 | } 18 | .alert { 19 | position: relative; 20 | padding: .75rem 1.25rem; 21 | margin-bottom: 0.5rem; 22 | border: 1px solid transparent; 23 | border-radius: .25rem; 24 | } 25 | ::ng-deep mat-card { 26 | box-sizing: border-box; 27 | -moz-box-sizing: border-box; 28 | } 29 | ` 30 | ], 31 | template: ` 32 | 33 |
34 | 40 |
41 |
42 | 43 |
44 | ` 45 | }) 46 | export class FormioComponent extends FormioBaseComponent { 47 | @ViewChild('formio', {static: true, read: ViewContainerRef}) formioViewContainer: ViewContainerRef; 48 | constructor( 49 | private resolver: ComponentFactoryResolver, 50 | private cd: ChangeDetectorRef, 51 | public ngZone: NgZone, 52 | @Optional() public config: FormioAppConfig 53 | ) { 54 | super(ngZone, config); 55 | } 56 | 57 | getRendererOptions(): any { 58 | const rendererOptions = super.getRendererOptions(); 59 | return {...rendererOptions, validateOnInit: get(rendererOptions, 'validateOnInit', true) } 60 | } 61 | 62 | createRenderer() { 63 | const options = this.getRendererOptions(); 64 | const flags = { 65 | validateOnInit: options.validateOnInit 66 | }; 67 | options.viewResolver = this.resolver; 68 | const form = new Form(); 69 | form._form = this.form; 70 | form.options = options; 71 | form.options.events = form.events; 72 | form.instance = form.create(this.form.display); 73 | form.instance.viewContainer = () => this.formioViewContainer; 74 | if (this.submission && this.submission.data) { 75 | form.instance.data = this.submission.data; 76 | } 77 | this.ngZone.run(() => form.instance.setForm(this.form, flags) 78 | .then(() => form.readyResolve(form.instance)) 79 | .catch(() => form.readyReject()) 80 | ); 81 | return form.instance; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/calendar/calendar.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Input, NgModule, Output, ViewChild} from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | @Component({ 4 | selector: 'mat-formio-calendar', 5 | styles: [ 6 | `.calendar, .formio-time { 7 | padding: 16px; 8 | background-color: white; 9 | box-shadow: 0 2px 1px -1px rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 1px 3px 0 rgba(0,0,0,.12); 10 | } 11 | .formio-time { 12 | display: flex; 13 | } 14 | ` 15 | ], 16 | template: ` 17 |
18 |
19 | 20 | 29 | 30 | 40 | 41 | 42 |
43 |
44 | ` 45 | }) 46 | 47 | export class MaterialCalendarComponent extends MaterialComponent { 48 | public selectedDate: any; 49 | public selectedTime: any; 50 | public selectedTimeComponent: any; 51 | 52 | @ViewChild('time') time; 53 | 54 | @Input() enableDate: boolean; 55 | @Input() enableTime: boolean; 56 | @Input() minDate: any; 57 | @Input() maxDate: any; 58 | @Input() dateFilter: any; 59 | @Input() hourStep: any; 60 | @Input() minuteStep: any; 61 | @Output() timeSelectEvent = new EventEmitter(); 62 | @Output() dateSelectEvent = new EventEmitter(); 63 | 64 | setInstance(instance: any) { 65 | super.setInstance(instance); 66 | } 67 | 68 | setExistedDate(value) { 69 | this.selectedDate = value; 70 | } 71 | 72 | setExistedTime(value, forTime) { 73 | this.selectedTime = value; 74 | this.time.setValue(forTime); 75 | } 76 | 77 | onDate(event){ 78 | this.selectedDate = event; 79 | this.dateSelectEvent.emit(this.selectedDate); 80 | } 81 | 82 | onTime(event) { 83 | this.selectedTime = event.value; 84 | this.timeSelectEvent.emit(this.selectedTime); 85 | } 86 | 87 | getPopupStyles() { 88 | return { 89 | position: 'absolute', 90 | zIndex: '1000', 91 | display: 'flex', 92 | maxWidth: '100%', 93 | maxHeight: '100%', 94 | top: '90px', 95 | left: '30px' 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | // (window as any).global = window; 65 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/formio.wizard.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core'; 2 | import { MatStepper } from '@angular/material/stepper'; 3 | import Wizard from 'formiojs/Wizard'; 4 | import Displays from 'formiojs/displays/Displays'; 5 | import { MaterialNestedComponent } from './MaterialNestedComponent'; 6 | 7 | @Component({ 8 | selector: 'mat-formio-wizard', 9 | styles: [ 10 | ':host .navigation-button-row { margin-top: 8px; }', 11 | ':host .navigation-button-row button { margin-right: 8px; }' 12 | ], 13 | template: ` 14 | 15 | 16 | 17 | 23 | 24 | ` 25 | }) 26 | export class MaterialWizardComponent extends MaterialNestedComponent { 27 | @ViewChild('stepper', {static: true}) stepper: MatStepper; 28 | public isLinear = true; 29 | private prevNumOfPages = 0; 30 | 31 | setInstance(instance: any) { 32 | this.isLinear = ( 33 | instance.options && 34 | instance.options.breadcrumbSettings && 35 | instance.options.breadcrumbSettings.clickable 36 | ) ? false : true; 37 | 38 | this.updatePages(instance); 39 | instance.on('pagesChanged', () => this.updatePages()); 40 | 41 | super.setInstance(instance); 42 | } 43 | 44 | updatePages(instance = this.instance) { 45 | if (this.prevNumOfPages !== instance.pages.length) { 46 | instance.pages.forEach((page, pageIndex) => { 47 | page.viewContainer = () => { 48 | return this.viewContainers ? this.viewContainers[pageIndex] : null; 49 | }; 50 | }); 51 | this.prevNumOfPages = instance.pages.length; 52 | } 53 | } 54 | 55 | resetWizard() { 56 | this.instance.cancel(); 57 | this.stepper.reset(); 58 | } 59 | 60 | nextPage() { 61 | this.instance.nextPage().then(() => this.stepper.next()); 62 | } 63 | 64 | prevPage() { 65 | this.instance.prevPage().then(() => this.stepper.previous()); 66 | } 67 | 68 | submit() { 69 | this.instance.submit(); 70 | } 71 | 72 | renderComponents() { 73 | if (this.instance.renderComponents && this.instance.pages) { 74 | this.instance.renderComponents(this.instance.pages.reduce((comps, page) => { 75 | return comps.concat(page.components); 76 | }, [])); 77 | } 78 | } 79 | } 80 | Wizard.MaterialComponent = MaterialWizardComponent; 81 | Displays.addDisplay('wizard', Wizard); 82 | export { Wizard }; 83 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/button/button.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import ButtonComponent from 'formiojs/components/button/Button.js'; 3 | import { MaterialComponent } from '../MaterialComponent'; 4 | import { AngularButtonsThemes, ButtonsThemes } from '../../const/ButtonsThemes'; 5 | 6 | @Component({ 7 | selector: 'mat-formio-button', 8 | templateUrl: './button.component.html', 9 | styleUrls: ['./button.component.css'] 10 | }) 11 | export class MaterialButtonComponent extends MaterialComponent { 12 | public loading = false; 13 | public done = false; 14 | public error = false; 15 | public disabled = false; 16 | public clicked = false; 17 | 18 | get color() { 19 | if (this.error) { 20 | return AngularButtonsThemes.WARN; 21 | } 22 | const theme = this.angularButtonTheme; 23 | return theme || AngularButtonsThemes.PRIMARY; 24 | } 25 | 26 | get angularButtonTheme() { 27 | switch (this.instance.component.theme) { 28 | case ButtonsThemes.PRIMARY: 29 | return AngularButtonsThemes.PRIMARY; 30 | 31 | case ButtonsThemes.WARNING: 32 | return AngularButtonsThemes.ACCENT; 33 | 34 | case ButtonsThemes.DANGER: 35 | return AngularButtonsThemes.WARN; 36 | 37 | case ButtonsThemes.SECONDARY: 38 | return AngularButtonsThemes.BASIC; 39 | 40 | default: 41 | return ''; 42 | } 43 | } 44 | 45 | get buttonClass() { 46 | let className = this.instance.component.block ? 'mat-formio-button-block' : ''; 47 | className += this.instance.component.size ? ` mat-formio-button-${this.instance.component.size}` : ''; 48 | className += !this.angularButtonTheme ? ` mat-formio-theme-${this.instance.component.theme}` : ''; 49 | return className; 50 | } 51 | 52 | onClick(event) { 53 | this.clicked = true; 54 | this.instance.onClick(event); 55 | } 56 | 57 | getValue() { 58 | return this.clicked; 59 | } 60 | 61 | setState(loading, error, done) { 62 | this.loading = loading; 63 | this.done = done; 64 | this.error = error; 65 | } 66 | 67 | getIconFontSet(icon: string) { 68 | const fontSet = icon.split(' ')[0]; 69 | return fontSet; 70 | } 71 | 72 | getIconName(icon: string) { 73 | return icon.replace(this.getIconFontSet(icon), ''); 74 | } 75 | 76 | setInstance(instance) { 77 | const retVal = super.setInstance(instance); 78 | this.disabled = instance.shouldDisabled; 79 | instance.on('submitButton', () => this.setState(true, false, false)); 80 | instance.on('submitDone', () => this.setState(false, false, true)); 81 | instance.on('submitError', () => this.setState(false, true, false)); 82 | instance.on('requestButton', () => this.setState(true, false, false)); 83 | instance.on('requestDone', () => this.setState(false, false, true)); 84 | instance.on('change', (event) => { 85 | this.disabled = this.instance.shouldDisabled || (this.instance.component.disableOnInvalid && !event.isValid); 86 | if (event.isValid) { 87 | this.loading = false; 88 | this.error = false; 89 | } 90 | }); 91 | return retVal; 92 | } 93 | } 94 | ButtonComponent.MaterialComponent = MaterialButtonComponent; 95 | export { ButtonComponent }; 96 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/select/select.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import SelectComponent from 'formiojs/components/select/Select.js'; 4 | import _ from 'lodash'; 5 | @Component({ 6 | selector: 'mat-formio-select', 7 | template: ` 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{ instance.component.prefix }}  18 | 19 | 26 |
27 | 28 |
29 | 30 | Nothing was found 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | {{ instance.component.suffix }} 39 | 40 | {{ instance.error.message }} 41 |
42 |
43 | ` 44 | }) 45 | export class MaterialSelectComponent extends MaterialComponent implements OnInit { 46 | selectOptions: Promise; 47 | filteredOptions: Promise; 48 | filteredOptionsLength: number; 49 | 50 | selectOptionsResolve: any; 51 | 52 | setInstance(instance: any) { 53 | super.setInstance(instance); 54 | this.instance.triggerUpdate(); 55 | } 56 | 57 | ngOnInit() { 58 | this.selectOptions = new Promise((resolve) => { 59 | this.selectOptionsResolve = resolve; 60 | }); 61 | this.selectOptions.then((options) => { 62 | this.filteredOptionsLength = options.length; 63 | }) 64 | 65 | this.filteredOptions = this.selectOptions; 66 | } 67 | 68 | onFilter(value) { 69 | this.filteredOptions = this.selectOptions.then((options) => { 70 | const filtered = options.filter((option) => option.label.indexOf(value) !== -1); 71 | this.filteredOptionsLength = filtered.length; 72 | return filtered; 73 | }) 74 | } 75 | 76 | compareObjects(o1: any, o2: any): boolean { 77 | return _.isEqual(o1, o2); 78 | } 79 | } 80 | SelectComponent.MaterialComponent = MaterialSelectComponent; 81 | 82 | // Make sure we detect changes when new items make their way into the select dropdown. 83 | const setItems = SelectComponent.prototype.setItems; 84 | SelectComponent.prototype.setItems = function(...args) { 85 | setItems.call(this, ...args); 86 | if (this.materialComponent && this.materialComponent.selectOptionsResolve) { 87 | this.materialComponent.selectOptionsResolve(this.selectOptions); 88 | } 89 | }; 90 | 91 | export { SelectComponent }; 92 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/textfield/textfield.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, AfterContentInit } from '@angular/core'; 2 | import TextFieldComponent from 'formiojs/components/textfield/TextField.js'; 3 | import { MaterialComponent } from '../MaterialComponent'; 4 | 5 | export const TEXTFIELD_TEMPLATE = ` 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | {{ instance.component.prefix }}  18 | 19 | 25 | {{ instance.component.suffix }} 26 | 27 | 28 | {{ getHint() }} 29 | 30 | 31 |
32 | {{ getErrorMessage() }} 33 |
34 |
35 | `; 36 | 37 | @Component({ 38 | selector: 'mat-formio-textfield', 39 | template: TEXTFIELD_TEMPLATE 40 | }) 41 | export class MaterialTextfieldComponent extends MaterialComponent implements AfterContentInit { 42 | public inputType = 'text'; 43 | 44 | ngAfterContentInit() { 45 | if (this.instance && this.control && this.instance.component.disabled) { 46 | this.control.disable(); 47 | } 48 | } 49 | 50 | getHint() { 51 | if (!this.instance || !this.control || !this.control.value) { 52 | return ''; 53 | } 54 | 55 | const {showWordCount, showCharCount} = this.instance.component; 56 | 57 | if (showWordCount && showCharCount) { 58 | return `${this.getWordsCount()} words, ${this.control.value.length} characters`; 59 | } else if (showWordCount) { 60 | return `${this.getWordsCount()} words`; 61 | } else { 62 | return `${this.control.value.length} characters`; 63 | } 64 | } 65 | 66 | getWordsCount() { 67 | const matches = this.control.value ? this.control.value.match(/[\w\d’'-]+/gi) : []; 68 | return matches ? matches.length : 0; 69 | } 70 | 71 | getFormFieldAppearance() { 72 | const appearances = ['legacy', 'standard', 'fill', 'outline']; 73 | const appearance = this.instance.component.appearance ? this.instance.component.appearance.toLowerCase() : ''; 74 | return appearances.includes(appearance) ? appearance : undefined; 75 | } 76 | 77 | isError() { 78 | if (this.instance.error) { 79 | this.control.setErrors(this.instance.component.validate); 80 | return true; 81 | } 82 | else { 83 | return false; 84 | } 85 | } 86 | 87 | getErrorMessage() { 88 | if (this.instance.error && this.instance.error.messages) { 89 | const { messages } = this.instance.error; 90 | 91 | for (const msg of messages) { 92 | if (msg.context && this.control.hasError(msg.context.validator)) { 93 | return this.instance.error.message; 94 | } 95 | } 96 | } 97 | } 98 | } 99 | TextFieldComponent.MaterialComponent = MaterialTextfieldComponent; 100 | export { TextFieldComponent }; 101 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "align": { 8 | "options": [ 9 | "parameters", 10 | "statements" 11 | ] 12 | }, 13 | "array-type": false, 14 | "arrow-parens": false, 15 | "arrow-return-shorthand": true, 16 | "deprecation": { 17 | "severity": "warning" 18 | }, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "curly": true, 24 | "interface-name": false, 25 | "max-classes-per-file": false, 26 | "eofline": true, 27 | "max-line-length": [ 28 | true, 29 | 140 30 | ], 31 | "import-spacing": true, 32 | "indent": { 33 | "options": [ 34 | "spaces" 35 | ] 36 | }, 37 | "member-access": false, 38 | "member-ordering": [ 39 | true, 40 | { 41 | "order": [ 42 | "static-field", 43 | "instance-field", 44 | "static-method", 45 | "instance-method" 46 | ] 47 | } 48 | ], 49 | "no-consecutive-blank-lines": false, 50 | "no-console": [ 51 | true, 52 | "debug", 53 | "info", 54 | "time", 55 | "timeEnd", 56 | "trace" 57 | ], 58 | "no-empty": false, 59 | "no-inferrable-types": [ 60 | true, 61 | "ignore-params" 62 | ], 63 | "no-non-null-assertion": true, 64 | "no-redundant-jsdoc": true, 65 | "no-switch-case-fall-through": true, 66 | "no-var-requires": false, 67 | "object-literal-key-quotes": [ 68 | true, 69 | "as-needed" 70 | ], 71 | "object-literal-sort-keys": false, 72 | "ordered-imports": false, 73 | "quotemark": [ 74 | true, 75 | "single" 76 | ], 77 | "trailing-comma": false, 78 | "component-class-suffix": true, 79 | "contextual-lifecycle": true, 80 | "directive-class-suffix": true, 81 | "no-conflicting-lifecycle": true, 82 | "no-host-metadata-property": true, 83 | "no-input-rename": true, 84 | "no-inputs-metadata-property": true, 85 | "no-output-native": true, 86 | "no-output-on-prefix": true, 87 | "no-output-rename": true, 88 | "semicolon": { 89 | "options": [ 90 | "always" 91 | ] 92 | }, 93 | "space-before-function-paren": { 94 | "options": { 95 | "anonymous": "never", 96 | "asyncArrow": "always", 97 | "constructor": "never", 98 | "method": "never", 99 | "named": "never" 100 | } 101 | }, 102 | "no-outputs-metadata-property": true, 103 | "template-banana-in-box": true, 104 | "template-no-negated-async": true, 105 | "typedef-whitespace": { 106 | "options": [ 107 | { 108 | "call-signature": "nospace", 109 | "index-signature": "nospace", 110 | "parameter": "nospace", 111 | "property-declaration": "nospace", 112 | "variable-declaration": "nospace" 113 | }, 114 | { 115 | "call-signature": "onespace", 116 | "index-signature": "onespace", 117 | "parameter": "onespace", 118 | "property-declaration": "onespace", 119 | "variable-declaration": "onespace" 120 | } 121 | ] 122 | }, 123 | "use-lifecycle-interface": true, 124 | "use-pipe-transform-interface": true, 125 | "variable-name": { 126 | "options": [ 127 | "ban-keywords", 128 | "check-format", 129 | "allow-pascal-case" 130 | ] 131 | }, 132 | "whitespace": { 133 | "options": [ 134 | "check-branch", 135 | "check-decl", 136 | "check-operator", 137 | "check-separator", 138 | "check-type", 139 | "check-typecast" 140 | ] 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/survey/survey.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import SurveyComponent from 'formiojs/components/survey/Survey.js'; 4 | import { FormControl } from '@angular/forms'; 5 | 6 | @Component({ 7 | selector: 'mat-formio-survey', 8 | template: ` 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 48 | 49 | 50 | {{ instance.component.description }} 51 | 52 | 53 | 54 | {{ instance.error.message }} 55 |
21 |

22 | 23 |

24 |
29 | {{ value.label }} 30 |
{{ question.label }} 41 | 45 | 46 | 47 |
56 |
57 | ` 58 | }) 59 | export class MaterialSurveyComponent extends MaterialComponent { 60 | public controls: any = {}; 61 | getFormControl(question) { 62 | if (!this.controls[question]) { 63 | this.controls[question] = new FormControl(); 64 | if (this.instance.shouldDisabled) { 65 | this.controls[question].disable(); 66 | } 67 | } 68 | return this.controls[question]; 69 | } 70 | 71 | setDisabled(disabled) { 72 | const method = disabled ? 'disable' : 'enable'; 73 | for (const question in this.controls) { 74 | if (this.controls.hasOwnProperty(question)) { 75 | this.controls[question][method](); 76 | } 77 | } 78 | } 79 | 80 | getValue() { 81 | const values = {}; 82 | for (const question in this.controls) { 83 | if (this.controls.hasOwnProperty(question)) { 84 | values[question] = this.controls[question].value || false; 85 | } 86 | } 87 | return values; 88 | } 89 | 90 | setValue(value) { 91 | for (const question in value) { 92 | if (value.hasOwnProperty(question)) { 93 | const control = this.getFormControl(question); 94 | if (control) { 95 | control.setValue(value[question] || false); 96 | } 97 | } 98 | } 99 | } 100 | 101 | getUniqueName(question) { 102 | return `${this.instance.id}-${question}`; 103 | } 104 | } 105 | SurveyComponent.MaterialComponent = MaterialSurveyComponent; 106 | export { SurveyComponent }; 107 | -------------------------------------------------------------------------------- /demo/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-parens": false, 12 | "arrow-return-shorthand": true, 13 | "deprecation": { 14 | "severity": "warning" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "curly": true, 19 | "directive-class-suffix": true, 20 | "directive-selector": [ 21 | true, 22 | "attribute", 23 | "app", 24 | "camelCase" 25 | ], 26 | "component-selector": [ 27 | true, 28 | "element", 29 | "app", 30 | "kebab-case" 31 | ], 32 | "eofline": true, 33 | "import-blacklist": [ 34 | true, 35 | "rxjs/Rx" 36 | ], 37 | "import-spacing": true, 38 | "indent": { 39 | "options": [ 40 | "spaces" 41 | ] 42 | }, 43 | "interface-name": false, 44 | "max-classes-per-file": false, 45 | "max-line-length": [ 46 | true, 47 | 140 48 | ], 49 | "member-access": false, 50 | "member-ordering": [ 51 | true, 52 | { 53 | "order": [ 54 | "static-field", 55 | "instance-field", 56 | "static-method", 57 | "instance-method" 58 | ] 59 | } 60 | ], 61 | "no-consecutive-blank-lines": false, 62 | "no-console": [ 63 | true, 64 | "debug", 65 | "info", 66 | "time", 67 | "timeEnd", 68 | "trace" 69 | ], 70 | "no-empty": false, 71 | "no-inferrable-types": [ 72 | true, 73 | "ignore-params" 74 | ], 75 | "no-non-null-assertion": true, 76 | "no-redundant-jsdoc": true, 77 | "no-switch-case-fall-through": true, 78 | "no-var-requires": false, 79 | "object-literal-key-quotes": [ 80 | true, 81 | "as-needed" 82 | ], 83 | "object-literal-sort-keys": false, 84 | "ordered-imports": false, 85 | "quotemark": [ 86 | true, 87 | "single" 88 | ], 89 | "trailing-comma": false, 90 | "no-conflicting-lifecycle": true, 91 | "no-host-metadata-property": true, 92 | "no-input-rename": true, 93 | "no-inputs-metadata-property": true, 94 | "no-output-native": true, 95 | "no-output-on-prefix": true, 96 | "no-output-rename": true, 97 | "semicolon": { 98 | "options": [ 99 | "always" 100 | ] 101 | }, 102 | "space-before-function-paren": { 103 | "options": { 104 | "anonymous": "never", 105 | "asyncArrow": "always", 106 | "constructor": "never", 107 | "method": "never", 108 | "named": "never" 109 | } 110 | }, 111 | "no-outputs-metadata-property": true, 112 | "template-banana-in-box": true, 113 | "template-no-negated-async": true, 114 | "typedef-whitespace": { 115 | "options": [ 116 | { 117 | "call-signature": "nospace", 118 | "index-signature": "nospace", 119 | "parameter": "nospace", 120 | "property-declaration": "nospace", 121 | "variable-declaration": "nospace" 122 | }, 123 | { 124 | "call-signature": "onespace", 125 | "index-signature": "onespace", 126 | "parameter": "onespace", 127 | "property-declaration": "onespace", 128 | "variable-declaration": "onespace" 129 | } 130 | ] 131 | }, 132 | "use-lifecycle-interface": true, 133 | "use-pipe-transform-interface": true, 134 | "variable-name": { 135 | "options": [ 136 | "ban-keywords", 137 | "check-format", 138 | "allow-pascal-case" 139 | ] 140 | }, 141 | "whitespace": { 142 | "options": [ 143 | "check-branch", 144 | "check-decl", 145 | "check-operator", 146 | "check-separator", 147 | "check-type", 148 | "check-typecast" 149 | ] 150 | } 151 | }, 152 | "rulesDirectory": [ 153 | "codelyzer" 154 | ] 155 | } -------------------------------------------------------------------------------- /demo/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-material-formio": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/angular-material-formio", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "preserveSymlinks": true, 24 | "polyfills": "src/polyfills.ts", 25 | "tsConfig": "tsconfig.app.json", 26 | "aot": false, 27 | "assets": [ 28 | "src/favicon.ico", 29 | "src/assets" 30 | ], 31 | "styles": [ 32 | "src/styles.scss" 33 | ], 34 | "scripts": [] 35 | }, 36 | "configurations": { 37 | "production": { 38 | "fileReplacements": [ 39 | { 40 | "replace": "src/environments/environment.ts", 41 | "with": "src/environments/environment.prod.ts" 42 | } 43 | ], 44 | "optimization": true, 45 | "outputHashing": "all", 46 | "sourceMap": false, 47 | "extractCss": true, 48 | "namedChunks": false, 49 | "aot": true, 50 | "extractLicenses": true, 51 | "vendorChunk": false, 52 | "buildOptimizer": true, 53 | "budgets": [ 54 | { 55 | "type": "initial", 56 | "maximumWarning": "2mb", 57 | "maximumError": "5mb" 58 | }, 59 | { 60 | "type": "anyComponentStyle", 61 | "maximumWarning": "6kb" 62 | } 63 | ] 64 | } 65 | } 66 | }, 67 | "serve": { 68 | "builder": "@angular-devkit/build-angular:dev-server", 69 | "options": { 70 | "browserTarget": "angular-material-formio:build" 71 | }, 72 | "configurations": { 73 | "production": { 74 | "browserTarget": "angular-material-formio:build:production" 75 | } 76 | } 77 | }, 78 | "extract-i18n": { 79 | "builder": "@angular-devkit/build-angular:extract-i18n", 80 | "options": { 81 | "browserTarget": "angular-material-formio:build" 82 | } 83 | }, 84 | "test": { 85 | "builder": "@angular-devkit/build-angular:karma", 86 | "options": { 87 | "main": "src/test.ts", 88 | "polyfills": "src/polyfills.ts", 89 | "tsConfig": "tsconfig.spec.json", 90 | "karmaConfig": "karma.conf.js", 91 | "assets": [ 92 | "src/favicon.ico", 93 | "src/assets" 94 | ], 95 | "styles": [ 96 | "src/styles.scss" 97 | ], 98 | "scripts": [] 99 | } 100 | }, 101 | "lint": { 102 | "builder": "@angular-devkit/build-angular:tslint", 103 | "options": { 104 | "tsConfig": [ 105 | "tsconfig.app.json", 106 | "tsconfig.spec.json", 107 | "e2e/tsconfig.json" 108 | ], 109 | "exclude": [ 110 | "**/node_modules/**" 111 | ] 112 | } 113 | }, 114 | "e2e": { 115 | "builder": "@angular-devkit/build-angular:protractor", 116 | "options": { 117 | "protractorConfig": "e2e/protractor.conf.js", 118 | "devServerTarget": "angular-material-formio:serve" 119 | }, 120 | "configurations": { 121 | "production": { 122 | "devServerTarget": "angular-material-formio:serve:production" 123 | } 124 | } 125 | } 126 | } 127 | }}, 128 | "defaultProject": "angular-material-formio" 129 | } 130 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/day/day.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { MaterialComponent } from '../MaterialComponent'; 4 | import DayComponent from 'formiojs/components/day/Day.js'; 5 | import { momentDate } from 'formiojs/utils/utils.js'; 6 | DayComponent.prototype.getFieldValue = function(name) { 7 | return this.refs[name] ? this.refs[name].value : ''; 8 | }; 9 | 10 | @Component({ 11 | selector: 'mat-formio-day', 12 | template: ` 13 | 14 | 15 | 16 | 17 | 18 | 19 | Day 20 | 21 | 22 | {{day.label}} 23 | 24 | 25 | 26 | 27 | Month 28 | 29 | 30 | {{month.label}} 31 | 32 | 33 | 34 | 35 | Day 36 | 37 | 38 | {{day.label}} 39 | 40 | 41 | 42 | 43 | Year 44 | 45 | 46 | {{year.label}} 47 | 48 | 49 | 50 | {{ instance.error.message }} 51 | 52 | ` 53 | }) 54 | export class MaterialDayComponent extends MaterialComponent { 55 | public dayControl: FormControl = new FormControl(); 56 | public monthControl: FormControl = new FormControl(); 57 | public yearControl: FormControl = new FormControl(); 58 | setInstance(instance) { 59 | // Add stub methods to match dom elements. 60 | (this.dayControl as any).setAttribute = () => {}; 61 | (this.dayControl as any).removeAttribute = () => {}; 62 | (this.monthControl as any).setAttribute = () => {}; 63 | (this.monthControl as any).removeAttribute = () => {}; 64 | (this.yearControl as any).setAttribute = () => {}; 65 | (this.yearControl as any).removeAttribute = () => {}; 66 | instance.refs = { 67 | day: this.dayControl, 68 | month: this.monthControl, 69 | year: this.yearControl 70 | }; 71 | return super.setInstance(instance); 72 | } 73 | 74 | setDisabled(disabled) { 75 | if (disabled) { 76 | this.dayControl.disable(); 77 | this.monthControl.disable(); 78 | this.yearControl.disable(); 79 | } else { 80 | this.dayControl.enable(); 81 | this.monthControl.enable(); 82 | this.yearControl.enable(); 83 | } 84 | } 85 | 86 | getValue() { 87 | return this.instance.getDate(); 88 | } 89 | 90 | setValue(value) { 91 | if (value) { 92 | this.dayControl.setValue(parseInt(momentDate(value).format('D'))); 93 | this.monthControl.setValue(parseInt(momentDate(value).format('M'))); 94 | this.yearControl.setValue(parseInt(momentDate(value).format('YYYY'))); 95 | } 96 | this.instance.setValueAt(0, value); 97 | } 98 | } 99 | DayComponent.MaterialComponent = MaterialDayComponent; 100 | export { DayComponent }; 101 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/time/time.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Input, Output} from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { MaterialComponent } from '../MaterialComponent'; 4 | import TimeComponent from 'formiojs/components/time/Time.js'; 5 | import * as moment_ from 'moment'; 6 | @Component({ 7 | selector: 'mat-formio-time', 8 | template: ` 9 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | 30 | 39 | 50 | 57 |
58 | {{ instance.error.message }} 59 |
60 |
61 | ` 62 | }) 63 | 64 | export class MaterialTimeComponent extends MaterialComponent { 65 | public disabled = false; 66 | public period = 'AM'; 67 | public hourControl: FormControl = new FormControl(); 68 | public minuteControl: FormControl = new FormControl(); 69 | public secondControl: FormControl = new FormControl(); 70 | @Output() selectedEvent = new EventEmitter(); 71 | @Input() hourStep = 1; 72 | @Input() minuteStep = 1; 73 | @Input() secondStep = 1; 74 | @Input() renderElementOnly = false; 75 | 76 | setDisabled(disabled) { 77 | if (disabled) { 78 | this.hourControl.disable(); 79 | this.minuteControl.disable(); 80 | this.secondControl.disable(); 81 | } 82 | } 83 | 84 | get dataFormat() { 85 | let format = this.instance.component.dataFormat; 86 | format = format ? format : 'HH:mm'; 87 | return format; 88 | } 89 | 90 | setInstance(instance) { 91 | super.setInstance(instance); 92 | // this.control.setValue('00:00:00'); 93 | this.onChange(); 94 | } 95 | 96 | onChange() { 97 | const hours = this.hourControl.value; 98 | const minutes = this.minuteControl.value || '00'; 99 | const seconds = this.secondControl.value || ''; 100 | const rawValue = `${hours || '00'}:${minutes}${seconds ? ':' + seconds : ''} ${this.period}`; 101 | let value = this.getTwentyFourHourTime(rawValue); 102 | 103 | if (!hours) { 104 | value = this.instance.emptyValue; 105 | } 106 | 107 | this.control.setValue(value); 108 | if (this.instance) { 109 | super.onChange(); 110 | } 111 | this.selectedEvent.emit(this.control); 112 | } 113 | 114 | setValue(value) { 115 | if (!value) { 116 | return; 117 | } 118 | super.setValue(value); 119 | const [hourValue, minuteValue, period] = value.split(':'); 120 | this.hourControl.setValue(hourValue); 121 | this.minuteControl.setValue(minuteValue); 122 | // fix for default value with seconds instead of period 123 | this.period = period === ('AM' || 'PM') ? period : this.period; 124 | } 125 | 126 | getTwentyFourHourTime(amPmString) { 127 | const moment = moment_; 128 | return moment(amPmString, ['h:mm:ss A']).format(this.dataFormat); 129 | } 130 | 131 | changePeriod() { 132 | this.period = this.period === 'AM' ? 'PM' : 'AM'; 133 | this.onChange(); 134 | } 135 | } 136 | 137 | TimeComponent.MaterialComponent = MaterialTimeComponent; 138 | export { TimeComponent }; 139 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/MaterialComponent.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input, ViewChild, ElementRef, ChangeDetectorRef, AfterViewInit, OnInit} from '@angular/core'; 2 | import FormioComponent from './Base'; 3 | import Validator from 'formiojs/validator/Validator.js'; 4 | import { FormioControl } from '../FormioControl'; 5 | import get from 'lodash/get'; 6 | 7 | @Component({ 8 | selector: 'mat-formio-comp', 9 | template: 'Unknown Component: {{ instance.component.type }}' 10 | }) 11 | export class MaterialComponent implements AfterViewInit, OnInit { 12 | @Input() instance: any; 13 | @ViewChild('input') input: ElementRef; 14 | @Input() control: FormioControl = new FormioControl(); 15 | constructor(public element: ElementRef, public ref: ChangeDetectorRef) {} 16 | 17 | setInstance(instance: any) { 18 | this.control.setInstance(instance); 19 | instance.materialComponent = this; 20 | this.instance = instance; 21 | this.instance.disabled = this.instance.shouldDisabled; 22 | this.setVisible(this.instance.visible); 23 | this.renderComponents(); 24 | } 25 | 26 | ngOnInit() { 27 | if (this.instance) { 28 | if (this.shouldValidateOnInit()) { 29 | this.storeFormData(); 30 | this.validateOnInit(); 31 | } 32 | this.instance.component.defaultValue ? this.setValue(this.instance.component.defaultValue) : ''; 33 | } 34 | } 35 | 36 | validateOnInit() { 37 | const {key} = this.instance.component; 38 | const validationValue = this.getFormValue(this.instance.path); 39 | 40 | if (validationValue === null) { 41 | return; 42 | } 43 | 44 | this.instance.setPristine(false); 45 | 46 | const validationResult = Validator.checkComponent( 47 | this.instance, 48 | {[key]: validationValue}, 49 | {[key]: validationValue} 50 | ); 51 | 52 | if (validationResult.length) { 53 | this.instance.setCustomValidity(validationResult, false); 54 | if (!!validationValue) { 55 | this.control.markAsTouched(); 56 | } 57 | this.ref.detectChanges(); 58 | } 59 | } 60 | 61 | storeFormData() { 62 | if (this.instance.parent && this.instance.parent.submission && this.instance.parent.submission.data) { 63 | sessionStorage.setItem('formData', JSON.stringify(this.instance.parent.submission.data)); 64 | } 65 | } 66 | 67 | getFormValue(path) { 68 | const formData = JSON.parse(sessionStorage.getItem('formData')); 69 | 70 | if (!formData) { 71 | return null; 72 | } 73 | 74 | return get(formData, path); 75 | } 76 | 77 | renderComponents() {} 78 | 79 | onChange(keepInputRaw?: boolean) { 80 | let value = this.getValue(); 81 | 82 | if (value === undefined || value === null) { 83 | value = this.instance.emptyValue; 84 | } 85 | 86 | if (this.input && this.input.nativeElement.mask && value && !keepInputRaw) { 87 | this.input.nativeElement.mask.textMaskInputElement.update(value); 88 | this.control.setValue(this.input.nativeElement.value); 89 | value = this.getValue(); 90 | } 91 | this.instance.updateValue(value, {modified: true}); 92 | } 93 | 94 | getValue() { 95 | return this.control.value; 96 | } 97 | 98 | setValue(value) { 99 | this.control.setValue(value); 100 | } 101 | 102 | beforeSubmit() { 103 | this.control.markAsTouched(); 104 | } 105 | 106 | hasError() { 107 | return !!this.instance && !!this.instance.error; 108 | } 109 | 110 | shouldValidateOnInit() { 111 | if (!this.instance) { 112 | return; 113 | } 114 | 115 | return this.instance.options.validateOnInit 116 | || this.instance.parent.options.validateOnInit; 117 | } 118 | 119 | setDisabled(disabled) { 120 | if (disabled) { 121 | this.control.disable(); 122 | } else { 123 | this.control.enable(); 124 | } 125 | } 126 | 127 | setVisible(visible) { 128 | if (this.element && this.element.nativeElement) { 129 | if (visible) { 130 | this.element.nativeElement.removeAttribute('hidden'); 131 | this.element.nativeElement.style.visibility = 'visible'; 132 | this.element.nativeElement.style.position = 'relative'; 133 | } else { 134 | this.element.nativeElement.setAttribute('hidden', true); 135 | this.element.nativeElement.style.visibility = 'hidden'; 136 | this.element.nativeElement.style.position = 'absolute'; 137 | } 138 | } 139 | } 140 | 141 | ngAfterViewInit() { 142 | if (this.element && this.element.nativeElement && this.instance) { 143 | // Add custom classes to elements. 144 | if (this.instance.component.customClass) { 145 | this.element.nativeElement.classList.add(this.instance.component.customClass); 146 | } 147 | } 148 | 149 | if (this.input) { 150 | // Set the input masks. 151 | this.instance.setInputMask(this.input.nativeElement); 152 | this.instance.addFocusBlurEvents(this.input.nativeElement); 153 | } 154 | } 155 | } 156 | 157 | FormioComponent.MaterialComponent = MaterialComponent; 158 | export { FormioComponent }; 159 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/index.ts: -------------------------------------------------------------------------------- 1 | const Components = require('formiojs/components/Components').default; 2 | import Component from 'formiojs/components/_classes/component/Component.js'; 3 | 4 | import { TextFieldComponent } from './textfield/textfield.component'; 5 | import { PasswordComponent } from './password/password.component'; 6 | import { EmailComponent } from './email/email.component'; 7 | import { UrlComponent } from './url/url.component'; 8 | import { PhoneNumberComponent } from './phonenumber/phonenumber.component'; 9 | import { NumberComponent } from './number/number.component'; 10 | import { CurrencyComponent } from './currency/currency.component'; 11 | import { DayComponent } from './day/day.component'; 12 | import { HiddenComponent } from './hidden/hidden.component'; 13 | import { HtmlComponent } from './html/html.component'; 14 | import { TagsComponent } from './tags/tags.component'; 15 | import { ButtonComponent } from './button/button.component'; 16 | import { TextAreaComponent } from './textarea/textarea.component'; 17 | import { PanelComponent } from './panel/panel.component'; 18 | import { ColumnsComponent } from './columns/columns.component'; 19 | import { ContainerComponent } from './container/container.component'; 20 | import { TabsComponent } from './tabs/tabs.component'; 21 | import { DateTimeComponent } from './date/date.component'; 22 | import { CheckboxComponent } from './checkbox/checkbox.component'; 23 | import { FieldsetComponent } from './fieldset/fieldset.component'; 24 | import { ContentComponent } from './content/content.component'; 25 | import { SignatureComponent } from './signature/signature.component'; 26 | import { SurveyComponent } from './survey/survey.component'; 27 | import { SelectBoxesComponent } from './selectboxes/selectboxes.component'; 28 | import { RadioComponent } from './radio/radio.component'; 29 | import { SelectComponent } from './select/select.component'; 30 | import { WellComponent } from './well/well.component'; 31 | import { DataGridComponent } from './datagrid/datagrid.component'; 32 | import { EditGridComponent } from './editgrid/editgrid.component'; 33 | import { TableComponent } from './table/table.component'; 34 | import { FormioComponent } from './MaterialComponent'; 35 | import { Wizard } from './formio.wizard'; 36 | import { TimeComponent } from './time/time.component' 37 | 38 | // Set the components. 39 | const components: any = { 40 | textfield: TextFieldComponent, 41 | password: PasswordComponent, 42 | url: UrlComponent, 43 | checkbox: CheckboxComponent, 44 | email: EmailComponent, 45 | phoneNumber: PhoneNumberComponent, 46 | number: NumberComponent, 47 | currency: CurrencyComponent, 48 | day: DayComponent, 49 | hidden: HiddenComponent, 50 | htmlelement: HtmlComponent, 51 | tags: TagsComponent, 52 | textarea: TextAreaComponent, 53 | button: ButtonComponent, 54 | datetime: DateTimeComponent, 55 | panel: PanelComponent, 56 | columns: ColumnsComponent, 57 | tabs: TabsComponent, 58 | table: TableComponent, 59 | well: WellComponent, 60 | fieldset: FieldsetComponent, 61 | content: ContentComponent, 62 | signature: SignatureComponent, 63 | survey: SurveyComponent, 64 | selectboxes: SelectBoxesComponent, 65 | radio: RadioComponent, 66 | select: SelectComponent, 67 | container: ContainerComponent, 68 | datagrid: DataGridComponent, 69 | editgrid: EditGridComponent, 70 | unknown: FormioComponent, 71 | time: TimeComponent, 72 | wizard: Wizard 73 | }; 74 | 75 | export function getComponents() { 76 | for (const type of Object.keys(components)) { 77 | const CompClass = components[type]; 78 | CompClass.prototype.render = (function () { 79 | if (this.materialComponent) { 80 | return this.materialComponent.renderComponents(); 81 | } 82 | 83 | const viewContainer = this.parent ? this.parent.viewContainer(this) : this.viewContainer(this); 84 | if (!viewContainer) { 85 | return; 86 | } 87 | const factory = this.options.viewResolver.resolveComponentFactory(CompClass.MaterialComponent); 88 | const componentRef = viewContainer.createComponent(factory); 89 | (componentRef.instance as any).setInstance(this); 90 | }); 91 | 92 | const setValue = CompClass.prototype.setValue; 93 | CompClass.prototype.setValue = (function (...args) { 94 | if (this.materialComponent) { 95 | this.materialComponent.setValue(args[0]); 96 | } 97 | return setValue.call(this, ...args); 98 | }); 99 | 100 | components[type] = CompClass; 101 | } 102 | 103 | return components; 104 | } 105 | 106 | export function registerComponent(name: string, CompClass: any) { 107 | 108 | class DummyComponent extends Component {}; 109 | const formIOComp = (DummyComponent as any); 110 | 111 | formIOComp.MaterialComponent = CompClass; 112 | formIOComp.prototype.render = (function () { 113 | if (this.materialComponent) { 114 | return this.materialComponent; 115 | } 116 | const viewContainer = this.parent ? this.parent.viewContainer(this) : this.viewContainer(this); 117 | if (!viewContainer) { 118 | return; 119 | } 120 | const factory = this.options.viewResolver.resolveComponentFactory(formIOComp.MaterialComponent); 121 | const componentRef = viewContainer.createComponent(factory); 122 | (componentRef.instance as any).setInstance(this); 123 | }); 124 | 125 | const setValue = formIOComp.prototype.setValue; 126 | formIOComp.prototype.setValue = (function (...args) { 127 | if (this.materialComponent) { 128 | this.materialComponent.setValue(args[0]); 129 | } 130 | return setValue.call(this, ...args); 131 | }); 132 | 133 | Components.addComponent(name, formIOComp); 134 | 135 | } 136 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/datagrid/datagrid.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core'; 2 | import { MatTableDataSource } from '@angular/material/table'; 3 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 4 | import DataGridComponent from 'formiojs/components/datagrid/DataGrid.js'; 5 | import {CdkDragDrop, moveItemInArray, DragDropModule} from '@angular/cdk/drag-drop'; 6 | import {MatTable} from '@angular/material/table'; 7 | @Component({ 8 | selector: 'mat-formio-datagrid', 9 | template: ` 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 26 | 27 | 35 | > 36 | 37 | 38 | 41 | 42 | 43 | 44 | 49 | 50 | 51 | 52 | 55 | 56 | 57 |
58 |
60 | 61 |
62 |
63 | 64 |
{{ getColumnLabel(columns[column]) }} 39 | 40 | 45 | 48 | No. 53 | reorder 54 |
65 |
66 | 67 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | `, 81 | styles: [ 82 | '.datagrid-row { height: auto; }' 83 | ] 84 | }) 85 | export class MaterialDataGridComponent extends MaterialNestedComponent { 86 | displayedColumns: string[]; 87 | formColumns: string[]; 88 | columns: any; 89 | dataSource = new MatTableDataSource(); 90 | 91 | getColumnLabel(column) { 92 | return column.label || column.key; 93 | } 94 | 95 | setInstance(instance: any) { 96 | super.setInstance(instance); 97 | this.dataSource.data = instance.dataValue; 98 | this.columns = {}; 99 | this.displayedColumns = []; 100 | this.formColumns = []; 101 | instance.getColumns().map((column) => { 102 | this.formColumns.push(column.key); 103 | this.displayedColumns.push(column.key); 104 | this.columns[column.key] = column; 105 | }); 106 | this.displayedColumns.push('__removeRow'); 107 | if (this.instance.component.reorder) { 108 | this.displayedColumns.push('position'); 109 | } 110 | instance.viewContainer = (component) => { 111 | let viewContainer; 112 | if (this.instance.component.disabled) { 113 | component.component.disabled = true; 114 | } 115 | this.viewContainers.forEach((container) => { 116 | const td = container.element.nativeElement.parentNode; 117 | if ( 118 | component.rowIndex === parseInt(td.getAttribute('rowIndex'), 10) && 119 | component.component.key === td.getAttribute('component') 120 | ) { 121 | viewContainer = container; 122 | } 123 | }); 124 | 125 | return viewContainer ? viewContainer : null; 126 | }; 127 | } 128 | 129 | addAnother() { 130 | this.checkRowsNumber(); 131 | this.instance.addRow(); 132 | if (this.dataSource.data.length < this.instance.rows.length) { 133 | this.dataSource.data.push({}); 134 | } 135 | this.dataSource.data = [...this.dataSource.data]; 136 | } 137 | 138 | checkRowsNumber() { 139 | while (this.instance.rows.length < this.dataSource.data.length) { 140 | this.instance.addRow(); 141 | } 142 | } 143 | 144 | removeRow(index) { 145 | this.instance.removeRow(index); 146 | this.dataSource.data.splice(index, 1); 147 | this.dataSource.data = [...this.dataSource.data]; 148 | } 149 | 150 | dropTable(event: CdkDragDrop) { 151 | const prevIndex = this.dataSource.data.findIndex((d) => d === event.item.data); 152 | moveItemInArray(this.control.value, prevIndex, event.currentIndex); 153 | this.renderComponents(); 154 | } 155 | 156 | renderComponents() { 157 | this.instance.getRows(); 158 | this.instance.setValue(this.control.value || []); 159 | } 160 | 161 | setValue(value: [] | null) { 162 | const gridLength = value ? value.length : 0; 163 | 164 | while (this.instance.rows.length < gridLength) { 165 | this.addAnother(); 166 | this.instance.dataValue = value; 167 | this.instance.setValue(value); 168 | } 169 | 170 | if (!value && this.instance.component.clearOnHide) { 171 | this.dataSource = new MatTableDataSource(this.instance.defaultValue); 172 | } 173 | super.setValue(value); 174 | } 175 | } 176 | DataGridComponent.MaterialComponent = MaterialDataGridComponent; 177 | export { DataGridComponent }; 178 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/editgrid/editgrid.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | ViewChild, 4 | ViewChildren, 5 | QueryList, 6 | ElementRef, 7 | AfterViewInit 8 | } from '@angular/core'; 9 | import { MaterialNestedComponent } from '../MaterialNestedComponent'; 10 | import EditGridComponent from 'formiojs/components/editgrid/EditGrid.js'; 11 | import { FormioComponent } from '../../formio.component'; 12 | import Components from 'formiojs/components/Components'; 13 | import isString from 'lodash/isString'; 14 | 15 | enum EditRowState { 16 | NEW = 'new', 17 | EDITING ='editing', 18 | SAVED = 'saved', 19 | REMOVED = 'removed', 20 | DRAFT = 'draft' 21 | }; 22 | 23 | /* tslint:disable no-bitwise only-arrow-functions */ 24 | const hashCode = function(str) { 25 | let hash = 0; 26 | let i = 0; 27 | let chr; 28 | str = str.replace(/\s/g, ''); 29 | if (str.length === 0) { 30 | return hash; 31 | } 32 | for (i = 0; i < str.length; i++) { 33 | chr = str.charCodeAt(i); 34 | hash = ((hash << 5) - hash) + chr; 35 | hash |= 0; // Convert to 32bit integer 36 | } 37 | return hash; 38 | }; 39 | /* tslint:enable no-bitwise only-arrow-functions */ 40 | 41 | // Do nothing to createRowComponents, let mat-formio handle it. 42 | /* tslint:disable only-arrow-functions */ 43 | EditGridComponent.prototype.createRowComponents = function() { 44 | return []; 45 | }; 46 | 47 | const checkRow = EditGridComponent.prototype.checkRow; 48 | EditGridComponent.prototype.checkRow = function(data, editRow, flags: any = {}) { 49 | if (flags.checkRow) { 50 | return checkRow.call(this, data, editRow, flags); 51 | } else { 52 | return true; 53 | } 54 | }; 55 | /* tslint:enable only-arrow-functions */ 56 | 57 | const DEFAULT_HEADER_TEMPLATES = [ 58 | hashCode((EditGridComponent as any).defaultHeaderTemplate), 59 | hashCode(` 60 |
61 | {% (components || []).forEach(function(component) { %} 62 |
{{ component.label }}
63 | {% }) %} 64 |
`) 65 | ]; 66 | 67 | const DEFAULT_ROW_TEMPLATES = [ 68 | hashCode((EditGridComponent as any).defaultRowTemplate), 69 | hashCode(`
70 | {% util.eachComponent(components, function(component) { %} 71 |
72 | {{ getView(component, row[component.key]) }} 73 |
74 | {% }) %} 75 | {% if (!instance.options.readOnly) { %} 76 |
77 |
78 | 79 | 80 |
81 |
82 | {% } %} 83 |
`) 84 | ]; 85 | 86 | @Component({ 87 | selector: 'mat-formio-editgrid', 88 | styleUrls: ['./editgrid.component.css'], 89 | templateUrl: './editgrid.component.html' 90 | }) 91 | export class MaterialEditGridComponent extends MaterialNestedComponent implements AfterViewInit { 92 | @ViewChild('header') headerElement: ElementRef; 93 | @ViewChild('footer') footerElement: ElementRef; 94 | @ViewChildren('rows') rowElements: QueryList; 95 | @ViewChildren('forms') forms: QueryList; 96 | public header: string; 97 | public footer: string; 98 | public displayedColumns: string[]; 99 | public columns: any = {}; 100 | public colWidth = 0; 101 | public valid = true; 102 | public RowStates = EditRowState; 103 | 104 | getRowTemplate(content) { 105 | return ` 106 | {% (components || []).forEach(function(component) { %} 107 | {% if (!component.hasOwnProperty('tableView') || component.tableView) { %} 108 | ${content} 109 | {% } %} 110 | {% }) %} 111 | `; 112 | } 113 | 114 | validate(index) { 115 | if (!this.forms) { 116 | return; 117 | } 118 | const forms = this.forms.toArray(); 119 | if (!forms[index]) { 120 | return; 121 | } 122 | const formioComponent = forms[index]; 123 | const {data} = formioComponent.formio.submission; 124 | const isInvalid = Object.keys(data).some( 125 | value => isString(data[value]) && data[value].length === 0 126 | ); 127 | 128 | if (isInvalid) { 129 | this.valid = false; 130 | } else { 131 | this.valid = true; 132 | } 133 | } 134 | 135 | setInstance(instance) { 136 | if ( 137 | instance.component.templates.header && 138 | (DEFAULT_HEADER_TEMPLATES.indexOf(hashCode(instance.component.templates.header)) !== -1) 139 | ) { 140 | instance.component.templates.header = this.getRowTemplate('{{ component.label }}'); 141 | } 142 | 143 | if (instance.component.templates.row && 144 | (DEFAULT_ROW_TEMPLATES.indexOf(hashCode(instance.component.templates.row)) !== -1) 145 | ) { 146 | instance.component.templates.row = this.getRowTemplate('{{ getView(component, row[component.key]) }}'); 147 | } 148 | 149 | this.displayedColumns = instance.component.components.map((comp) => { 150 | if (comp.hasOwnProperty('tableView') && !comp.tableView) { 151 | return false; 152 | } 153 | 154 | this.columns[comp.key] = comp; 155 | return comp.key; 156 | }).filter(name => !!name); 157 | const dataValue = instance.dataValue || []; 158 | this.colWidth = instance.component.components.length ? Math.floor(100 / instance.component.components.length) : 100; 159 | if (instance.component.templates && instance.component.templates.header) { 160 | this.header = instance.renderString(instance.component.templates.header, { 161 | components: instance.component.components, 162 | value: dataValue, 163 | colWidth: this.colWidth 164 | }); 165 | } 166 | if (instance.component.templates && instance.component.templates.footer) { 167 | this.footer = instance.renderString(instance.component.templates.footer, { 168 | components: instance.component.components, 169 | value: dataValue, 170 | colWidth: this.colWidth 171 | }); 172 | } 173 | setTimeout(() => { 174 | this.renderTemplate(this.headerElement, this.header); 175 | this.renderTemplate(this.footerElement, this.footer); 176 | }, 0); 177 | super.setInstance(instance); 178 | } 179 | 180 | addAnother() { 181 | const row = this.instance.addRow(); 182 | } 183 | 184 | editRow(row, index) { 185 | const { state } = row; 186 | const { NEW, REMOVED } = this.RowStates; 187 | 188 | if (state === NEW || state === REMOVED) { 189 | return; 190 | } 191 | this.instance.editRow(index); 192 | const forms = this.forms.toArray(); 193 | if (forms[index]) { 194 | forms[index].formio.submission = {data: JSON.parse(JSON.stringify(row.data))}; 195 | } 196 | } 197 | 198 | /** 199 | * Updates the row template. 200 | * 201 | * @param row 202 | * @param index 203 | */ 204 | updateRowTemplate(rowElement: ElementRef, index, comps) { 205 | const self = this; 206 | const editRow: any = {...this.instance.editRows[index]}; 207 | if (editRow.state !== this.RowStates.NEW) { 208 | this.renderTemplate(rowElement, this.instance.renderString(this.instance.component.templates.row, { 209 | row: this.instance.dataValue[index] || {}, 210 | data: this.instance.data, 211 | rowIndex: index, 212 | colWidth: this.colWidth, 213 | components: this.instance.component.components, 214 | getView: function getView(component, data) { 215 | if (!comps[component.type]) { 216 | comps[component.type] = Components.create(component, {}, {}, true); 217 | } 218 | return comps[component.type] ? comps[component.type].getView(data) : ''; 219 | } 220 | })); 221 | } 222 | } 223 | 224 | /** 225 | * Saves a row. 226 | * 227 | * @param row - The edit grid row. 228 | * @param index - The index for this row. 229 | */ 230 | saveRow(row, index) { 231 | const forms = this.forms.toArray(); 232 | if (forms[index]) { 233 | const formioComponent = forms[index]; 234 | row.data = JSON.parse(JSON.stringify(formioComponent.formio.submission.data)); 235 | this.instance.saveRow(index); 236 | const rows = this.rowElements.toArray(); 237 | if (rows && rows[index]) { 238 | this.updateRowTemplate(rows[index], index, {}); 239 | } 240 | } 241 | } 242 | 243 | cancelRow(index) { 244 | this.instance.cancelRow(index); 245 | this.valid = true; 246 | } 247 | 248 | renderTemplate(element: ElementRef, template) { 249 | if (!element || !element.nativeElement) { 250 | return; 251 | } 252 | element.nativeElement.innerHTML = template; 253 | } 254 | 255 | ngAfterViewInit() { 256 | this.rowElements.changes.subscribe((rows: QueryList) => { 257 | const rowCache = {}; 258 | rows.forEach((row: ElementRef, index) => this.updateRowTemplate(row, index, rowCache)); 259 | }); 260 | } 261 | } 262 | EditGridComponent.MaterialComponent = MaterialEditGridComponent; 263 | export { EditGridComponent }; 264 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/angular-material-formio.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { FlexLayoutModule } from '@angular/flex-layout'; 5 | import { MatInputModule } from '@angular/material/input'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatFormFieldModule } from '@angular/material/form-field'; 8 | import { MatCheckboxModule } from '@angular/material/checkbox'; 9 | import { MatRadioModule } from '@angular/material/radio'; 10 | import { MatSelectModule } from '@angular/material/select'; 11 | import { MatListModule } from '@angular/material/list'; 12 | import { MatChipsModule } from '@angular/material/chips'; 13 | import { MatExpansionModule } from '@angular/material/expansion'; 14 | import { MatCardModule } from '@angular/material/card'; 15 | import { MatStepperModule } from '@angular/material/stepper'; 16 | import { MatTabsModule } from '@angular/material/tabs'; 17 | import { MatTableModule } from '@angular/material/table'; 18 | import { MatDatepickerModule } from '@angular/material/datepicker'; 19 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 20 | import { MatIconModule } from '@angular/material/icon'; 21 | import { MatTooltipModule } from '@angular/material/tooltip'; 22 | import { MatMenuModule } from '@angular/material/menu'; 23 | import { MatNativeDateModule } from '@angular/material/core'; 24 | 25 | import { initRenderer } from './renderer'; 26 | import { FormioComponent } from './formio.component'; 27 | import { MaterialComponent } from './components/MaterialComponent'; 28 | import { MaterialNestedComponent } from './components/MaterialNestedComponent'; 29 | import { MaterialButtonComponent } from './components/button/button.component'; 30 | import { MaterialTextfieldComponent } from './components/textfield/textfield.component'; 31 | import { MaterialPasswordComponent } from './components/password/password.component'; 32 | import { MaterialUrlComponent } from './components/url/url.component'; 33 | import { MaterialEmailComponent } from './components/email/email.component'; 34 | import { MaterialPhoneNumberComponent } from './components/phonenumber/phonenumber.component'; 35 | import { MaterialNumberComponent } from './components/number/number.component'; 36 | import { MaterialHiddenComponent } from './components/hidden/hidden.component'; 37 | import { MaterialHtmlComponent } from './components/html/html.component'; 38 | import { MaterialTagsComponent } from './components/tags/tags.component'; 39 | import { MaterialCurrencyComponent } from './components/currency/currency.component'; 40 | import { MaterialDayComponent } from './components/day/day.component'; 41 | import { MaterialTextareaComponent } from './components/textarea/textarea.component'; 42 | import { MaterialColumnsComponent } from './components/columns/columns.component'; 43 | import { MaterialContainerComponent } from './components/container/container.component'; 44 | import { MaterialCheckboxComponent } from './components/checkbox/checkbox.component'; 45 | import { MaterialFieldsetComponent } from './components/fieldset/fieldset.component'; 46 | import { MaterialContentComponent } from './components/content/content.component'; 47 | import { MaterialSignatureComponent } from './components/signature/signature.component'; 48 | import { MaterialSurveyComponent } from './components/survey/survey.component'; 49 | import { MaterialSelectBoxesComponent } from './components/selectboxes/selectboxes.component'; 50 | import { MaterialRadioComponent } from './components/radio/radio.component'; 51 | import { MaterialSelectComponent } from './components/select/select.component'; 52 | import { MaterialPanelComponent } from './components/panel/panel.component'; 53 | import { MaterialTabsComponent } from './components/tabs/tabs.component'; 54 | import { MaterialTableComponent } from './components/table/table.component'; 55 | import { MaterialDateComponent } from './components/date/date.component'; 56 | import { MaterialDataGridComponent } from './components/datagrid/datagrid.component'; 57 | import { MaterialEditGridComponent } from './components/editgrid/editgrid.component'; 58 | import { MaterialWellComponent } from './components/well/well.component'; 59 | import { MaterialWizardComponent } from './components/formio.wizard'; 60 | import { MaterialTimeComponent } from './components/time/time.component'; 61 | import { MaterialCalendarComponent } from './components/calendar/calendar.component'; 62 | import { DragDropModule } from '@angular/cdk/drag-drop'; 63 | import { FormioFormFieldComponent } from './components/formio-form-field/formio-form-field.component'; 64 | import { LabelComponent } from './components/label/label.component'; 65 | import { MaskDirective } from './directives/mask.directive'; 66 | 67 | export { 68 | FormioComponent, 69 | MaterialButtonComponent, 70 | MaterialTextfieldComponent, 71 | MaterialPasswordComponent, 72 | MaterialUrlComponent, 73 | MaterialEmailComponent, 74 | MaterialPhoneNumberComponent, 75 | MaterialNumberComponent, 76 | MaterialCurrencyComponent, 77 | MaterialDayComponent, 78 | MaterialHiddenComponent, 79 | MaterialHtmlComponent, 80 | MaterialTagsComponent, 81 | MaterialTableComponent, 82 | MaterialTextareaComponent, 83 | MaterialColumnsComponent, 84 | MaterialContainerComponent, 85 | MaterialDataGridComponent, 86 | MaterialEditGridComponent, 87 | MaterialPanelComponent, 88 | MaterialCheckboxComponent, 89 | MaterialFieldsetComponent, 90 | MaterialContentComponent, 91 | MaterialSignatureComponent, 92 | MaterialSurveyComponent, 93 | MaterialSelectBoxesComponent, 94 | MaterialRadioComponent, 95 | MaterialSelectComponent, 96 | MaterialTabsComponent, 97 | MaterialDateComponent, 98 | MaterialWellComponent, 99 | MaterialComponent, 100 | MaterialNestedComponent, 101 | MaterialTimeComponent 102 | }; 103 | 104 | @NgModule({ 105 | declarations: [ 106 | FormioComponent, 107 | MaterialButtonComponent, 108 | MaterialTextfieldComponent, 109 | MaterialPasswordComponent, 110 | MaterialUrlComponent, 111 | MaterialEmailComponent, 112 | MaterialPhoneNumberComponent, 113 | MaterialNumberComponent, 114 | MaterialCurrencyComponent, 115 | MaterialDayComponent, 116 | MaterialHiddenComponent, 117 | MaterialHtmlComponent, 118 | MaterialTagsComponent, 119 | MaterialTextareaComponent, 120 | MaterialColumnsComponent, 121 | MaterialContainerComponent, 122 | MaterialDataGridComponent, 123 | MaterialEditGridComponent, 124 | MaterialPanelComponent, 125 | MaterialCheckboxComponent, 126 | MaterialFieldsetComponent, 127 | MaterialContentComponent, 128 | MaterialSignatureComponent, 129 | MaterialSurveyComponent, 130 | MaterialSelectBoxesComponent, 131 | MaterialRadioComponent, 132 | MaterialSelectComponent, 133 | MaterialTabsComponent, 134 | MaterialTableComponent, 135 | MaterialDateComponent, 136 | MaterialWellComponent, 137 | MaterialWizardComponent, 138 | MaterialComponent, 139 | MaterialNestedComponent, 140 | MaterialTimeComponent, 141 | MaterialCalendarComponent, 142 | FormioFormFieldComponent, 143 | LabelComponent, 144 | MaskDirective 145 | ], 146 | imports: [ 147 | CommonModule, 148 | FormsModule, 149 | ReactiveFormsModule, 150 | FlexLayoutModule, 151 | MatInputModule, 152 | MatFormFieldModule, 153 | MatCheckboxModule, 154 | MatRadioModule, 155 | MatSelectModule, 156 | MatListModule, 157 | MatChipsModule, 158 | MatExpansionModule, 159 | MatButtonModule, 160 | MatCardModule, 161 | MatStepperModule, 162 | MatTabsModule, 163 | MatTableModule, 164 | MatNativeDateModule, 165 | MatDatepickerModule, 166 | MatProgressSpinnerModule, 167 | MatTooltipModule, 168 | MatIconModule, 169 | MatMenuModule, 170 | DragDropModule 171 | ], 172 | exports: [ 173 | FormioComponent, 174 | FlexLayoutModule, 175 | MatInputModule, 176 | MatFormFieldModule, 177 | MatCheckboxModule, 178 | MatRadioModule, 179 | MatSelectModule, 180 | MatListModule, 181 | MatChipsModule, 182 | MatExpansionModule, 183 | MatButtonModule, 184 | MatCardModule, 185 | MatTabsModule, 186 | MatTableModule, 187 | MatNativeDateModule, 188 | MatDatepickerModule, 189 | MatProgressSpinnerModule, 190 | MatTooltipModule, 191 | MatIconModule 192 | ], 193 | entryComponents: [ 194 | MaterialButtonComponent, 195 | MaterialTextfieldComponent, 196 | MaterialPasswordComponent, 197 | MaterialUrlComponent, 198 | MaterialEmailComponent, 199 | MaterialPhoneNumberComponent, 200 | MaterialNumberComponent, 201 | MaterialCurrencyComponent, 202 | MaterialDayComponent, 203 | MaterialHiddenComponent, 204 | MaterialHtmlComponent, 205 | MaterialTagsComponent, 206 | MaterialTextareaComponent, 207 | MaterialColumnsComponent, 208 | MaterialContainerComponent, 209 | MaterialDataGridComponent, 210 | MaterialEditGridComponent, 211 | MaterialPanelComponent, 212 | MaterialCheckboxComponent, 213 | MaterialFieldsetComponent, 214 | MaterialContentComponent, 215 | MaterialSignatureComponent, 216 | MaterialSurveyComponent, 217 | MaterialSelectBoxesComponent, 218 | MaterialRadioComponent, 219 | MaterialSelectComponent, 220 | MaterialTabsComponent, 221 | MaterialTableComponent, 222 | MaterialDateComponent, 223 | MaterialWellComponent, 224 | MaterialComponent, 225 | MaterialNestedComponent, 226 | MaterialTimeComponent, 227 | MaterialWizardComponent 228 | ], 229 | providers: [] 230 | }) 231 | export class MatFormioModule { 232 | constructor() { 233 | initRenderer(); 234 | } 235 | } 236 | export * from './renderer'; 237 | -------------------------------------------------------------------------------- /projects/angular-material-formio/src/lib/components/date/date.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core' 2 | import { MaterialComponent } from '../MaterialComponent'; 3 | import DateTimeComponent from 'formiojs/components/datetime/DateTime.js'; 4 | import { momentDate } from 'formiojs/utils/utils.js'; 5 | import {FormControl} from '@angular/forms'; 6 | @Component({ 7 | selector: 'mat-formio-date', 8 | host: { 9 | '(document:click)': 'clickOutside($event)', 10 | }, 11 | template: ` 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | schedule 21 | 22 | 23 | 32 | 41 | 49 | 50 | 51 | 65 | {{ instance.error.message }} 66 |
67 |
68 | ` 69 | }) 70 | 71 | export class MaterialDateComponent extends MaterialComponent { 72 | public timeControl: FormControl = new FormControl(); 73 | public displayControl: FormControl = new FormControl(); 74 | public isPickerOpened: boolean; 75 | public selectedDate: any; 76 | public selectedTime: any = '00:00'; 77 | public allowManualInput: boolean = true; 78 | 79 | @ViewChild('calendar') calendar; 80 | 81 | get enableDate() { 82 | return this.instance && this.instance.component.enableDate !== false; 83 | } 84 | 85 | get enableTime() { 86 | return this.instance && this.instance.component.enableTime === true; 87 | } 88 | 89 | setDisplayControlValue(value = null) { 90 | const format = `YYYY-MM-DD${this.enableTime ? 'THH:mm' : ''}`; 91 | value = value || this.getDateTimeValue(); 92 | 93 | if (value) { 94 | this.displayControl.setValue(momentDate(value).format(format)); 95 | } 96 | else { 97 | this.displayControl.setValue(''); 98 | } 99 | } 100 | 101 | onChangeDate(event) { 102 | this.selectedDate = momentDate(event).utc().format(); 103 | this.control.setValue(this.selectedDate); 104 | this.setDateTime(); 105 | } 106 | 107 | onChangeTime(time) { 108 | this.selectedTime = time; 109 | if (this.selectedDate || (this.enableTime && !this.enableDate)) { 110 | this.setDateTime(); 111 | } 112 | } 113 | 114 | onChangeInput() { 115 | const value = this.dateFilter(this.displayControl.value) && 116 | this.checkMinMax(this.displayControl.value) ? this.displayControl.value : ''; 117 | 118 | this.control.setValue(value); 119 | this.onChange(); 120 | } 121 | 122 | getDateTimeValue() { 123 | let newDate = ''; 124 | let isSelectedTime = false; 125 | 126 | if (this.calendar && this.calendar.selectedTime) { 127 | const { selectedTime } = this.calendar; 128 | isSelectedTime = true; 129 | 130 | if (this.selectedTime !== selectedTime) { 131 | this.selectedTime = selectedTime; 132 | } 133 | } 134 | 135 | if (this.enableTime && this.enableDate) { 136 | const [hours, minutes] = this.selectedTime.split(':'); 137 | newDate = isSelectedTime 138 | ? momentDate(this.selectedDate) 139 | .hours(Number.parseInt(hours)) 140 | .minutes(Number.parseInt(minutes)) 141 | .utc() 142 | : this.selectedDate; 143 | } 144 | 145 | if (!this.enableTime && this.enableDate) { 146 | newDate = this.selectedDate; 147 | } 148 | 149 | if (this.enableTime && !this.enableDate) { 150 | const [hours, minutes] = this.selectedTime.split(':'); 151 | newDate = momentDate(new Date()) 152 | .hours(Number.parseInt(hours)) 153 | .minutes(Number.parseInt(minutes)) 154 | .seconds(0) 155 | .utc(); 156 | } 157 | 158 | return newDate; 159 | } 160 | 161 | setDateTime() { 162 | this.control.setValue(this.getDateTimeValue()); 163 | this.onChange(); 164 | } 165 | 166 | setInstance(instance: any) { 167 | super.setInstance(instance); 168 | this.isDisabled() ? this.control.disable() : this.control.enable(); 169 | this.isDisabled() ? this.displayControl.disable() : this.displayControl.enable(); 170 | 171 | if (this.instance) { 172 | this.allowManualInput = this.instance.component.allowInput === false ? false : true; 173 | if (this.instance.component && this.instance.component.datePicker) { 174 | const {minDate: min, maxDate: max} = this.instance.component.datePicker; 175 | 176 | // It improves the date to the full format if the customer set only a year. Otherwise we will have conflicts into the moment.js. 177 | const { minDate, maxDate } = this.improveMinMaxDate(min, max); 178 | this.instance.component.datePicker.minDate = minDate; 179 | this.instance.component.datePicker.maxDate = maxDate; 180 | } 181 | } 182 | } 183 | 184 | toggleCalendar(event) { 185 | if (!this.isDisabled()) { 186 | if (!this.isPickerOpened) { 187 | const date = this.getValue(); 188 | if (date &&this.checkMinMax(date)) { 189 | if (this.enableDate && this.calendar && !this.calendar.selectedDate) { 190 | this.calendar.setExistedDate(momentDate(date).toDate()) 191 | } 192 | 193 | if (this.enableTime && this.calendar && !this.calendar.selectedTime) { 194 | const time = momentDate(date) 195 | this.calendar.setExistedTime(time.format('HH:mm'), time.format('h:mm:A')) 196 | } 197 | } 198 | } 199 | this.isPickerOpened = !this.isPickerOpened; 200 | event.stopPropagation(); 201 | } 202 | } 203 | 204 | isDisabled() { 205 | const { readonly, disabled } = this.instance.component; 206 | return readonly || disabled || this.instance.root.options.readOnly 207 | } 208 | 209 | public formatTime = (value) => { 210 | if (!value) { 211 | return this.instance.emptyValue; 212 | } 213 | return momentDate(value).format(this.instance.component.format); 214 | } 215 | 216 | setValue(value) { 217 | if (this.dateFilter(value) && this.checkMinMax(value)) { 218 | this.setDisplayControlValue(value); 219 | super.setValue(value); 220 | } 221 | } 222 | 223 | onChange() { 224 | const value = this.dateFilter(this.getValue()) && this.checkMinMax(this.getValue()) ? this.getValue() : ''; 225 | this.setDisplayControlValue(value); 226 | } 227 | 228 | beforeSubmit() { 229 | this.onChange(); 230 | super.beforeSubmit(); 231 | } 232 | 233 | checkMinMax(value) { 234 | let isValid = true; 235 | const { minDate: min, maxDate: max } = this.instance.component.datePicker; 236 | const { minDate, maxDate } = this.improveMinMaxDate(min, max); 237 | 238 | if (minDate) { 239 | isValid = momentDate(value).isSameOrAfter(minDate); 240 | } 241 | if (maxDate && isValid) { 242 | isValid = momentDate(value).isSameOrBefore(maxDate); 243 | } 244 | return isValid; 245 | } 246 | 247 | disableWeekends(d: Date) { 248 | if (d && d.getDay) { 249 | const day = d.getDay(); 250 | return day !== 0 && day !== 6; 251 | } 252 | return true; 253 | } 254 | 255 | disableDates(dates: Array, d: Date) { 256 | const formattedDates = dates.map((date) => momentDate(date).format('YYYY-MM-DD')); 257 | return !formattedDates.includes(momentDate(d).format('YYYY-MM-DD')); 258 | } 259 | 260 | dateFilter = (d: Date | null): boolean => { 261 | const isValid = this.instance.component.datePicker.disableWeekends ? this.disableWeekends(d) : true; 262 | return this.instance.component.widget.disabledDates && isValid ? 263 | this.disableDates(this.instance.component.widget.disabledDates.split(','), d) : isValid; 264 | } 265 | 266 | clickOutside(event) { 267 | if (!this.calendar.element.nativeElement.contains(event.target) && this.isPickerOpened) 268 | this.toggleCalendar(event); 269 | } 270 | 271 | improveMinMaxDate(minDate, maxDate) { 272 | if (minDate && minDate.length === 4) { 273 | minDate = momentDate(`${minDate}-01-01`).format('YYYY-MM-DD'); 274 | } 275 | 276 | if (maxDate && maxDate.length === 4) { 277 | maxDate = momentDate(`${maxDate}-01-01`).subtract(1, 'day').format('YYYY-MM-DD'); 278 | } 279 | return {minDate, maxDate}; 280 | } 281 | } 282 | DateTimeComponent.MaterialComponent = MaterialDateComponent; 283 | export { DateTimeComponent }; 284 | --------------------------------------------------------------------------------