Skip to content

Commit

Permalink
Merge pull request #619 from bitovi/whitecolor-compontent-content-ove…
Browse files Browse the repository at this point in the history
…rflow-bug

Fixes nested components and `<content>` tags.
  • Loading branch information
daffl committed Dec 19, 2013
2 parents 7bbf460 + 36a00b1 commit adca7e1
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 18 deletions.
28 changes: 25 additions & 3 deletions component/component.js
Expand Up @@ -195,6 +195,9 @@ steal("can/util","can/control","can/observe","can/view/mustache","can/view/bindi
// create a control to listen to events
this._control = new this.constructor.Control(el, {scope: this.scope});


var self = this;

// if this component has a template (that we've already converted to a renderer)
if( this.constructor.renderer ) {
// add content to tags
Expand All @@ -208,12 +211,31 @@ steal("can/util","can/control","can/observe","can/view/mustache","can/view/bindi
// otherwise, render what was within <content>, the default code
var subtemplate = hookupOptions.subtemplate || rendererOptions.subtemplate;

if(subtemplate) {
can.view.live.replace( [el], subtemplate(rendererOptions.scope, rendererOptions.options.add(helpers) ) );
if(subtemplate) {


// rendererOptions.options is a scope of helpers where `<content>` was found, so
// the right helpers should already be available.
// However, _tags.content is going to point to this current content callback. We need to
// remove that so it will walk up the chain

delete helpers._tags.content;

can.view.live.replace( [el], subtemplate(
// This is the context of where `<content>` was found
// which will have the the component's context
rendererOptions.scope,



rendererOptions.options ) );

// restore the content tag so it could potentially be used again (as in lists)
helpers._tags.content = arguments.callee;
}
}
// render the component's template
var frag = this.constructor.renderer( renderedScope, helpers);
var frag = this.constructor.renderer( renderedScope, hookupOptions.options.add(helpers) );
} else {
// otherwise render the contents between the
var frag = can.view.frag( hookupOptions.subtemplate ? hookupOptions.subtemplate(renderedScope, hookupOptions.options.add(helpers)) : "");
Expand Down
78 changes: 72 additions & 6 deletions component/component_test.js
Expand Up @@ -778,9 +778,9 @@ test("defined view models (#563)", function(){
});

test("scope not rebound correctly (#550)", function(){

var nameChanges = 0;

can.Component.extend({
tag: "scope-rebinder",
events: {
Expand All @@ -789,21 +789,87 @@ test("scope not rebound correctly (#550)", function(){
}
}
});

var template = can.view.mustache("<scope-rebinder></scope-rebinder>");

var frag = template();
var scope = can.scope( can.$(frag.childNodes[0]) );

var n1 = can.compute(),
n2 = can.compute()

scope.attr("name", n1 );
n1("updated");
scope.attr("name", n2);
n2("updated");
equal(nameChanges, 2)
})

test("content extension stack overflow error", function(){

can.Component({
tag: 'outer-tag',
template: '<inner-tag>inner-tag CONTENT <content/></inner-tag>'
})

can.Component({
tag: 'inner-tag',
template: 'inner-tag TEMPLATE <content/>'
})

// currently causes Maximum call stack size exceeded
var template = can.view.mustache("<outer-tag>outer-tag CONTENT</outer-tag>");

// RESULT = <outer-tag><inner-tag>inner-tag TEMPLATE inner-tag CONTENT outer-tag CONTENT</inner-tag></outer-tag>

var frag = template();

equal(frag.childNodes[0].childNodes[0].innerHTML, 'inner-tag TEMPLATE inner-tag CONTENT outer-tag CONTENT')

})

test("inserted event fires twice if component inside live binding block", function(){

var inited = 0,
inserted = 0;

can.Component({
tag: 'child-tag',

scope: {
init: function(){
inited++
}
},
events: {
' inserted': function() {
inserted++
}
}
});

can.Component({
tag: 'parent-tag',

template: '{{#shown}}<child-tag></child-tag>{{/shown}}',

scope: {
shown: false
},
events: {
' inserted': function() {
this.scope.attr('shown', true)
}
}
});

var frag = can.view.mustache("<parent-tag></parent-tag>")({})

can.append( can.$("#qunit-test-area") , frag );


equal(inited, 1)
equal(inserted, 1)
});

})()
4 changes: 2 additions & 2 deletions component/test.html
@@ -1,7 +1,8 @@
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="../bower_components/qunit/qunit/qunit.css" />
<link rel="stylesheet" type="text/css" href="../bower_components/qunit/qunit/qunit.css" />

<style>.active {
border: solid 1px red;
}</style>
Expand All @@ -15,7 +16,6 @@ <h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-test-area"></div>

<script type="text/javascript" src="../lib/steal/steal.js"></script>
<script type="text/javascript" src="../bower_components/qunit/qunit/qunit.js"></script>
<script type="text/javascript">
Expand Down
4 changes: 3 additions & 1 deletion util/inserted/inserted.js
Expand Up @@ -2,6 +2,8 @@ steal('can/util/can.js',function (can) {
// Given a list of elements, check if they are in the dom, if they
// are in the dom, trigger inserted on them.
can.inserted = function(elems){
// prevent mutations from changing the looping
elems = can.makeArray(elems);
var inDocument = false,
checked = false,
children;
Expand All @@ -19,8 +21,8 @@ steal('can/util/can.js',function (can) {
}

if(inDocument && elem.getElementsByTagName){
can.trigger(elem,"inserted",[],false);
children = can.makeArray( elem.getElementsByTagName("*") );
can.trigger(elem,"inserted",[],false);
for ( var j = 0, child; (child = children[j]) !== undefined; j++ ) {
// Trigger the destroyed event
can.trigger(child,"inserted",[],false);
Expand Down
11 changes: 5 additions & 6 deletions view/scanner.js
Expand Up @@ -186,12 +186,11 @@ Scanner.hookupTag = function(hookupOptions){
fn(el);
});

var helperTags = hookupOptions.options.read('helpers._tags',{}).value,
tagName= hookupOptions.tagName,
tagCallback = ( helperTags && helperTags[tagName] ) || Scanner.tags[tagName]


// if this was an element like <foo-bar> that doesn't have a component, just render its content
var tagName= hookupOptions.tagName,
helperTagCallback = hookupOptions.options.read('helpers._tags.'+tagName,{isArgument: true, proxyMethods: false}).value,
tagCallback = helperTagCallback || Scanner.tags[tagName];

// If this was an element like <foo-bar> that doesn't have a component, just render its content
var scope = hookupOptions.scope,
res = tagCallback ? tagCallback(el, hookupOptions) : scope;

Expand Down

0 comments on commit adca7e1

Please sign in to comment.