Skip to content

Commit

Permalink
Behaviors
Browse files Browse the repository at this point in the history
* properly de-dup behaviors from superclasses
* apply only "own" behaviors to class
  • Loading branch information
Steven Orvell committed Oct 31, 2018
1 parent 20c8bf1 commit efb8d71
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 47 deletions.
73 changes: 32 additions & 41 deletions lib/legacy/class.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,34 +62,7 @@
* @suppress {invalidCasts, checkTypes}
*/
function mixinBehaviors(behaviors, klass) {
if (behaviors) {
klass = applyBehaviors(behaviors, klass);
}
// provides behaviors functionality
return GenerateClassFromInfo({}, klass);
}

function applyBehaviors(behaviors, klass) {
if (!behaviors) {
klass = /** @type {HTMLElement} */(klass); // eslint-disable-line no-self-assign
return klass;
}
// NOTE: ensure the behavior is extending a class with
// legacy element api. This is necessary since behaviors expect to be able
// to access 1.x legacy api.
klass = class extends Polymer.LegacyElementMixin(klass) { };
if (!Array.isArray(behaviors)) {
behaviors = [behaviors];
}
let superBehaviors = klass.prototype.behaviors;
// get flattened, deduped list of behaviors *not* already on super class
behaviors = flattenBehaviors(behaviors, null, superBehaviors);
if (superBehaviors) {
behaviors = superBehaviors.concat(behaviors);
}
// Set behaviors on prototype
klass.prototype.behaviors = behaviors;
return klass;
return GenerateClassFromInfo({}, Polymer.LegacyElementMixin(klass), behaviors);
}

// NOTE:
Expand Down Expand Up @@ -124,20 +97,21 @@
// (again same as 1.x)
function copyBehaviorProperties(behaviors, klass) {
const meta = {};
const superMeta = klass.prototype.__behaviorMetaProps;
if (behaviors) {
klass.prototype.__behaviorMetaProps = meta;
for (let i=0; i<behaviors.length; i++) {
copyProperties(behaviors[i], klass.prototype);
memoizeBehaviorMetaProps(meta, behaviors[i]);
memoizeBehaviorMetaProps(meta, behaviors[i], superMeta);
}
}
klass.prototype.__behaviorMetaProps = meta;
}

function memoizeBehaviorMetaProps(meta, behavior) {
function memoizeBehaviorMetaProps(meta, behavior, superMeta) {
for (let p in memoizedProps) {
if (behavior[p]) {
meta[p] = meta[p] || [];
meta[p] = meta[p] || (superMeta && superMeta[p] ? superMeta[p].slice() : []);
meta[p].push(behavior[p]);
}
}
Expand Down Expand Up @@ -222,7 +196,7 @@
* @suppress {checkTypes}
* @private
*/
function GenerateClassFromInfo(info, Base) {
function GenerateClassFromInfo(info, Base, behaviors) {

/** @private */
class PolymerGenerated extends Base {
Expand Down Expand Up @@ -289,6 +263,7 @@
/**
* @return {void}
*/
// Called on element prototype
_registered() {
/* NOTE: `beforeRegister` is called here for bc, but the behavior
is different than in 1.x. In 1.0, the method was called *after*
Expand All @@ -297,17 +272,20 @@
in `beforeRegister` or `registered`. It is no longer possible to set
`is` in `beforeRegister` as you could in 1.x.
*/
const proto = Object.getPrototypeOf(this);
copyBehaviorProperties(proto.behaviors, proto.constructor);
const proto = this;
if (proto.hasOwnProperty('behaviors')) {
copyBehaviorProperties(proto.behaviors, proto.constructor);
}
proto.__behaviorMetaProps = proto.__behaviorMetaProps || {};
copyProperties(info, proto);
// Note, previously these were interleaved.
let list = this.__behaviorMetaProps.beforeRegister;
let list = proto.__behaviorMetaProps.beforeRegister;
if (list) {
for (let i=0; i < list.length; i++) {
list[i].call(proto);
}
}
list = this.__behaviorMetaProps.registered;
list = proto.__behaviorMetaProps.registered;
if (list) {
for (let i=0; i < list.length; i++) {
list[i].call(proto);
Expand Down Expand Up @@ -431,7 +409,23 @@
if (info.attributeChanged) {
info.attributeChanged.call(this, name, old, value);
}
}
}
}

// apply behaviors
if (behaviors) {
// NOTE: ensure the behavior is extending a class with
// legacy element api. This is necessary since behaviors expect to be able
// to access 1.x legacy api.
if (!Array.isArray(behaviors)) {
behaviors = [behaviors];
}
let superBehaviors = PolymerGenerated.prototype.__allBehaviors;
// get flattened, deduped list of behaviors *not* already on super class
behaviors = flattenBehaviors(behaviors, null, superBehaviors);
PolymerGenerated.prototype.__allBehaviors = superBehaviors ?
superBehaviors.concat(behaviors) : behaviors;
PolymerGenerated.prototype.behaviors = behaviors;
}

PolymerGenerated.generatedFrom = info;
Expand Down Expand Up @@ -513,10 +507,7 @@
}
let klass = mixin ? mixin(Polymer.LegacyElementMixin(HTMLElement)) :
Polymer.LegacyElementMixin(HTMLElement);
if (info.behaviors) {
klass = applyBehaviors(info.behaviors, klass);
}
klass = GenerateClassFromInfo(info, klass);
klass = GenerateClassFromInfo(info, klass, info.behaviors);
// decorate klass with registration info
klass.is = info.is;
return klass;
Expand Down
17 changes: 11 additions & 6 deletions test/unit/mixin-behaviors.html
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,10 @@
<script>
HTMLImports.whenReady(function() {
customElements.define('nested-behaviors',
class extends Polymer.mixinBehaviors(
class extends Polymer.mixinBehaviors([window.BehaviorD, window.LifeCycleBehavior1], Polymer.mixinBehaviors(
[
[window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA],
[window.BehaviorD]
], Polymer.Element) {
[window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA, window.LifeCycleBehavior2],
], Polymer.Element)) {
});
});
</script>
Expand Down Expand Up @@ -356,7 +355,7 @@

<test-fixture id="nested">
<template>
<nested-behaviors></nested-behaviors>
<nested-behaviors foo="foo"></nested-behaviors>
</template>
</test-fixture>

Expand Down Expand Up @@ -552,7 +551,13 @@
});

test('nested-behavior dedups', function() {
assert.equal(el.behaviors.length, 4);
assert.equal(el.behaviors.length, 2);
});

test('nested-behavior lifecycle', function() {
assert.equal(el._calledCreated, 2, 'created call count wrong');
assert.equal(el._calledAttached, 2, 'attached call count wrong');
assert.equal(el._calledAttributeChanged, 1, 'attributeChanged call count wrong');
});

test('nested-behavior overrides ordering', function() {
Expand Down

0 comments on commit efb8d71

Please sign in to comment.