Skip to content

Commit

Permalink
Add forceUpdate method to Component
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert-Frampton authored and Robert-Frampton committed Oct 6, 2017
1 parent 8fe3762 commit 776160d
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 1 deletion.
25 changes: 25 additions & 0 deletions packages/metal-component/src/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class Component extends EventEmitter {
*/
this.eventsStateKeyHandler_ = null;

/**
* Whether the element is currently force updating.
* @type {boolean}
*/
this.forceUpdating_ = false;

/**
* Whether the element is in document.
* @type {boolean}
Expand Down Expand Up @@ -275,6 +281,16 @@ class Component extends EventEmitter {
super.disposeInternal();
}

/**
* Forces a an update that ignores `shouldUpdate` for components whose
* render depends on external variables.
*/
forceUpdate() {
this.forceUpdating_ = true;

this.updateRenderer_();
}

/**
* Gets data about where this component was attached at.
* @return {!Object}
Expand Down Expand Up @@ -385,6 +401,7 @@ class Component extends EventEmitter {
*/
informRendered() {
const firstRender = !this.hasRendererRendered_;
this.forceUpdating_ = false;
this.hasRendererRendered_ = true;
this.rendered(firstRender);
this.emit('rendered', firstRender);
Expand All @@ -399,6 +416,14 @@ class Component extends EventEmitter {
return fn.prototype && fn.prototype[Component.COMPONENT_FLAG];
}

/**
* Returns true if component is currently force updating.
* @return {boolean}
*/
isForceUpdating() {
return this.forceUpdating_;
}

/**
* Merges two values for the ELEMENT_CLASSES property into a single one.
* @param {string} class1
Expand Down
15 changes: 15 additions & 0 deletions packages/metal-component/test/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,21 @@ describe('Component', function() {
assert.strictEqual(0, renderer.update.callCount);
});
});

it('should flag component as force updating only when "forceUpdate" has been called', function() {
var CustomComponent = createCustomComponentClass();
comp = new CustomComponent();

assert.isFalse(comp.isForceUpdating());

comp.forceUpdate();

assert.isTrue(comp.isForceUpdating());

comp.informRendered();

assert.isFalse(comp.isForceUpdating());
});
});

describe('Events', function() {
Expand Down
4 changes: 3 additions & 1 deletion packages/metal-incremental-dom/src/IncrementalDomRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ class IncrementalDomRenderer extends ComponentRenderer.constructor {
* @param {!Component} component
*/
update(component) {
if (this.shouldUpdate(component, getChanges(component))) {
if (component.isForceUpdating() ||
this.shouldUpdate(component, getChanges(component))) {

this.patch(component);
}
}
Expand Down
44 changes: 44 additions & 0 deletions packages/metal-incremental-dom/test/IncrementalDomRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,50 @@ describe('IncrementalDomRenderer', function() {
assert.strictEqual('foo', component.element.textContent);
});

it('should rerender when component\'s "forceUpdate" method has been called', function() {
let globalVar = 'foo';
class TestComponent extends Component {
render() {
IncDom.elementOpen('div');
IncDom.text(globalVar);
IncDom.elementClose('div');
}
}
TestComponent.RENDERER = IncrementalDomRenderer;

component = new TestComponent();

assert.equal(component.element.innerHTML, 'foo');

globalVar = 'bar';

component.forceUpdate();

assert.equal(component.element.innerHTML, 'bar');
});

it('should ignore component\'s "shouldUpdate" method when "forceUpdate" method has been called', function() {
let globalVar = 'foo';
class TestComponent extends Component {
render() {
IncDom.elementOpen('div');
IncDom.text(globalVar);
IncDom.elementClose('div');
}
}
TestComponent.RENDERER = IncrementalDomRenderer;

component = new TestComponent();
globalVar = 'bar';

component.shouldUpdate = sinon.stub();

component.forceUpdate();

assert.equal(component.element.innerHTML, 'bar');
assert.equal(component.shouldUpdate.callCount, 0);
});

it('should run component\'s "rendered" lifecycle method on updates', function(done) {
var calledArgs = [];
class TestComponent extends Component {
Expand Down

0 comments on commit 776160d

Please sign in to comment.