Skip to content

Commit

Permalink
fix: Corrects Runtime compiler error
Browse files Browse the repository at this point in the history
Replaces Angular compiler with Handlerbars.
Angular currently prevents the use of its compiler
in AOT mode.  See angular issue #20156.

Closes #11
  • Loading branch information
jscharett committed Aug 13, 2019
1 parent ae7133f commit d14beb1
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 52 deletions.
21 changes: 6 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0-development",
"scripts": {
"ng": "ng",
"start": "ng serve",
"start": "ng serve --prod",
"build": "npm run build:lib && npm run build:demo",
"build:lib": "ng build ngx-json-schema-form",
"build:demo": "ng build demo --prod --base-href=https://jscharett.github.io/ngx-json-schema-form/",
Expand Down Expand Up @@ -45,6 +45,7 @@
"ajv": "^6.10.0",
"brace": "^0.11.0",
"core-js": "^2.5.4",
"handlebars": "^4.1.2",
"json-schema-traverse": "^0.4.1",
"lodash": "^4.17.14",
"lodash-decorators": "^6.0.1",
Expand Down
3 changes: 2 additions & 1 deletion projects/ngx-json-schema-form/ng-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"ajv": "Ajv",
"json-schema-traverse": "traverse",
"lodash": "_",
"lodash-decorators": "lodashDecorators"
"lodash-decorators": "lodashDecorators",
"handlebars/dist/cjs/handlebars": "Handlebars"
}
}
}
6 changes: 5 additions & 1 deletion projects/ngx-json-schema-form/src/docs/pages/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ There are numerous attributes that can be set on a widget which are set via the

### Button

There are 4 types of buttons that jsf supports, `button`, `image`, `reset` and `submit`. By default, these will all use the `<input>` tag for display. However, with the exception of `image`, you can specify custom html via the `content` option. This will result in the `<button>` being used for display and the content being inserted. Content can contain simple angular bindings so that you can bind to various properties of the layout. For example:
There are 4 types of buttons that jsf supports, `button`, `image`, `reset` and `submit`. By default, these will all use the `<input>` tag for display. However, with the exception of `image`, you can specify custom html via the `content` option. This will result in the `<button>` being used for display and the content being inserted. Content can contain simple bindings so that you can bind to various properties of the layout. For example:

<div class="panel panel-primary docs">
<div class="panel-heading panel-title">button-layout.json</div>
Expand Down Expand Up @@ -221,6 +221,10 @@ will result in

Here you can see we are specifying that the button should contain an image via the `content` options. The template binds to the properties of the `icon` option which results in an image tag being rendered. The `icon` option is also used when redering a simple button of type `image`.

<div class="alert alert-warning">
Note: due to current limitation of Angular AOT compilation, use of the angular compile to transpile content strings into HTML DOM is not possible. Recent Angular changes have restricted the inclusion of the compiler in favor of reducing the file size and load time when in AOT mode. Therefore, Handlebars is currently being imported to handle the compilation of content string to HTML DOM.
</div>

We can also respond to events such as a `click`. To do this, we must first bind an event handler to the jsf component


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('JsonSchemaFormService', () => {
expect(widget.controlName).toBe('widget');
});

