<!DOCTYPE html>
<html>
<head>
<base href="." />
<title>ng-formly example</title>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.4/css/bootstrap.min.css" integrity="sha384-2hfp1SzUoho7/TsGGGDaFdsuuDL0LX2hnUp6VkX3CUQ2K4K+xjboZdsXyp4oUHZj" crossorigin="anonymous" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
<script data-require="lodash.js@4.16.2" data-semver="4.16.2" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.2/lodash.js"></script>
<script src="https://unpkg.com/zone.js@0.6.26/dist/zone.js"></script>
<script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
<script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
<script src="config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
</head>
<body>
<formly-demo-app>
loading...
</formly-demo-app>
</body>
</html>
pre {
display: block;
padding: 9.5px;
margin: 0 0 10px;
font-size: 13px;
line-height: 1.42857143;
color: #333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
}
.c-select {
display: block;
width: 100%;
}
/* Checkbox Toggle */
.checkbox-toggle {
margin: 0;
padding: 0;
}
.checkbox-toggle input[type="checkbox"] {
display: none;
}
.checkbox-toggle input[type="checkbox"]:checked + label {
border-color: #0275D8;
background: #0275D8;
box-shadow: inset 0 0 0 20px #0275D8;
}
.checkbox-toggle input[type="checkbox"]:checked + label.toggle-alert {
border-color: #f10000;
background: #f10000;
box-shadow: inset 0 0 0 20px #f10000;
}
.checkbox-toggle label {
-webkit-transition: all .2s ease-in-out;
transition: all .2s ease-in-out;
display: inline-block;
position: relative;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background: #ECEEEF;
box-shadow: inset 0 0 0 0 #0275D8;
}
.checkbox-toggle label div {
-webkit-transition: all .2s ease-in-out;
transition: all .2s ease-in-out;
background: white;
width: 30px;
height: 30px;
border-radius: 50%;
}
.checkbox-toggle--large label div {
width: 41px;
height: 41px;
}
.checkbox-toggle label:hover, .checkbox-toggle label > div:hover {
cursor: pointer;
}
.checkbox-toggle input[type="checkbox"]:checked + label > div {
margin-left: 36px;
}
.checkbox-toggle--large input[type="checkbox"]:checked + label > div {
margin-left: 55px;
}
.checkbox-toggle label {
border: 2px solid #ECEEEF;
border-radius: 42px;
height: 34px;
width: 70px;
}
.checkbox-toggle--large label {
width: 100px;
height: 45px;
}
System.config({
//use typescript for compilation
transpiler: 'typescript',
//typescript compiler options
typescriptOptions: {
emitDecoratorMetadata: true
},
paths: {
'npm:': 'https://unpkg.com/'
},
//map tells the System loader where to look for things
map: {
'app': './src',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
'@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
'@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
'@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
'@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
'ng-formly': 'npm:ng-formly/bundles/ng-formly.umd.min.js',
'rxjs': 'npm:rxjs',
'typescript': 'npm:typescript@2.0.2/lib/typescript.js',
'lodash': 'npm:lodash/cloneDeep.js'
},
//packages defines our app package
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
rxjs: {
defaultExtension: 'js'
}
}
});
//main entry point
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
import { NgModule, Component, enableProdMode, OnInit } from '@angular/core';
import { FormsModule, ReactiveFormsModule, Validators, FormBuilder, FormGroup, FormControl } from '@angular/forms';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { BrowserModule } from '@angular/platform-browser';
import { FormlyModule, FormlyFieldConfig, FormlyBootstrapModule } from 'ng-formly';
import { DynamicFormlyFieldBuilder } from './dynamic-formly-field.builder';
import { FormlyFieldTemplate } from './template';
@Component({
selector: 'formly-demo-app',
templateUrl: './template.html',
})
export class FormlyDemoApp implements OnInit {
author = { name: 'Tony Franzese', url: 'https://github.com/franzeal' };
form: FormGroup;
model: any;
options: any;
fields: FormlyFieldConfig[];
constructor() {
}
ngOnInit(): void {
this.form = new FormGroup({});
this.model = {};
this.fields = [
{
type: 'template'
templateOptions: {
template: `The current time is {{time | date:'M/d/yyyy HH:mm:ss'}}`;
},
lifecycle: {
onInit: function() {
setInterval(() => this.time = new Date(), 1000);
}
}
}
];
this.options = {};
}
}
@NgModule({
declarations: [
FormlyDemoApp,
FormlyFieldTemplate
],
imports: [
BrowserModule,
FormlyModule.forRoot({
types: [
{
name: 'template',
component: FormlyFieldTemplate
}
]
}),
FormlyBootstrapModule,
FormsModule,
ReactiveFormsModule,
],
providers: [DynamicFormlyFieldBuilder],
bootstrap: [FormlyDemoApp]
})
export class AppModule {
}
<div class="container">
<h1>ng-formly example: Dynamically compiled templates</h1>
<p>This is an example to demonstrate using dynamically compiled templates in formly forms.</p>
<hr>
<form class="formly" role="form" novalidate [formGroup]="form">
<formly-form [form]="form" [fields]="fields" [model]="model" [options]="options">
</formly-form>
</form>
<hr>
<br><strong>Form Data:</strong><br><pre>{{model | json}}</pre><br>
<hr>
<div style="margin-top:30px">
<small>
This is an example for the
<a href="https://formly-js.github.io/ng2-formly">ng-formly</a>
project made with ♥ by
<strong>
<span *ngIf="!author.name || !author.url">
{{author.name || 'anonymous'}}
</span>
<a *ngIf="author.url" href="{{author.url}}">
{{author.name}}
</a>
</strong>
</small>
</div>
</div>
import { Compiler, Component, ComponentFactory, forwardRef, NgModule, Injectable } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FormlyModule, FieldType } from 'ng-formly';
/** [source]{@link http://stackoverflow.com/questions/34784778/equivalent-of-compile-in-angular-2/37044960#37044960} */
@Injectable()
export class DynamicFormlyFieldBuilder {
constructor(
private compiler: Compiler
) { }
private factoryCache: { [templateKey: string]: ComponentFactory<FieldType> } = {};
public createComponentFactory(template: string): Promise<ComponentFactory<FieldType>> {
let factory = this.factoryCache[template];
if (factory) {
return Promise.resolve(factory);
}
let type = this.createDynamicFormlyField(template);
let module = this.createComponentModule(type);
return new Promise(resolve => {
this.compiler
.compileModuleAndAllComponentsAsync(module)
.then(moduleWithFactories => {
factory = _.find(moduleWithFactories.componentFactories, { componentType: type });
this.factoryCache[template] = factory;
resolve(factory);
});
});
}
private createDynamicFormlyField(template: string) {
@Component({
selector: 'dynamic-formly-field',
template: template,
})
class DynamicFormlyField extends FieldType {
};
return DynamicFormlyField;
}
private createComponentModule(componentType: any) {
// Import any modules that export components/directives/pipes your
// templates need access to
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
declarations: [
componentType
]
})
class RuntimeComponentModule { }
return RuntimeComponentModule;
}
}
import { AfterViewInit, Component, ComponentRef, DoCheck, OnDestroy, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core';
import { FieldType } from 'ng-formly';
import { DynamicFormlyFieldBuilder } from './dynamic-formly-field.builder';
/** [source]{@link http://stackoverflow.com/questions/34784778/equivalent-of-compile-in-angular-2/37044960#37044960} */
@Component({
selector: 'formly-field-template',
template: `
<template #dynamicFieldComponent></template>
`
})
export class FormlyFieldTemplate extends FieldType implements AfterViewInit, OnDestroy, OnInit {
@ViewChild('dynamicFieldComponent', { read: ViewContainerRef }) dynamicFieldComponent: ViewContainerRef;
protected componentRef: ComponentRef<FieldType>;
protected viewInitialized = false;
constructor(
private dynamicFormlyFieldBuilder: DynamicFormlyFieldBuilder
) {
super();
}
get template(): string {
return _.isString(this.to["template"]) ? this.to["template"] : '';
}
protected refreshContent() {
if (this.componentRef) {
this.componentRef.destroy();
}
this.dynamicFormlyFieldBuilder
.createComponentFactory(this.template)
.then(factory => {
this.componentRef = this
.dynamicFieldComponent
.createComponent(factory);
Object.assign(this.componentRef.instance, {
model: this.model,
form: this.form,
field: this.field,
options: this.options
});
});
}
ngAfterViewInit(): void {
this.viewInitialized = true;
this.refreshContent();
}
ngOnChanges(changes: SimpleChanges): void {
if (!this.viewInitialized) {
return;
}
this.refreshContent();
}
ngOnDestroy() {
if (this.componentRef) {
this.componentRef.destroy();
this.componentRef = undefined;
}
}
}