Days
Hours
Minutes
Seconds
How Diablo was completely Reverse Engineered without Source Code
Software development consultant at Sparkhound in Baton Rouge, LA.
Received M.S. in Computer Engineering from LSU.
Interested in all things Angular, .NET, and maybe Rust.
Content Management Systems Rule!
Easy to modify content
Distributed ownership
Reuseable across projects
Content Management Systems Are Bad, and they should feel bad!
Inefficient developer experience.
Bad UI/UX!
(editing is slow or buggy, content gets jumbled on save, styles overwrite the editor, etc...)
Monolithic (more features than needed)
Limited backend choices (PHP)
Do not support new trends well like SPA Frameworks, Mobile, etc.
Wordpress
Drupal
Kentico
Sitefinity
Wix
Squarespace
...and many more!
Much better content management ...
... but they gave up on the UI features!
AirTable, Coda, Notion, etc...
https://www.slashgear.com/conquer-2019-with-these-new-breed-of-productivity-tools-01559572/
Components!
Dynamic Creation
Visual Layout/Content Editing
Bonus: Lazy Loading and Plugins
What is a component?
How do we create them?
How do we share them?
Also called Blocks, Elements, or Widgets for old folks
Inputs
Outputs
Services (Dependency Injection)
Content Projection
<input type="text" value="initial value">
<button onclick="console.log(event);">Click Me</button>
@Component({
selector: 'container',
templateUrl: './container.component.html',
styleUrls: ['./container.component.scss'],
})
export class ContainerComponent {
constructor(private sanitizer: DomSanitizer) {
}
}
<div>
<button>Click Me</button>
</div>
<my-custom-component>
<h1>Title</h1>
<button>Click Me</button>
</my-custom-component>
What is a component?
How do we create them?
How do we share them?
@Component({
selector: 'container',
template: `
<p>{{someInput}}</p>
<button (click)="someInputChange($event)">Click Me</button>
<ng-content select="slot-name"></ng-content>
`,
styleUrls: ['./container.component.scss'],
})
export class ContainerComponent {
constructor(private sanitizer: DomSanitizer) {
}
@Input() someInput: string;
@Output() someInputChange = new EventEmitter<string>();
}
What is a component?
How do we create them?
How do we share them?
Angular CLI makes it easy to create a Library!
> ng generate library my-lib
> ng build my-lib --watch
> ng serve
Works, but I ran into occasional file lock issues...
Configure your tsconfig.json file to reference the library code directly!
{
"compilerOptions": {
"paths": {
"elemental": [
"projects/elemental/src/public-api"
],
"elemental/*": [
"projects/elemental/src/public-api/*"
],
// ....
}
}
// ....
}
Framework Independent (works with Angular, React, Vue, plain JavaScript, etc.)
What is a component?
How do we create them?
How do we share them?
Components!
Dynamic Creation
Visual Layout/Content Editing
Bonus: Lazy Loading and Plugins
<ng-container #host></ng-container>
export class SomeComponent {
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
@ViewChild('host', {static: true, read: ViewContainerRef }) host: ViewContainerRef;
loadComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(MyComponent);
const viewContainerRef = this.host.viewContainerRef;
const componentRef = viewContainerRef.createComponent(componentFactory);
// You can set Input/Output properties!
(<MyComponent>componentRef.instance).property = property;
}
}
entryComponents: [
HeroJobAdComponent,
HeroProfileComponent
],
schema: ElementalBootstrapSchema = {
type: "container",
props: { fluid: true }
};
export let elementalTypes = {
"container": ContainerComponent,
"row": RowComponent,
"input": InputComponent,
"input-text": InputTextComponent,
"input-checkbox": InputCheckboxComponent,
"input-file": InputFileComponent,
};
<elemental [schema]="schema"></elemental>
schema: ElementalBootstrapSchema = {
type: "container",
props: { fluid: true },
children: [
{
type: "row",
props: {},
children: [
{
type: "input",
props: {}
},
]
}
]
};
export type ElementalBootstrapSchema =
{
type: "container",
props: ContainerComponentData,
children?: ElementalBootstrapSchema[],
} | {
type: "row",
props: RowComponentData,
children?: ElementalBootstrapSchema[],
} | {
type: "input-text",
props: InputTextComponentData,
} | {
type: "input-checkbox",
props: InputCheckboxComponentData,
};
CMS
Dashboards
Lazy Loading
Code Generation
Create a schema by hand.
Template-driven vs Reactive
Custom Controls? Nested? Projected?
Collecting results into a model
<formly-form
[model]="model"
[fields]="fields"
[options]="options"
[form]="form">
</formly-form>
fields: FormlyFieldConfig[] = [
{
key: 'text',
type: 'input',
templateOptions: {
label: 'Text',
placeholder: 'Formly is terrific!',
required: true,
},
},
];
Interface for custom controls
interface ControlValueAccessor {
writeValue(obj: any): void
registerOnChange(fn: any): void
registerOnTouched(fn: any): void
setDisabledState(isDisabled: boolean)?: void
}
Explains how to handle validation as well!
Allows us to use the dynamic componet just like any other control
<elemental [schema]="schema" [(ngModel)]="model"></elemental>
Components!
Dynamic Creation
Visual Layout/Content Editing
Bonus: Lazy Loading and Plugins
Many users would like to move things around visually!
End-users
Designers
Grandma
... and even developers!
Wordpress (Gutenberg, Bakery, etc...)
Dragon-drop?
Angular CDK drag-and-drop
Components!
Dynamic Creation
Visual Layout/Content Editing
Bonus: Lazy Loading and Plugins
> ng build --project elemental-bootstrap-bundle --prod --output-hashing none --single-bundle true --extraWebpackConfig webpack.extra.js
module.exports = {
output: {
library: 'elemental-bundle',
libraryTarget: 'umd'
},
"externals": {
"rxjs": "rxjs",
"@angular/core": "ng.core",
"@angular/common": "ng.common",
"@angular/platform-browser": "ng.platformBrowser",
"@angular/platform-browser-dynamic": "ng.platformBrowserDynamic",
"@angular/elements": "ng.elements"
}
}
export * from '../../elemental-bootstrap/src/lib/elemental-bootstrap.module';
// NOTE:
// Dont worry if Angular Language Service shows errors when importing/exporting ngfactory.
// It should still compile correctly.
export * from '../../elemental-bootstrap/src/lib/elemental-bootstrap.module.ngfactory';
import { ElementalBootstrapModuleNgFactory } from '../../elemental-bootstrap/src/lib/elemental-bootstrap.module.ngfactory';
export default ElementalBootstrapModuleNgFactory;
let exports = {};
let module = {exports: exports};
let modules = {
'ng.core': core,
'ng.common': common,
'ng.cdk': cdk,
'ng.platformBrowser': platformBrowser,
'ng.platformBrowserDynamic': platformBrowserDynamic,
'ngxLogger': ngxLoader,
"rxjs": rxjs,
};
let url = `http://localhost:4200/${dynamicModule.url}`;
let response = await this.http.get(url, { responseType: "text" }).toPromise();
// shim 'require' and eval
let require: any = (module) => modules[module];
let func = Function("exports, module, require", response);
let result = func(exports, module, require);
let plugin = exports['ElementalBootstrapModuleFactory'];
let module = plugin.ngModuleFactory.create(this.injector);
let component = plugin.componentFactories[0].create(this.injector, null, null, module);
Components!
Dynamic Creation
Visual Layout/Content Editing
Bonus: Lazy Loading and Plugins
Components Rule
They can be Dynamic
Make your own CMS
Your feedback is valuable!
Created by John Harvey