Skip to content

Commit

Permalink
Cleanup markdown sample code (#44)
Browse files Browse the repository at this point in the history
* fix markdown code snippets

* Use const/let in sample code

* run eslint on markdown code

* run eslint on markdown code
  • Loading branch information
Christopher Baker committed Feb 15, 2018
1 parent 78d083e commit e185638
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 61 deletions.
18 changes: 9 additions & 9 deletions can-view-nodelist.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ The basic use is
A `target` is going to be hydrated:

```js
target.hydrate(scope)
target.hydrate( scope );
```

This will call the callbacks on placeholder elements.

Those callbacks register their placeholder like this:

```js
nodeLists.register(nodeList = [placeholderElement], null)
nodeLists.register( nodeList = [ placeholderElement ], null );
```

Then they render the content for the
Expand All @@ -59,15 +59,15 @@ After the content renders, it will call:
// this doesn't actually update the dom. But this will
// detach any "old" nodeLists within `nodeList`
// but oldNodes are all the nodes within the nodeLists
var oldNodes = nodeLists.update(
nodeList,
renderedContentFragment.childNodes );
const oldNodes = nodeLists.update(
nodeList,
renderedContentFragment.childNodes );
```

The children calling `.update()` end up adding to the parent `nodeList`'s `.replacements`
array. `nodList` might look like:

```js
```
[
TEXT_NODE<> //original placeholder text node
replacements: [
Expand All @@ -86,7 +86,7 @@ array. `nodList` might look like:
When `.update` is called on `nodeList`, the `renderedContentFragment` will have
the final content for what is being rendered. For example, it will be a fragment like:

```js
```
Items:
<label></label>
<label></label>
Expand All @@ -97,7 +97,7 @@ Items:

1. Unregister any child nodeLists previously within `nodeList`. (there won't be any at this point)
2. Make a Map of the first node in a `replacements` nodeList to its nodelist:
```js
```
replacementsMap = Map({
[<label>]: [
<label>
Expand All @@ -111,7 +111,7 @@ Items:
```
3. Go through the nodes in renderedContentFragment. If any of them are in the replacementsMap,
update `nodeList` accordingly. `nodeList` will then look like:
```js
```
[
TEXT_NODE<"Items: ">,
[
Expand Down
114 changes: 62 additions & 52 deletions docs/nodelists-blog-post.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,35 @@ From there on, there are two widly different code paths needed when registering

The other is when there is a supplied parent nodeList, as happens in nodes directly registered in `can-component` and `can-stache`. Unlike the previous case which sets the nodeList directly on the global nodeMap, the parent case is transactional and requires and update step. In the parent case we have three possible paths to go down:

* A special case is to register and pass `true` as the parent nodeList. This is a case for when a nodeList intends to be a parentList to another nodeList, but it is not itself being registered to a parentList.
* A special case is to register and pass `true` as the parent nodeList. This is a case for when a nodeList intends to be a parentList to another nodeList, but it is not itself being registered to a parentList.

* The case with a parent nodeList and directlyNested having a truthy value, pushes the nodeList onto the parent nodeList's `replacements` (the nodeList is pushed as a whole rather than its elements). `replacements` is used when updating the nodeList; this essentially means that updating a _parent_ nodeList with new nodes is _transactional_.
* The case with a parent nodeList and directlyNested having a truthy value, pushes the nodeList onto the parent nodeList's `replacements` (the nodeList is pushed as a whole rather than its elements). `replacements` is used when updating the nodeList; this essentially means that updating a _parent_ nodeList with new nodes is _transactional_.

* The case with a parent nodeList and directlyNested having a falsy value, pushes the nodeList onto the parent nodeList's `newDeepChildren` (the nodeList is pushed as a whole rather than its elements). Again, because registration with a parent is transactional, `newDeepChildren` will be set as `deepChildren` when `update` is called.

Here's an example of registering a list as a deep child (not a replacement) of another list:

```javascript
list1 = [document.createTextNode("")];
list2 = [document.createTextNode("a"), document.createTextNode("1")];
```js
const list1 = [ document.createTextNode( "" ) ];
const list2 = [ document.createTextNode( "a" ), document.createTextNode( "1" ) ];

nodeLists.register(list1, null, true)
nodeLists.register(list2, null, list1)
nodeLists.register( list1, null, true );
nodeLists.register( list2, null, list1 );

list1.deepChildren // -> []
list1.newDeepChildren -> [Array[2]]
list1.newDeepChildren[0] === list2 // -> true
list2.parentList === list1 // -> true
list1.deepChildren; // -> []
list1.newDeepChildren; // -> [Array[2]]
list1.newDeepChildren[ 0 ] === list2; // -> true
list2.parentList === list1; // -> true

list3 = [document.createTextNode("b"), document.createTextNode("2"), document.createTextNode("ii")]
nodeLists.register(list3, null, list1, true)
const list3 = [
document.createTextNode( "b" ),
document.createTextNode( "2" ),
document.createTextNode( "ii" )
];
nodeLists.register( list3, null, list1, true );

list1.replacements // -> [Array[3]]
list3.parentList === list1 // -> true
list1.replacements; // -> [Array[3]]
list3.parentList === list1; // -> true
```

`list1` now has a "new" deep child in `list2` and a replacement in `list3`. These don't mean anything by themselves. What has to happen from here is that the nodeList will be `update`d with new contents, which *may* contain `list2` and/or `list3`.
Expand All @@ -72,7 +76,7 @@ Since span1 comes first, it will be the key to recognizing the spans list while
To demonstrate register a list of nodes that covers the spans and the label, and adds in new texts

[text1, span1, span2, text2, label2]

nestList() will iterate over this list, and when it finds a match, it will replace contiguous nodes
with the list that contains them. For efficiency, this always assumes that the subsequence of nodes
replaced by a list (keyed on its first element in the map) is the same as the sequence of nodes in the
Expand Down Expand Up @@ -143,78 +147,84 @@ There are library functions in nodeLists for working with the nodes in a nodeLis

Here's the source of `can.view.live.replace()`:

```javascript
replace: function (nodes, val, teardown) {
```js
{
replace: function( nodes, val, teardown ) {

// #### replace
// Replaces one element with some content while keeping nodeLists data
// correct.
//
//
// Take a copy of old nodeList
var oldNodes = nodes.slice(0),
frag = makeFrag(val);
const oldNodes = nodes.slice( 0 ), frag = makeFrag( val );

// Register a teardown callback
nodeLists.register(nodes, teardown);
nodeLists.register( nodes, teardown );

// Mark each node as belonging to the node list.
nodeLists.update(nodes, childNodes(frag));
nodeLists.update( nodes, childNodes( frag ) );

// Replace old nodes with new on the DOM
nodeLists.replace(oldNodes, frag);
nodeLists.replace( oldNodes, frag );
return nodes;
},
}
}
```

This is generally how the flow works when working with the global nodeMap. First be sure that the nodeList is `register()`ed to have all of the trees and everything contained in the nodeMap, then `update()` the nodeList to have the new nodes from the frag (this doesn't call the `teardown` supplied here but it does call unregister functions during any child nodeList's registration), then finally `replace()` the old nodes in the DOM with the content of the fragment.

For the second one we'll have to jump around a bit. This flow starts when we render a partial into a parent Stache. So we'll call `makeLiveBindingPartialRenderer()` from `can-stache`'s mustache_core.js. This sets up a new parent nodeList based on the text node that's the placeholder for this partial before the Stache hydrates.

```javascript
var nodeList = [this];
```js
const nodeList = [ this ];
```

Farther down this function, a `renderer` callback references this nodelist and hydrates into this nodeList the fragment created by rendering the partial template.

```javascript
renderer = function () {
if (typeof localPartialName === 'function') {
return localPartialName(scope, options, nodeList);
} else {
return core.getTemplateById(localPartialName)(scope, options, nodeList);
}
};
```js
renderer = function() {
if ( typeof localPartialName === "function" ) {
return localPartialName( scope, options, nodeList );
} else {
return core.getTemplateById( localPartialName )( scope, options, nodeList );
}
};
```

So now when this is called, `localPartialName` is either a function (from the scope), or it's a string (referencing the DOM) and gets resolved to a function. This function is returned from `stache.compile()` via `HTMLSectionBuilder.prototype.compile()`, is usually called a "renderer," and takes as arguments scope, options, and nodeList. Scope is the only required argument, but the fact that we're passing in a `nodeList` is key here. This renderer gets the compiled AST for the Stache template, does a couple cursory checks on scope and options then does this:

```javascript
return compiled.hydrate(scope, options, nodeList);
```js
compiled.hydrate( scope, options, nodeList );
```

Let's assume that we have some callbacks to hydrate. If the Stache only had raw text, the hydrator would just return a frag and not set anything up. Let's see what happens when we have a scope lookup like `{{foo}}`, which triggers a callback to `makeLiveBindingBranchRenderer()` in mustache_core.js

```javascript
return function branchRenderer(scope, options, parentSectionNodeList, truthyRenderer, falseyRenderer) {
var nodeList = [this];
nodeList.expression = expressionString;
nodeLists.register(nodeList, null, parentSectionNodeList || true, state.directlyNested);
```js
function branchRenderer( scope, options, parentSectionNodeList, truthyRenderer, falseyRenderer ) {
const nodeList = [ this ];
nodeList.expression = expressionString;
nodeLists.register( nodeList, null, parentSectionNodeList || true, state.directlyNested );
}
```

`parentSectionNodeList` contains the node in the parent Stache rendering where the partial was called. `nodeList = [this]` is now a nodeList containing the node for `{{foo}}`. So you can see that the registration of the lookup expression is a child of the partial. If the partial were later removed completely from the surrounding template, all of the nodes we're currently constructing would have to be unregistered and moved. `state.directlyNested` is true here because we want the content of the rendered partial to completely replace any placeholder nodes.

Most of the function is now spent setting up a compute, but then we have to actually set up the rendering. That is accomplished by using the library functions in [can-view-live](https://github.com/canjs/can-view-live), in this case one to render plain text:

```javascript
live.text(this, computeValue, this.parentNode, nodeList);
```js
live.text( this, computeValue, this.parentNode, nodeList );
```

Because we pass a nodeList into `live.text()`, it knows that the nodes should be updated and replaced through operating on the nodeList like this:

```javascript
var node = el.ownerDocument.createTextNode(live.makeString(compute()));
if(nodeList) {
nodeList.unregistered = data.teardownCheck;
data.nodeList = nodeList;
nodeLists.update(nodeList, [node]);
nodeLists.replace([el], node);
}
```js
const node = el.ownerDocument.createTextNode( live.makeString( compute() ) );
if ( nodeList ) {
nodeList.unregistered = data.teardownCheck;
data.nodeList = nodeList;
nodeLists.update( nodeList, [ node ] );
nodeLists.replace( [ el ], node );
}
```

`data` is an object created by `live.listen()`, and isn't particularly important to know here. But what is important to note is the nodeList is updated with a text node containing the rendered text content (from calling `compute()`) and then the element that was being used as a placeholder (`el`) is replaced in the DOM by the rendered text node. The parent nodeList, containing the placeholder for the partial, also has gotten the directly nested child nodeList's content updated into it via calling `live.html()`. I avoided using that code for demonstration because it's a bit less clear in what it's doing.
Expand Down

0 comments on commit e185638

Please sign in to comment.