Skip to content

Commit

Permalink
Merge f7c5c35 into d91db78
Browse files Browse the repository at this point in the history
  • Loading branch information
Benoit Charbonnier committed Oct 1, 2014
2 parents d91db78 + f7c5c35 commit 865e0b8
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 23 deletions.
48 changes: 31 additions & 17 deletions hsp/rt/$root.js
Expand Up @@ -33,8 +33,8 @@ var CPT_TYPES={
var DOCUMENT_FRAGMENT_NODE = 11;

/**
* Root node - created at the root of each template
* Contains the listeners requested by the child nodes
* Root node - created at the root of each template
* Contains the listeners requested by the child nodes
* Is replaced by the $CptNode (child class) when the template is inserted in another template
*/
var $RootNode = klass({
Expand All @@ -51,7 +51,7 @@ var $RootNode = klass({
* @param {Map} ctlInitAtts the init value of the controller attributes (optional) - e.g.
* {value:'123',mandatory:true}
*/
$constructor : function (vscope, nodedefs, argnames, ctlWrapper, ctlInitAtts) {
$constructor : function(vscope, nodedefs, argnames, ctlWrapper, ctlInitAtts) {
if (this.isInsertNode) {
TNode.$constructor.call(this, this.exps);
} else {
Expand Down Expand Up @@ -125,12 +125,16 @@ var $RootNode = klass({
o.$dispose();
}
this.propObs=null;
this.$disposeCtlWrapper();
TNode.$dispose.call(this);
},

$disposeCtlWrapper: function() {
if (this.ctlWrapper) {
this.ctlWrapper.$dispose();
this.ctlWrapper = null;
this.controller = null;
}
TNode.$dispose.call(this);
},

/**
Expand Down Expand Up @@ -318,6 +322,7 @@ var getObject = exports.getObject = function (path, scope) {
o = o[path[i]];
}
}

return o;
};

Expand All @@ -338,7 +343,7 @@ var $CptNode = klass({
* expression index associated to the event hanlder callback
* @param {Array} children list of child node generators - correponding to pseudo components and attribute content
*/
$constructor : function (tplPath, exps, attcfg, ehcfg, children) {
$constructor : function(tplPath, exps, attcfg, ehcfg, children) {
this.pathInfo=tplPath.slice(1).join("."); // debugging info
this.info="[Component: #"+this.pathInfo+"]"; // debug info
this.isCptNode = true;
Expand Down Expand Up @@ -411,7 +416,7 @@ var $CptNode = klass({
ni.node2=node2;

if (p.cptType==="$CptAttInsert") {
// this cpt is used to an insert another component passed as attribute
// this cpt is used to an insert another component passed as attribute
ni.initCpt(po);
} else {
// we are in a template or component cpt
Expand All @@ -425,20 +430,20 @@ var $CptNode = klass({
// create an element to avoid generating other errors
ni=this.createCptInstance("$CptAttInsert",parent);
}

return ni;
},

/**
* Calculates the object referenced by the path and the component type
* @return {Object} object with the following properties:
* pathObject: {Object} the object referenced by the path
* cptType: {String} one of the following option: "$CptComponent",
* cptType: {String} one of the following option: "$CptComponent",
* "$CptTemplate", "$CptAttInsert" or "InvalidComponent"
*/
getPathData:function(path, vscope) {
// determine the type of this component:
// - either a template - e.g. <#mytemplate foo="bar"/>
// determine the type of this component:
// - either a template - e.g. <#mytemplate foo="bar"/>
// -> instance will extend $CptTemplate
// - a component with controller - e.g. <#mycpt foo="bar"/>
// -> instance will extend $CptComponent
Expand Down Expand Up @@ -513,7 +518,7 @@ var $CptNode = klass({
nd.appendChild(this.node2);
}
},

/**
* Callback called when a controller attribute or a template attribute has changed
*/
Expand Down Expand Up @@ -599,12 +604,22 @@ var $CptNode = klass({

if (tplChanged) {
// check if component nature changed from template to component or opposite

// Change might also be a different component ref, ie cpt1 -> cpt2
// let's dispose anything related to previous template
if (this.template.$dispose) {
this.template.$dispose(true);
//this.controller.$dispose();
} else {
// this.template might be only a reference to template closure function, not what it returns
this.$disposeCtlWrapper();
}
this.template=tpl;
this.createChildNodeInstances();
} else {
if (this.refreshAttributes) {
this.refreshAttributes();
// for component and sub-templates the original vscope is substituted
// for component and sub-templates the original vscope is substituted
// to the one of the component- or sub-template
// so we need to revert to the parent scope to observe the correct objects
var vs=this.vscope;
Expand Down Expand Up @@ -668,7 +683,7 @@ var $CptNode = klass({
var sz=pos.length;

this._pathChgeCb = this.onPathChange.bind(this);

for (var i=0;sz>i;i++) {
json.observe(pos[i], this._pathChgeCb);
}
Expand Down Expand Up @@ -747,9 +762,9 @@ var $CptAttElement = klass({
isCptAttElement : true,

/**
* $CptAttElement generator
* $CptAttElement generator
*/
$constructor : function (name, exps, attcfg, ehcfg, children) {
$constructor : function(name, exps, attcfg, ehcfg, children) {
this.name = name;
this.info = "[Component attribute element: @"+this.name+"]";
this.tagName = "@"+name;
Expand Down Expand Up @@ -789,7 +804,7 @@ var $CptAttElement = klass({
if (p.ctlAttributes) {
attDef=p.ctlAttributes[this.name];
}

if (!eltDef && !attDef) {
// invalid elt
log.error(this.info+" Element not supported by its parent component");
Expand Down Expand Up @@ -846,4 +861,3 @@ cptComponent.setDependency("$CptAttElement",$CptAttElement);
exports.$RootNode = $RootNode;
exports.$CptNode = $CptNode;
exports.$CptAttElement = $CptAttElement;

8 changes: 2 additions & 6 deletions hsp/rt/cptcomponent.js
Expand Up @@ -138,11 +138,7 @@ exports.$CptComponent = {
* must be used when a new instance is created to adapt to a path change
*/
$dispose:function(localPropOnly) {
if (this.ctlWrapper) {
this.ctlWrapper.$dispose();
this.ctlWrapper=null;
this.controller=null;
}
this.$disposeCtlWrapper();
this.ctlAttributes=null;
this.cleanObjectProperties(localPropOnly);
this.ctlConstuctor=null;
Expand Down Expand Up @@ -219,7 +215,7 @@ exports.$CptComponent = {
// nm is a template attribute passed as text attribute
if (this.tplAttributes && this.tplAttributes[nm]) {
// already defined: raise an error

log.error(this+" Component attribute '" + nm + "' is defined multiple times - please check");
} else {
// create new tpl Attribute Text Node and add it to the tplAttributes collection
Expand Down
99 changes: 99 additions & 0 deletions test/rt/cptlifecyle.spec.hsp
@@ -0,0 +1,99 @@
<script>
/*
* Copyright 2013 Amadeus s.a.s.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var klass=require("hsp/klass"),
ht=require("hsp/utils/hashtester");

var constructor_counter = 0;
var dispose_counter = 0;

var LifeCycleController = klass({
$constructor: function() {
constructor_counter++;
},
$dispose: function() {
dispose_counter++;
}
});
</script>

<template lifecycle1 using ctrl:LifeCycleController>
<div class="content">I am component 1</div>
</template>

<template lifecycle2 using ctrl:LifeCycleController>
<div class="content">I am component 2</div>
</template>

<template wrapper(data)>
{if data.visible}
<#lifecycle1 />
{/if}
</template>

<template dynamicWrapper(data)>
<#data.cpt />
</template>

<script>
var data = {
visible: true,
cpt: lifecycle1
};
</script>

<script>
describe("Component lifecycle", function () {

it("should invoke $contructor and $dispose anytime a new instance is needed", function() {
var h=ht.newTestContext();
constructor_counter = dispose_counter = 0;

wrapper(data).render(h.container);
expect(h(".content").text()).to.equal('I am component 1');
expect(constructor_counter).to.equal(1);
h.$set(data, "visible", false);
h.$set(data, "visible", true);
expect(constructor_counter).to.equal(2);

expect(dispose_counter).to.equal(1);

h.$dispose();
});
});

describe("Component used as dynamic <#ref >", function() {

it("should invoke $constructor at instance creation and $dispose when not needed anymore", function() {
var h=ht.newTestContext();
constructor_counter = dispose_counter = 0;

dynamicWrapper(data).render(h.container);
expect(h(".content").text()).to.equal('I am component 1');
expect(constructor_counter).to.equal(1);

h.$set(data, "cpt", lifecycle2);
expect(h(".content").text()).to.equal('I am component 2');
expect(constructor_counter).to.equal(2);

h.$set(data, "cpt", lifecycle1);
expect(dispose_counter).to.equal(2);

h.$dispose();
});

});
</script>

0 comments on commit 865e0b8

Please sign in to comment.