it('should create a runtime component', () => {
it('should convert template to DOM', () => {
const service: JsonSchemaFormService = TestBed.get(JsonSchemaFormService);
const fragment: DocumentFragment = service.compileTemplate('<span>{{ options.title }}</span>', {title: 'hi'});
const div = document.createElement('div');
Expand Down
81 changes: 48 additions & 33 deletions projects/ngx-json-schema-form/src/lib/json-schema-form.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { CommonModule } from '@angular/common';
import {
Compiler, Component, ComponentRef, Injectable, Injector,
ModuleWithComponentFactories, NgModule, NgModuleRef
} from '@angular/core';
// import { CommonModule } from '@angular/common';
import { Injectable } from '@angular/core';
// import {
// Compiler, Component, ComponentRef, Injectable, Injector,
// ModuleWithComponentFactories, NgModule, NgModuleRef
// } from '@angular/core';

import Handlebars from 'handlebars/dist/cjs/handlebars';

import { Widget } from './widget-library';

Expand All @@ -13,32 +16,32 @@ export class JsonSchemaFormService {
private x = false;

/** Creates a component with passed in template and associated context */
private static createComponent(template: string, context: {[key: string]: any}): any {
return Component({
selector: `runtime-component-sample`,
template
})(class RuntimeComponent {
options: any = context;
});
}
// private static createComponent(template: string, context: {[key: string]: any}): any {
// return Component({
// selector: `runtime-component-sample`,
// template
// })(class RuntimeComponent {
// options: any = context;
// });
// }

/** Create a module for use with the RuntimeComponent */
private static createModule(component: any, compiler: Compiler): ModuleWithComponentFactories<any> {
@NgModule({ imports: [CommonModule], declarations: [component] })
class RuntimeComponentModule { }
// private static createModule(component: any, compiler: Compiler): ModuleWithComponentFactories<any> {
// @NgModule({ imports: [CommonModule], declarations: [component] })
// class RuntimeComponentModule { }

return compiler.compileModuleAndAllComponentsSync(RuntimeComponentModule);
}
// return compiler.compileModuleAndAllComponentsSync(RuntimeComponentModule);
// }

/** Get a components innerHTML as a document Fragment */
private static getDocumentFragment(componentRef: ComponentRef<any>): DocumentFragment {
const template = document.createElement('template');
template.innerHTML = (<HTMLElement>componentRef.location.nativeElement).innerHTML;
// private static getDocumentFragment(componentRef: ComponentRef<any>): DocumentFragment {
// const template = document.createElement('template');
// template.innerHTML = (<HTMLElement>componentRef.location.nativeElement).innerHTML;

return template.content;
}
// return template.content;
// }

constructor(private readonly compiler: Compiler, private readonly injector: Injector, private readonly moduleRef: NgModuleRef<any>) {}
// constructor(private readonly compiler: Compiler,private readonly injector: Injector, private readonly moduleRef: NgModuleRef<any>) {}

/** Sets a widgets properties upon Widget creation */
initializeControl(control: Widget, bind = true): void {
Expand All @@ -58,15 +61,27 @@ export class JsonSchemaFormService {
* @param context - options to use when rendering the template
*/
compileTemplate(template: string, context: {[key: string]: any} = {}): DocumentFragment {
const component: any = JsonSchemaFormService.createComponent(template, context);
const module = JsonSchemaFormService.createModule(component, this.compiler);
const factory = module.componentFactories.find((f) => f.componentType === component);
const componentRef: ComponentRef<any> = factory.create(this.injector, undefined, undefined, this.moduleRef);
const templateTag = document.createElement('template');
templateTag.innerHTML = Handlebars.compile(template)({options: context});

componentRef.hostView.detectChanges();
const fragment = JsonSchemaFormService.getDocumentFragment(componentRef);
componentRef.destroy();

return fragment;
return templateTag.content;
}

// /**
// * Compiles an HTML tempalte and data into a document fragment
// * @param template - HTML template to render
// * @param context - options to use when rendering the template
// */
// compileTemplate(template: string, context: {[key: string]: any} = {}): DocumentFragment {
// const component: any = JsonSchemaFormService.createComponent(template, context);
// const module = JsonSchemaFormService.createModule(component, this.compiler);
// const factory = module.componentFactories.find((f) => f.componentType === component);
// const componentRef: ComponentRef<any> = factory.create(this.injector, undefined, undefined, this.moduleRef);

// componentRef.hostView.detectChanges();
// const fragment = JsonSchemaFormService.getDocumentFragment(componentRef);
// componentRef.destroy();

// return fragment;
// }
}

0 comments on commit d14beb1

Please sign in to comment.