-
-
Save fragsalat/0542bb772df1903cbec7f13fa18143b1 to your computer and use it in GitHub Desktop.
Aurelia Gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<require from="./children"></require> | |
<p>Below are groups which get's re-rendered every time you add a child or re-name one. You can see it because of the logged getter calls.</p> | |
<div repeat.for="group of groupedChildren"> | |
<h3>${group.title}</h3> | |
<div repeat.for="child of group.children"> | |
<children child.bind="child"></children> | |
</div> | |
</div> | |
<br/> | |
<br/> | |
<label>Add new child</label> | |
<input type="text" value.bind="newChildName" /> | |
<button click.delegate="addChildren(newChildName)">Add</button> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {computedFrom} from 'aurelia-framework'; | |
import {Store} from './store'; | |
import {ChildGroup} from './child-group'; | |
export class App { | |
static inject = [Store] | |
/** | |
* Get object updates | |
* @param {Store} store | |
*/ | |
constructor(store) { | |
this.store = store; | |
this.store.replaceState({ | |
object: { | |
name: 'I\'m the only and real object', | |
children: [ | |
{id: 1, name: 'Nami'}, | |
{id: 2, name: 'Zoro'}, | |
{id: 3, name: 'Luffy'}, | |
{id: 4, name: 'Shopper'}, | |
{id: 5, name: 'Sanji'}, | |
{id: 6, name: 'Brook'} | |
] | |
} | |
}); | |
store.subscribe(state => { | |
console.log('new state'); | |
debugger; | |
this.object = state.clone().object; | |
}) | |
} | |
/** | |
* Add a children with name | |
*/ | |
addChildren(name) { | |
// Would normally be done by an action class | |
const state = this.store.getState().clone(); | |
state.object.children.push({ | |
id: state.object.children.length, | |
name | |
}); | |
this.store.replaceState(state); | |
} | |
/** | |
* Group children based on their id | |
* @returns {Array} | |
*/ | |
@computedFrom('object') | |
get groupedChildren() { | |
return ChildGroup.groupChildren(this.object.children); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class ChildGroup { | |
/** | |
* @param {string} title | |
*/ | |
constructor(title) { | |
this.title = title; | |
this.amount = 0; | |
this.children = []; | |
} | |
/** | |
* @param {Object} child | |
*/ | |
addChild(child) { | |
this.children.push(child); | |
} | |
recalculateValues() { | |
this.amount = this.children.length + 0; | |
} | |
/** | |
* @param {Array<Object>} children | |
* @returns {Array<PositionGroup>} | |
*/ | |
static groupChildren(children) { | |
const groups = children.reduce((groups, child) => { | |
const groupingKey = child.id % 2; | |
// Create new group | |
if (!groups.has(groupingKey)) { | |
groups.set(groupingKey, new ChildGroup('Group ' + groupingKey)); | |
} | |
const group = groups.get(groupingKey); | |
group.addChild(child); | |
groups.set(groupingKey, group); | |
return groups; | |
}, new Map()); | |
return Array.from(groups.values()).map(group => { | |
group.recalculateValues(); | |
return group; | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<input type="text" value="${childName}" change.delegate="changeName($event.target.value)" /> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {bindable, computedFrom, customElement} from 'aurelia-framework'; | |
import {Store} from './store'; | |
export class ChildrenCustomElement { | |
static inject = [Store]; | |
@bindable child; | |
/** | |
* @param {Store} store | |
*/ | |
constructor(store) { | |
this.store = store; | |
} | |
/** | |
* Change name of current child | |
*/ | |
changeName(newName) { | |
// Would normally be done by an action class | |
const state = this.store.getState().clone(); | |
state.object.children.find(child => child.id === this.child.id).name = newName; | |
this.store.replaceState(state); | |
} | |
/** | |
* Get child name an log for debugging | |
*/ | |
@computedFrom('child.name') | |
get childName() { | |
console.log('Got name for ', this.child.id); | |
return this.child.name; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>Aurelia</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
</head> | |
<body aurelia-app> | |
<h1>Loading...</h1> | |
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/jspm_packages/system.js"></script> | |
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/config.js"></script> | |
<script> | |
System.import('aurelia-bootstrapper'); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {freeze} from './util'; | |
export class Store { | |
/** | |
* Current application state | |
* @type {Object} | |
*/ | |
_state:Object; | |
/** | |
* List of subscribers to notify on state change | |
* @type {Array} | |
*/ | |
_callbacks:Array = []; | |
/** | |
* Initialize store | |
*/ | |
constructor() { | |
this._state = freeze({}); | |
} | |
/** | |
* Subscribe to state changes and get the initial state | |
* @param {Function} callback Will be initially called and when state changes. The state is passed as parameter. | |
*/ | |
subscribe(callback: Function): void { | |
this._callbacks.push(callback); | |
// Call back with current state | |
callback(this.getState()); | |
} | |
/** | |
* Replaces the current application state with a new one | |
* @param {State} newState | |
*/ | |
replaceState(newState: Object): void { | |
const nextState = freeze(newState); | |
this._callbacks.forEach(callback => | |
callback(nextState, this._state) | |
); | |
this._state = nextState; | |
} | |
/** | |
* Get a clone of the current state | |
* @returns {Object} | |
*/ | |
getState(): Object { | |
return this._state; | |
} | |
/** | |
* Dispatch a action and update the application state | |
* @param {AbstractAction} action | |
*/ | |
dispatch(action: AbstractAction): void { | |
this.replaceState(action.state); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Helper function to use splice to add an array entry or to just assign the property | |
* Splice is important because otherwise JS/Aurelia observers doesn't recognize changes | |
* @param {Object|Array} objOrArray | |
* @param {string} keyOrIndex | |
* @param {*} value | |
*/ | |
function setOrSplice(objOrArray, keyOrIndex, value): void { | |
if (Array.isArray(objOrArray) && isFinite(keyOrIndex)) { | |
objOrArray.splice(keyOrIndex, 0, value); | |
} else { | |
objOrArray[keyOrIndex] = value; | |
} | |
} | |
/** | |
* Merge objects or arrays deep | |
* @param {Object|Array} destination | |
* @param {Array<Object|Array>} objects | |
* @returns {Object} | |
*/ | |
export function deepMerge(destination: Object, ...objects: Array<Object>) { | |
// Iterate over objects object by object | |
for (let o = 0; o < objects.length; o++) { | |
// Iterate over object levels | |
const levels = [{target: destination, source: objects[o], path: []}]; | |
for (let l = 0; l < levels.length; l++) { | |
// Iterate over level properties | |
const {target, source, path} = levels[l]; | |
const keys = Object.keys(source); | |
for (let k = 0; k < keys.length; k++) { | |
const key = keys[k]; | |
const value = source[key]; | |
// If the value is an array concat with target | |
if (Array.isArray(value)) { | |
// Create new array (or array extending class) if not exist in target | |
if (Array.isArray(target[key])) { | |
target[key].splice(0, target[key].length); | |
} else { | |
setOrSplice(target, key, new value.constructor()); | |
} | |
levels.push({target: target[key], source: value, path: path.concat([key])}); | |
// If the value is an object add it as level | |
} else if (value && typeof value === 'object') { | |
// Ensure the target is an object we can merge into | |
if (typeof target[key] !== 'object') { | |
setOrSplice(target, key, new value.constructor()); | |
} | |
levels.push({target: target[key], source: value, path: path.concat([key])}); | |
} else { | |
setOrSplice(target, key, value); | |
} | |
} | |
} | |
} | |
return destination; | |
} | |
/** | |
* Freezes an object deep and prevent any modification | |
* @param {Object} obj | |
* @returns {Object} | |
*/ | |
export function freeze(obj: Object): Object { | |
// Iterate over all nesting levels to freeze them | |
const levels = [obj]; | |
for (let l = 0; l < levels.length; l++) { | |
const target = levels[l]; | |
// Create clone function to create a unfrozen object | |
if (!Object.isFrozen(target)) { | |
Object.defineProperty(target, 'clone', { | |
enumerable: false, | |
writable: false, | |
value: function cloneFrozen() { | |
const clone = deepMerge({}, {target}); | |
return clone.target; | |
} | |
}); | |
Object.freeze(target); | |
} | |
const keys = Object.keys(target); | |
for (let k = 0; k < keys.length; k++) { | |
const key = keys[k]; | |
if (target[key] && typeof target[key] === 'object') { | |
levels.push(target[key]); | |
} | |
} | |
} | |
return obj; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment