Commit f06190a2 by Иван Кубота

__un is D.Unsubscribe now

ensure that beforeAddToDOM/afterAddToDOM and beforeRemoveFromDOM/afterRemoveFromDOM are called on the whole path. It was leaking support of async functions as children
parent 24d5ed8e
...@@ -295,6 +295,10 @@ NS.apply = function(a,b) { ...@@ -295,6 +295,10 @@ NS.apply = function(a,b) {
if(sub.__cmp) if(sub.__cmp)
sub.__cmp[fnName] && sub.__cmp[fnName](el); sub.__cmp[fnName] && sub.__cmp[fnName](el);
if(fnName === 'beforeRemoveFromDOM' && sub.__un && sub.__un.un){
sub.__un.un();
}
var children = Array.isArray(sub) ? sub : sub.childNodes; var children = Array.isArray(sub) ? sub : sub.childNodes;
for( var i = 0, _i = children.length; i < _i; i++ ){ for( var i = 0, _i = children.length; i < _i; i++ ){
...@@ -365,10 +369,14 @@ NS.apply = function(a,b) { ...@@ -365,10 +369,14 @@ NS.apply = function(a,b) {
if(subEl instanceof _Promise){ if(subEl instanceof _Promise){
var origin = subEl; var origin = subEl;
subEl = function(update) { subEl = function(update) {
origin.then(function(a,b) { origin.then(function(v) {
update(a); v !== void 0 && update(v);
}, function(err) {
console.error('D: promise child rejected', err);
}); });
} }
type = 'function';
notObject = true;
} }
if(isHook){ if(isHook){
...@@ -385,7 +393,7 @@ NS.apply = function(a,b) { ...@@ -385,7 +393,7 @@ NS.apply = function(a,b) {
isNotFragment && el.setAttribute('data-hooked', 'yep'); isNotFragment && el.setAttribute('data-hooked', 'yep');
// maybe do it through outer weak map? // maybe do it through outer weak map?
isNotFragment && (el.__un = el.__un || []); isNotFragment && (el.__un = el.__un || new D.Unsubscribe());
var hookFn, release; var hookFn, release;
if(isHook){ if(isHook){
...@@ -402,8 +410,13 @@ NS.apply = function(a,b) { ...@@ -402,8 +410,13 @@ NS.apply = function(a,b) {
if(!el) if(!el)
return; return;
var isInDOM = D.isInDOM(el);
for( var i = 0, _i = list.length; i < _i; i++ ){ for( var i = 0, _i = list.length; i < _i; i++ ){
list[ i ].parentNode === el && el.removeChild( list[ i ] ); if(list[ i ].parentNode === el){
isInDOM && D._recursiveCmpCall(el, list[ i ], 'beforeRemoveFromDOM');
el.removeChild( list[ i ] );
isInDOM && D._recursiveCmpCall(el, list[ i ], 'afterRemoveFromDOM');
}
} }
var fragment = document.createDocumentFragment(); var fragment = document.createDocumentFragment();
D.appendChild( fragment, ArraySlice.call( arguments ) ); D.appendChild( fragment, ArraySlice.call( arguments ) );
...@@ -411,11 +424,13 @@ NS.apply = function(a,b) { ...@@ -411,11 +424,13 @@ NS.apply = function(a,b) {
if( !tmp || !tmp.parentNode ) if( !tmp || !tmp.parentNode )
return; return;
isInDOM && D._recursiveCmpCall(el, fragment, 'beforeAddToDOM');
el.insertBefore( fragment, tmp ); el.insertBefore( fragment, tmp );
isInDOM && D._recursiveCmpCall(el, {childNodes: list}, 'afterAddToDOM');
} }
}; };
release = subEl.hook( hookFn ); release = subEl.hook( hookFn );
isNotFragment && el.__un.push(release); isNotFragment && release && el.__un.add(release);
}else{ }else{
hookFn = function(){ hookFn = function(){
...@@ -427,8 +442,13 @@ NS.apply = function(a,b) { ...@@ -427,8 +442,13 @@ NS.apply = function(a,b) {
if(!el) if(!el)
return; return;
var isInDOM = D.isInDOM(el);
for( var i = 0, _i = list.length; i < _i; i++ ){ for( var i = 0, _i = list.length; i < _i; i++ ){
list[ i ].parentNode === el && el.removeChild( list[ i ] ); if(list[ i ].parentNode === el){
isInDOM && D._recursiveCmpCall(el, list[ i ], 'beforeRemoveFromDOM');
el.removeChild( list[ i ] );
isInDOM && D._recursiveCmpCall(el, list[ i ], 'afterRemoveFromDOM');
}
} }
var fragment = document.createDocumentFragment(); var fragment = document.createDocumentFragment();
D.appendChild( fragment, ArraySlice.call( arguments ) ); D.appendChild( fragment, ArraySlice.call( arguments ) );
...@@ -437,7 +457,6 @@ NS.apply = function(a,b) { ...@@ -437,7 +457,6 @@ NS.apply = function(a,b) {
if(!tmp || !tmp.parentNode) if(!tmp || !tmp.parentNode)
return; return;
var isInDOM = D.isInDOM(el);
isInDOM && D._recursiveCmpCall(el, fragment, 'beforeAddToDOM'); isInDOM && D._recursiveCmpCall(el, fragment, 'beforeAddToDOM');
el.insertBefore(fragment, tmp); el.insertBefore(fragment, tmp);
isInDOM && D._recursiveCmpCall(el, {childNodes: list}, 'afterAddToDOM'); isInDOM && D._recursiveCmpCall(el, {childNodes: list}, 'afterAddToDOM');
...@@ -445,10 +464,14 @@ NS.apply = function(a,b) { ...@@ -445,10 +464,14 @@ NS.apply = function(a,b) {
}; };
release = subEl( hookFn ); release = subEl( hookFn );
if(release instanceof _Promise){ if(release instanceof _Promise){
// TODO CHECK IF LEAKING release.then(function(v) {
release.then(hookFn); v !== void 0 && hookFn(v);
}, function(err) {
console.error('D: async child rejected', err);
});
release = null;
} }
isNotFragment && el.__un.push(release); isNotFragment && release && el.__un.add(release);
} }
}else if( subEl !== void 0 && subEl !== false && subEl !== null ){ }else if( subEl !== void 0 && subEl !== false && subEl !== null ){
el.appendChild( D.Text( subEl ) ); el.appendChild( D.Text( subEl ) );
......
This diff is collapsed. Click to expand it.
...@@ -644,6 +644,14 @@ StoreBinding.prototype = { ...@@ -644,6 +644,14 @@ StoreBinding.prototype = {
draw(val); draw(val);
}); });
}, },
map: function(transform) {
var source = this;
return function backwardCallback(update) {
source.sub(function(val) {
update(transform(val));
});
};
},
array: function() { array: function() {
return this.store.array(this.key); return this.store.array(this.key);
} }
...@@ -838,6 +846,14 @@ HookPrototype.prototype = { ...@@ -838,6 +846,14 @@ HookPrototype.prototype = {
} }
}); });
} }
},
map: function(transform) {
var source = this;
return function backwardCallback(update) {
source.hook(function(val) {
update(transform(val));
});
};
} }
}; };
......
...@@ -53,6 +53,50 @@ describe('DOM lib', function(){ ...@@ -53,6 +53,50 @@ describe('DOM lib', function(){
} ); } );
it( 'should work with async setter pattern (_(content) calls)', async function(){
var div = D.div({},
async (_) => { await delay(50); _('hello'); }
);
await delay(100);
if(div.outerHTML !== '<div data-hooked="yep">hello</div>')
throw new Error(div.outerHTML);
else
return true;
} );
it( 'should not clear content when async setter resolves to undefined', async function(){
var div = D.div({},
async (_) => {
await delay(50);
_('first');
await delay(50);
_('second');
}
);
await delay(150);
if(div.outerHTML !== '<div data-hooked="yep">second</div>')
throw new Error(div.outerHTML);
else
return true;
} );
it( 'should accept Promise as child directly', async function(){
var div = D.div({},
delay(50).then(function() { return 'resolved'; })
);
await delay(100);
if(div.outerHTML !== '<div data-hooked="yep">resolved</div>')
throw new Error(div.outerHTML);
else
return true;
} );
it( 'events', function(){ it( 'events', function(){
var clicked = 0 var clicked = 0
var div = D.div({onclick: ()=>clicked++}); var div = D.div({onclick: ()=>clicked++});
......
...@@ -563,3 +563,57 @@ describe('Feature. Binding.valEqual', function() { ...@@ -563,3 +563,57 @@ describe('Feature. Binding.valEqual', function() {
}); });
}); });
describe('Feature. ReactiveValue.map', function() {
it('should return a backward callback that transforms values', function() {
var num = new Store.Value.Number(5);
var results = [];
var mapped = num.map(function(val) { return val * 2; });
mapped(function(val) { results.push(val); });
num.set(10);
num.set(3);
assert.deepEqual(results, [10, 20, 6]);
});
it('should work with boolean values', function() {
var flag = new Store.Value.Boolean(false);
var results = [];
var mapped = flag.map(function(val) { return val ? 'block' : 'none'; });
mapped(function(val) { results.push(val); });
flag.set(true);
flag.set(false);
assert.deepEqual(results, ['none', 'block', 'none']);
});
it('should work with string values', function() {
var name = new Store.Value.String('hello');
var results = [];
var mapped = name.map(function(val) { return val.toUpperCase(); });
mapped(function(val) { results.push(val); });
name.set('world');
assert.deepEqual(results, ['HELLO', 'WORLD']);
});
it('should work on StoreBinding', function() {
var s = new Store({ count: 0 });
var binding = s.bind('count');
var results = [];
var mapped = binding.map(function(val) { return val + ' items'; });
mapped(function(val) { results.push(val); });
s.set('count', 3);
s.set('count', 1);
assert.deepEqual(results, ['0 items', '3 items', '1 items']);
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment