ampersand-virtual-dom-mixin
Advanced tools
Comparing version 0.1.2 to 0.2.0
160
mixin.js
@@ -7,4 +7,19 @@ var parse = require('html-parse-stringify').parse; | ||
var isString = require('amp-is-string'); | ||
var getPath = require('get-object-path'); | ||
function astAttrsToVdomAttrs(attrs) { | ||
var tagAttrs = { | ||
attributes: attrs | ||
}; | ||
tagAttrs.key = attrs.key; | ||
delete attrs.key; | ||
tagAttrs.namespace = attrs.namespace; | ||
delete attrs.namespace; | ||
return tagAttrs; | ||
} | ||
module.exports = { | ||
@@ -14,2 +29,3 @@ renderOnViewChange: function () { | ||
}, | ||
renderOnModelChange: function () { | ||
@@ -20,6 +36,6 @@ if (this.model) { | ||
}, | ||
renderWithTemplate: function (data) { | ||
var firstRender = !this.tree || !this.el; | ||
var renderedTemplate, newTree; | ||
renderTemplateToVdom: function (data) { | ||
var renderedTemplate; | ||
if (isString(this.template)) { | ||
@@ -31,8 +47,22 @@ renderedTemplate = this.template; | ||
newTree = this.astToVdom(parse(renderedTemplate.trim())); | ||
return this.astToVdom(parse(renderedTemplate.trim())); | ||
}, | ||
if (firstRender) { | ||
renderWithTemplate: function (data) { | ||
var newTree = this.renderTemplateToVdom(data); | ||
var isBackbone = typeof this.setElement === 'function'; | ||
var isFirstRender; | ||
//view was initialized with an el, not yet rendered | ||
if (!this.tree && this.el) { | ||
this.tree = this.astToVdom(parse(this.el.outerHTML)); | ||
isFirstRender = true; | ||
} | ||
//first render | ||
if (!this.el) { | ||
isFirstRender = true; | ||
var el = createElement(newTree); | ||
this.tree = newTree; | ||
if (this.setElement) { | ||
if (isBackbone) { | ||
this.el.appendChild(el); | ||
@@ -43,7 +73,13 @@ this._backboneEl = el; | ||
} | ||
//subsequent renders | ||
} else { | ||
var patches = diff(this.tree, newTree); | ||
this.tree = newTree; | ||
patch(this._backboneEl || this.el, patches); | ||
if (isBackbone) { | ||
patch(this._backboneEl, patches); | ||
} else { | ||
patch(this.el, patches); | ||
} | ||
} | ||
this.trigger('render:after'); | ||
}, | ||
@@ -57,78 +93,56 @@ | ||
if (ast.type === 'tag') { | ||
if (!(this.components||{})[ast.name]) { | ||
return h(ast.name, this.parseTagAttrs(ast.attrs), ast.children.map(this.astToVdom, this)); | ||
} else { | ||
return h( | ||
ast.name, | ||
astAttrsToVdomAttrs(ast.attrs), | ||
ast.children.map(this.astToVdom, this) | ||
); | ||
} | ||
}, | ||
//TODO: this whole bit needs some work; | ||
var Constructor = this.components[ast.name]; | ||
var attrs = this.parseComponentAttrs(ast.attrs); | ||
_parseSubview: function (subview, name) { | ||
this._subviewStatus = this._subviewStatus || {}; | ||
return { | ||
type: 'Widget', | ||
name: 'MyWidget', | ||
id: ast.attrs.key, | ||
key: ast.attrs.key, | ||
init: function () { | ||
this.view = new Constructor(attrs); | ||
this.view.render(); | ||
return this.view.el; | ||
}, | ||
update: function (previous, dom) { | ||
this.view = previous.view; | ||
this.view.set(attrs); | ||
return this.view.el; | ||
}, | ||
destroy: function () { | ||
this.view.remove(); | ||
} | ||
}; | ||
var self = this; | ||
var opts = { | ||
selector: subview.container || '[data-hook="' + subview.hook + '"]', | ||
waitFor: subview.waitFor || '', | ||
prepareView: subview.prepareView || function () { | ||
return new subview.constructor({ | ||
//el: el, | ||
parent: self | ||
}); | ||
} | ||
} | ||
}, | ||
}; | ||
parseComponentAttrs: function (attrs) { | ||
var key, val, path, match; | ||
var isCurlyRe = /^\s*{\s*([^}]+)\s*}\s*$/; | ||
function updateSubview () { | ||
var el, subview; | ||
for (key in attrs) { | ||
val = attrs[key]; | ||
if (this._subviewStatus[name] && this._subviewStatus[name].rendered) { | ||
//Remove if rendered but no longer in the dom | ||
if (!this.query(opts.selector)) { | ||
this._subviewStatus[name].view.remove(); | ||
delete this._subviewStatus[name]; | ||
delete this[name]; | ||
} | ||
} else { | ||
if (!this.el || !(el = this.query(opts.selector))) { | ||
return; | ||
} | ||
match = val.match(isCurlyRe); | ||
if (!match) { | ||
attrs[key] = coerce(val); | ||
} else { | ||
path = match[1].split('.'); | ||
attrs[key] = path.reduce(function (o, pathPart) { | ||
return o && o[pathPart]; | ||
}, this); | ||
if (!opts.waitFor || getPath(this, opts.waitFor)) { | ||
subview = this[name] = opts.prepareView.call(this); | ||
subview.render(); | ||
this._subviewStatus[name] = { | ||
rendered: true, | ||
view: subview | ||
}; | ||
el.appendChild(subview.el); | ||
this.registerSubview(subview); | ||
} | ||
} | ||
} | ||
return attrs; | ||
}, | ||
parseTagAttrs: function (attrs) { | ||
attrs = { | ||
attributes: attrs | ||
}; | ||
var keep = ['key', 'namespace']; | ||
var i = keep.length; | ||
var k; | ||
while(i--) { | ||
k = keep[i]; | ||
if (attrs.attributes[k]) { | ||
attrs[k] = attrs.attributes[k]; | ||
delete attrs.attributes[k]; | ||
} | ||
} | ||
return attrs; | ||
//this.on('change', updateSubview, this); | ||
this.on('render:after', updateSubview, this); | ||
} | ||
}; | ||
function coerce(str) { | ||
var trimmed = str.trim(); | ||
if (trimmed === 'true') return true; | ||
if (trimmed === 'false') return false; | ||
if (!isNaN(trimmed)) return parseFloat(trimmed); | ||
return str; | ||
} |
{ | ||
"name": "ampersand-virtual-dom-mixin", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"dependencies": { | ||
"amp-is-string": "^1.0.0", | ||
"get-object-path": "0.0.2", | ||
"html-parse-stringify": "^1.0.1", | ||
@@ -11,3 +12,19 @@ "virtual-dom": "0.0.23" | ||
"main": "mixin.js", | ||
"scripts": {} | ||
"scripts": { | ||
"start": "run-browser test/index.js", | ||
"test": "browserify test/index.js | tape-run | tap-spec", | ||
"demo": "beefy demo.js" | ||
}, | ||
"devDependencies": { | ||
"ampersand-view": "^7.2.0", | ||
"beefy": "^2.1.2", | ||
"browserify": "^8.1.0", | ||
"jshint": "^2.5.11", | ||
"phantomjs": "^1.9.13", | ||
"precommit-hook": "^1.0.7", | ||
"run-browser": "^2.0.1", | ||
"tap-spec": "^2.1.2", | ||
"tape": "^3.0.3", | ||
"tape-run": "^0.3.0" | ||
} | ||
} |
# ampersand-virtual-dom-mixin | ||
## *NOTE:* this module is intentially at 0.x.x since the public api is still under discussion. minor/patch releases may be breaking changes until 1.0.0 hits. _Community feedback wanted_ | ||
```js | ||
@@ -18,1 +20,9 @@ var vdomMixin = require('ampersand-virtual-dom-mixin'); | ||
``` | ||
## Notes on usage of this version: | ||
* There's a demo.js in the repo `npm run demo`, http://localhost:9967 | ||
* Rendering subviews manually in an overridden render method is an anti-pattern with this mixin. You'll want to use the `subviews` hash to do so declaratively. | ||
* This enables you in a template to render/remove the target el for a subview, and that el will be created/torn down appropriately. | ||
* This mixin rewrites `_parseSubview`. The prepareView function will no longer receive an `el`, so rendered subviews will be appended to, rather than replace, the selected el. | ||
* component rendering has been removed, for now. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
12054
9
272
28
4
10
1
1
+ Addedget-object-path@0.0.2
+ Addedget-object-path@0.0.2(transitive)