Skip to content

Commit

Permalink
component/jsx) Support partials in children
Browse files Browse the repository at this point in the history
This would improve substantially with a `trackArrayChanges` on the right observables.
  • Loading branch information
brianmhunt committed Jun 26, 2018
1 parent e5ebeae commit 5615bf6
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 20 deletions.
42 changes: 40 additions & 2 deletions packages/tko.binding.component/spec/componentBindingBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -894,8 +894,6 @@ describe('Components: Component binding', function () {
it('inserts a partial when the `template` is an array', function () {
class ViewModel extends components.ComponentABC {
static get template () {
// Passing <div attr={obs}>{o2}</div> through
// babel-plugin-transform-jsx will yield:
return [
{ elementName: 'b', attributes: { }, children: ['x'] },
{ elementName: 'i', attributes: { }, children: ['y'] },
Expand All @@ -907,6 +905,46 @@ describe('Components: Component binding', function () {
applyBindings(outerViewModel, testNode)
expect(testNode.children[0].innerHTML).toEqual('<b>x</b><i>y</i><em>z</em>')
})

it('inserts partials from `children`', function () {
const children = [
'abc',
{ elementName: 'c', attributes: {}, children: [['C']] }
]
class ViewModel extends components.ComponentABC {
static get template () {
return [
{ elementName: 'b', attributes: { }, children: ['x', children] }
]
}
}
ViewModel.register('test-component')
applyBindings(outerViewModel, testNode)
expect(testNode.children[0].innerHTML).toEqual('<b>xabc<c>C</c></b>')
})

it('inserts & updates observable partials from `children`', function () {
const children = observableArray([
'abc',
{ elementName: 'c', attributes: {}, children: [['C']] }
])
class ViewModel extends components.ComponentABC {
static get template () {
return [
{ elementName: 'b', attributes: { }, children: ['x', children] }
]
}
}
ViewModel.register('test-component')
applyBindings(outerViewModel, testNode)
expect(testNode.children[0].innerHTML).toEqual('<b>xabc<c>C</c></b>')

children.pop()
expect(testNode.children[0].innerHTML).toEqual('<b>xabc</b>')

children.unshift('rrr')
expect(testNode.children[0].innerHTML).toEqual('<b>xrrrabc</b>')
})
})

describe('slots', function () {
Expand Down
60 changes: 42 additions & 18 deletions packages/tko.utils.jsx/src/jsx.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import {
cleanNode, removeNode, addDisposeCallback
removeNode, addDisposeCallback
} from 'tko.utils'

import {
Expand Down Expand Up @@ -44,8 +44,12 @@ export function jsxToNode (jsx) {
return node
}

function appendChild (possibleTemplateElement, nodeToAppend) {
if ('content' in possibleTemplateElement) {
function appendChildOrChildren (possibleTemplateElement, nodeToAppend) {
if (Array.isArray(nodeToAppend)) {
for (const node of nodeToAppend) {
appendChildOrChildren(possibleTemplateElement, node)
}
} else if ('content' in possibleTemplateElement) {
possibleTemplateElement.content.appendChild(nodeToAppend)
} else {
possibleTemplateElement.appendChild(nodeToAppend)
Expand All @@ -69,7 +73,7 @@ function updateChildren (node, children, subscriptions) {
if (isObservable(child)) {
subscriptions.push(monitorObservableChild(node, child))
} else {
appendChild(node, convertJsxChildToDom(child))
appendChildOrChildren(node, convertJsxChildToDom(child))
}
}
}
Expand Down Expand Up @@ -102,28 +106,48 @@ function updateAttributes (node, attributes, subscriptions) {
}
}

/**
*
* @param {jsx} newJsx
* @param {HTMLElement|Array} toReplace
* @return {HTMLElement|Array} Nodes to replace next time
*
* TODO: Use trackArrayChanges to minimize changes to the DOM and state-loss.
*/
function replaceNodeOrNodes (newJsx, toReplace, parentNode) {
const newNodeOrNodes = convertJsxChildToDom(newJsx)
const $context = contextFor(toReplace)

if (Array.isArray(toReplace)) {
for (const node of toReplace) { removeNode(node) }
} else {
removeNode(toReplace)
}
appendChildOrChildren(parentNode, newNodeOrNodes)
if ($context) { applyBindings($context, newNodeOrNodes) }
return newNodeOrNodes
}

function monitorObservableChild (node, child) {
const jsx = unwrap(child)
let nodeToReplace = convertJsxChildToDom(jsx)
let toReplace = convertJsxChildToDom(jsx)
appendChildOrChildren(node, toReplace)

const subscription = child.subscribe(newJsx => {
const newNode = convertJsxChildToDom(newJsx)
const $context = contextFor(node)
cleanNode(nodeToReplace)
node.replaceChild(newNode, nodeToReplace)
if ($context) {
applyBindings(contextFor(node), newNode)
}
nodeToReplace = newNode
toReplace = replaceNodeOrNodes(newJsx, toReplace, node)
})

appendChild(node, nodeToReplace)
return subscription
}

/**
* Convert a child to the anticipated HTMLElement(s).
* @param {string|array|jsx} child
* @return {Array|Comment|HTMLElement}
*/
function convertJsxChildToDom (child) {
return typeof child === 'string'
? document.createTextNode(child)
: child ? jsxToNode(child)
: document.createComment('[jsx placeholder]')
return typeof child === 'string' ? document.createTextNode(child)
: Array.isArray(child) ? child.map(convertJsxChildToDom)
: child ? jsxToNode(child)
: document.createComment('[jsx placeholder]')
}

0 comments on commit 5615bf6

Please sign in to comment.