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) {
if(sub.__cmp)
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;
for( var i = 0, _i = children.length; i < _i; i++ ){
......@@ -365,10 +369,14 @@ NS.apply = function(a,b) {
if(subEl instanceof _Promise){
var origin = subEl;
subEl = function(update) {
origin.then(function(a,b) {
update(a);
origin.then(function(v) {
v !== void 0 && update(v);
}, function(err) {
console.error('D: promise child rejected', err);
});
}
type = 'function';
notObject = true;
}
if(isHook){
......@@ -385,7 +393,7 @@ NS.apply = function(a,b) {
isNotFragment && el.setAttribute('data-hooked', 'yep');
// maybe do it through outer weak map?
isNotFragment && (el.__un = el.__un || []);
isNotFragment && (el.__un = el.__un || new D.Unsubscribe());
var hookFn, release;
if(isHook){
......@@ -402,8 +410,13 @@ NS.apply = function(a,b) {
if(!el)
return;
var isInDOM = D.isInDOM(el);
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();
D.appendChild( fragment, ArraySlice.call( arguments ) );
......@@ -411,11 +424,13 @@ NS.apply = function(a,b) {
if( !tmp || !tmp.parentNode )
return;
isInDOM && D._recursiveCmpCall(el, fragment, 'beforeAddToDOM');
el.insertBefore( fragment, tmp );
isInDOM && D._recursiveCmpCall(el, {childNodes: list}, 'afterAddToDOM');
}
};
release = subEl.hook( hookFn );
isNotFragment && el.__un.push(release);
isNotFragment && release && el.__un.add(release);
}else{
hookFn = function(){
......@@ -427,8 +442,13 @@ NS.apply = function(a,b) {
if(!el)
return;
var isInDOM = D.isInDOM(el);
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();
D.appendChild( fragment, ArraySlice.call( arguments ) );
......@@ -437,7 +457,6 @@ NS.apply = function(a,b) {
if(!tmp || !tmp.parentNode)
return;
var isInDOM = D.isInDOM(el);
isInDOM && D._recursiveCmpCall(el, fragment, 'beforeAddToDOM');
el.insertBefore(fragment, tmp);
isInDOM && D._recursiveCmpCall(el, {childNodes: list}, 'afterAddToDOM');
......@@ -445,10 +464,14 @@ NS.apply = function(a,b) {
};
release = subEl( hookFn );
if(release instanceof _Promise){
// TODO CHECK IF LEAKING
release.then(hookFn);
release.then(function(v) {
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 ){
el.appendChild( D.Text( subEl ) );
......
This diff is collapsed. Click to expand it.
......@@ -644,6 +644,14 @@ StoreBinding.prototype = {
draw(val);
});
},
map: function(transform) {
var source = this;
return function backwardCallback(update) {
source.sub(function(val) {
update(transform(val));
});
};
},
array: function() {
return this.store.array(this.key);
}
......@@ -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(){
} );
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(){
var clicked = 0
var div = D.div({onclick: ()=>clicked++});
......
......@@ -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