Skip to content

Instantly share code, notes, and snippets.

@bigopon
Last active August 5, 2017 04:04
Show Gist options
  • Save bigopon/ca3900a448a159943475740a4e7c27f4 to your computer and use it in GitHub Desktop.
Save bigopon/ca3900a448a159943475740a4e7c27f4 to your computer and use it in GitHub Desktop.
extend html only element
<template>
<require from='./dynamic-input.htm'></require>
<style>
.input-wrap { display: block; padding: 5px; background: #dedede; border: 1px solid #eee; }
.input-wrap + .input-wrap { margin-top: 10px; }
.input-wrap > * { padding-top: 5px; }
fieldset { margin: 5px; padding: 5px; border: 1px solid #999; }
dynamic-input { display: block; }
</style>
<div class='input-wrap'>
<h5>Dynamic Input Element:</h5>
<hr/>
<dynamic-input
view-model.ref='input'
type='text'
prop1.bind='num1'
prop2.bind='num2'
prop-1.trigger='onProp1Changed($event)'></dynamic-input>
</div>
<fieldset>
<div class="input-wrap">[num1] in App: <div>${num1 || 'not known'}</div></div>
<div class="input-wrap">[prop1] in DynamicInput Element: <div>${input.prop1 || 'not known'}</div></div>
<button click.delegate="makeRandom('num1')">Set App's [num1] to a number</button>
</fieldset>
<fieldset>
<div class="input-wrap">[num2] in App: <div>${num2 || 'not known'}</div></div>
<div class="input-wrap">[prop2] in DynamicInput Element: <div>${input.prop2 || 'not known'}</div></div>
<button click.delegate="makeRandom('num2')">Set App's [num2] to a number</button>
</fieldset>
<br/>
<fieldset>
[num3] will be updated via event
<div class="input-wrap">[num3] in App: <div>${num3 || 'not known'}</div></div>
<div class="input-wrap">[prop1] in DynamicInput Element: <div>${input.prop1 || 'not known'}</div></div>
</fieldset>
</template>
import {observable} from './aurelia-framework'
import {bindable} from './aurelia-framework'
import {
HtmlBehaviorResource
} from 'aurelia-framework'
export class App {
makeRandom(prop) {
this[prop] = Math.floor(Math.random() * 1e5);
}
attached() {
console.log(this)
}
onProp1Changed($event) {
console.log($event.detail); // notice console log, logged 2 times because we bind 2 times to same property
this.num3 = $event.detail[0];
}
}
/*eslint padded-blocks:0*/
import {useView, customElement, bindable} from 'aurelia-templating';
import {bindingMode} from 'aurelia-binding'
import {DOM} from 'aurelia-pal'
export function _createDynamicElement(name: string, viewUrl: string, bindableNames: string[]): Function {
@customElement(name)
@useView(viewUrl)
class DynamicElement {
constructor(element) {
if (element) {
this.$el = element; // is this name safe ?
}
}
bind(bindingContext) {
this.$parent = bindingContext;
}
}
let parts;
let bindableConfig
let bindableOptions;
let _bindingMode;
let baseBinding;
let eventMap;
let prop;
let notifyEvent;
for (let i = 0, ii = bindableNames.length; i < ii; ++i) {
bindableConfig = bindableNames[i];
parts = bindableConfig.split('&');
_bindingMode = parts[1];
bindableOptions = {};
// Check if has binding mode
if (_bindingMode) {
bindableOptions.defaultBindingMode = bindingMode[_bindingMode.trim()] || bindingMode.oneWay;
}
// Check if has notify prop changes by event
baseBinding = parts[0].split('^');
prop = baseBinding[0].trim();
if (notifyEvent = baseBinding[1].trim()) {
(eventMap || (eventMap = {}))[prop] = notifyEvent;
}
bindableOptions.name = prop;
bindable(bindableOptions)(DynamicElement);
}
if (eventMap) {
// should use define property or just assign ???
let proto = DynamicElement.prototype;
proto.$eventMap = eventMap;
proto.propertyChanged = propertyChanged;
// Inject element too
DynamicElement.inject = [DOM.Element];
}
return DynamicElement;
}
function propertyChanged(name, newVal, oldVal) {
const $event = DOM.createCustomEvent(this.$eventMap[name], { bubbles: true, detail: [newVal, oldVal] });
this.$el.dispatchEvent($event);
}
<template
bindable='prop1 ^prop-1 & oneWay, prop2 ^prop2Changed & twoWay'>
<div>
<label>[prop1] -- oneWay<div>Change event: 'prop-1'</div>
<input value.bind='prop1'>
</label>
</div>
<hr/>
<div>
<label>[prop2] -- twoWay<div>Change event: 'prop-2'</div>
<input value.bind='prop2'>
</label>
</div>
</template>
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<style>
body {
padding: 20px;
}
.form-component {
display: block;
margin-bottom: 20px;
}
</style>
</head>
<body aurelia-app="main">
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.plugin('./xtend-html-resource-plugin');
aurelia.start().then(() => aurelia.setRoot());
}
import {ViewEngine} from 'aurelia-templating';
import {_createDynamicElement} from './dynamic-element';
/*eslint padded-blocks:0*/
import {useView, customElement, bindable} from 'aurelia-templating';
export function getElementName(address) {
return /([^\/^\?]+)\.html?/i.exec(address)[1].toLowerCase();
}
export function configure(config) {
let viewEngine = config.container.get(ViewEngine);
let loader = config.aurelia.loader;
viewEngine.addResourcePlugin('.htm', {
'fetch': function(address) {
return loader.loadTemplate(address).then(registryEntry => {
let bindable = registryEntry.template.getAttribute('bindable');
let elementName = getElementName(address);
if (bindable) {
bindable = bindable.split(',').map(x => x.trim());
registryEntry.template.removeAttribute('bindable');
} else {
bindable = [];
}
return { [elementName]: _createDynamicElement(elementName, address, bindable) };
});
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment