diff --git a/packages/@glimmer/integration-tests/test/attributes-test.ts b/packages/@glimmer/integration-tests/test/attributes-test.ts
index 20408169aa..279ae2d081 100644
--- a/packages/@glimmer/integration-tests/test/attributes-test.ts
+++ b/packages/@glimmer/integration-tests/test/attributes-test.ts
@@ -1,6 +1,7 @@
import { normalizeProperty } from '@glimmer/runtime';
-import { assertElement, hasAttribute, jitSuite, RenderTest, test } from '..';
+import { assertElement, hasAttribute, jitSuite, RenderTest, test, tracked } from '..';
import { Namespace, SimpleElement } from '@simple-dom/interface';
+import { castToBrowser, expect } from '@glimmer/util';
export class AttributesTests extends RenderTest {
static suiteName = 'Attributes';
@@ -239,6 +240,40 @@ export class AttributesTests extends RenderTest {
this.assertStableNodes();
}
+ @test
+ 'handles successive updates to the same value'() {
+ class Model {
+ @tracked value = '';
+ }
+
+ let model = new Model();
+
+ this.render('', { model });
+ this.assert.equal(this.readDOMAttr('value'), '');
+ this.assertStableRerender();
+
+ let inputElement = castToBrowser(
+ expect(this.element.firstChild, 'expected input to exist'),
+ 'input'
+ );
+
+ inputElement.value = 'bar';
+ this.assert.equal(this.readDOMAttr('value'), 'bar');
+
+ model.value = 'foo';
+ this.rerender();
+ this.assert.equal(this.readDOMAttr('value'), 'foo');
+ this.assertStableNodes();
+
+ inputElement.value = 'bar';
+ this.assert.equal(this.readDOMAttr('value'), 'bar');
+
+ model.value = 'foo';
+ this.rerender();
+ this.assert.equal(this.readDOMAttr('value'), 'foo');
+ this.assertStableNodes();
+ }
+
@test
'input[checked] prop updates when set to undefined'() {
this.registerHelper('if', (params) => {
diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts
index 48c2205afb..4c9bb5c9c5 100644
--- a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts
+++ b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts
@@ -544,7 +544,7 @@ function setDeferredAttr(
.elements()
.setDynamicAttribute(name, valueForRef(value), trusting, namespace);
if (!isConstRef(value)) {
- vm.updateWith(new UpdateDynamicAttributeOpcode(value, attribute));
+ vm.updateWith(new UpdateDynamicAttributeOpcode(value, attribute, vm.env));
}
}
}
diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
index 73f879f2fd..4b6b9fe896 100644
--- a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
+++ b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts
@@ -1,4 +1,4 @@
-import { Reference, valueForRef, isConstRef } from '@glimmer/reference';
+import { Reference, valueForRef, isConstRef, createComputeRef } from '@glimmer/reference';
import { Revision, Tag, valueForTag, validateTag, consumeTag } from '@glimmer/validator';
import {
check,
@@ -17,6 +17,7 @@ import {
Owner,
CurriedType,
ModifierDefinitionState,
+ Environment,
} from '@glimmer/interfaces';
import { $t0 } from '@glimmer/vm';
import { APPEND_OPCODES, UpdatingOpcode } from '../../opcodes';
@@ -237,27 +238,34 @@ APPEND_OPCODES.add(Op.DynamicAttr, (vm, { op1: _name, op2: _trusting, op3: _name
let attribute = vm.elements().setDynamicAttribute(name, value, trusting, namespace);
if (!isConstRef(reference)) {
- vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute));
+ vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute, vm.env));
}
});
export class UpdateDynamicAttributeOpcode extends UpdatingOpcode {
public type = 'patch-element';
- public lastValue: unknown;
+ private updateRef: Reference;
- constructor(private reference: Reference, private attribute: DynamicAttribute) {
+ constructor(reference: Reference, attribute: DynamicAttribute, env: Environment) {
super();
- this.lastValue = valueForRef(reference);
- }
- evaluate(vm: UpdatingVM) {
- let { attribute, reference, lastValue } = this;
- let currentValue = valueForRef(reference);
+ let initialized = false;
- if (currentValue !== lastValue) {
- attribute.update(currentValue, vm.env);
- this.lastValue = currentValue;
- }
+ this.updateRef = createComputeRef(() => {
+ let value = valueForRef(reference);
+
+ if (initialized === true) {
+ attribute.update(value, env);
+ } else {
+ initialized = true;
+ }
+ });
+
+ valueForRef(this.updateRef);
+ }
+
+ evaluate() {
+ valueForRef(this.updateRef);
}
}