-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix problems with skipped _notifyPath forwarding #60
Conversation
An optimization in _notifyPath causes notifications not to be forwarded to imported-template if the value passed to _notifyPath is an object that is the same as the existing model. This is a legitimate case, in which dom-bind "resets" the object in order to inform that one of the properties has changed Solution: always forward the notifications if the model is the same instance. Also fixed a side problem that appeared while fixing this. Starcounter-include forwarded notifications to imported-template before the template was stamped
…s resets shadowRoot in partialChanged, even if not needed Solution: if the instance of defaultComposition is the same as previously used, skip stamping the composition
@@ -246,6 +247,7 @@ | |||
* @param {Mixed} value New value | |||
*/ | |||
StarcounterIncludePrototype._notifyPath = function(path, value) { | |||
var modelAlreadyLoaded = this.template.model == this.partial; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT: you can ignore this comment. After a second thought, both points aren't valid.
- I think there is a chance when
_notifyPath
is called before stamping (this.template
assignment). Maybethis.template && this.template.model == this.partial
would be more tolerant.- What about
null == null
? Maybe eventhis.template && this.template.model && this.template.model == this.partial
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rename it to sameModelAlreadyLoaded
as current name gave me lots of confusion in reasoning mentioned below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a fair point.
importedTemplate.addEventListener('stamping', function onceStamped(){ | ||
importedTemplate.removeEventListener('stamping', onceStamped); | ||
scope = domBind.get('model.Page.scope1'); | ||
scope.doesItWork = newVal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this line 197 needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, this line doesn't add anything meaningful to the test. I will remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look great. I can take care of it from here if you want.
Spotted by Omar in #60 (review) Solution: remove that line.
Sure! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alshakero @warpech
Have we changed the design decisions from #34 (comment)?
I thought we agreed to:
- In case of the change of one sibling, we reload the HTML (
partialChanged
) and DO NOT notify the existing view with new model, until it's loaded (NO sequential_notifyPath
starcounter-include/starcounter-include.html
Lines 283 to 288 in c4ce900
if (modelAlreadyLoaded && this.template._notifyPath) { this.template._notifyPath( path.replace("partial.", "model.") .replace("viewModel.", "model."), value); } modelAlreadyLoaded
check will pass it ), - In case of just nested property change
model.scope1.some.prop
, we DO NOT touch either URL (_htmlChanged
) nor composition (_compositionChanged
).
Means nopartialChanged
instarcounter-include/starcounter-include.html
Lines 283 to 288 in 8725dff
} else if (this.template._notifyPath) { this.template._notifyPath( path.replace("partial.", "model.") .replace("viewModel.", "model."), value); }
Maybe checks in individual _htmlChanged
, _compositionChanged
made tests passing, but running them, in my opinion, is unnecessarily complicating the flow and performance.
} else { | ||
// make clone to avoid direct binding | ||
composition = this.stringToDocumentFragment(compositionString); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was it moved here? Was it causing any problems? Now it's calling composition = this.stringToDocumentFragment(compositionString);
even though there may be no need for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right about this. Actually, I have created a regression that was not caught by tests. I will make another PR.
path.replace("partial.", "model.") | ||
.replace("viewModel.", "model."), | ||
value); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it the regression of #37 (ignoring Palindrom meta-data)?
At least performance-wise even thought no attrs for i-t
are being changed we may still ping _notifyPath
and partialChanged
after each version bump.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it the regression of #37 (ignoring Palindrom meta-data)?
There is a test for that (see the PR https://github.com/Starcounter/starcounter-include/pull/44/files), so I don't think so?
At least performance-wise even thought no attrs for
i-t
are being changed we may still ping_notifyPath
andpartialChanged
after each version bump.
If no attrs for i-t
are being changed than why was s-i._notifyPath
was called in first place? If some performance optimisation should be applied, I believe it should be done outside of s-i
, not inside of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a test for that
The test you mention, tests building URL, not propagating changes to nested templates.
So there is unfortunatelly no test for that.
If no attrs for
i-t
are being changed than why wass-i._notifyPath
was called in first place?
Consider the case:
viewModel = {
_ver#s: 1,
_ver#c$: 1,
App: {}
}
<dom-bind><template>
<starcounter-include view-model="{{viewModel}}"/>
</template></dom-bind>
// palindrom-client translates `{op: 'replace', path: '/_ver#s', value: 2}` to polymer notification
domBind.set('viewModel._ver#s', 2);
// palindrom-client translates `{op: 'replace', path: '/App', value: {}}` to polymer notification
domBind.set('viewModel.App', {});
In both cases no attributes for i-t
are being changed. But starcounter-include
is called for both.
I believe it should be done outside of
s-i
, not inside of it.
Like, where? s-i
is supposed to do all Starcounter specific magic to untangle merged view-models. I consider Palindrom metadata part of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if you mean that it is wrong behavior or performance problem?
The test that I mentioned was checking for wrong behavior. What you suggest seems to be a performance improvement and I agree with it, though I am not sure about the significance of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not just performance improvement.
The case I described is the correct behavior, that is not tested and might be broken by this change.
Why do you think https://github.com/Starcounter/starcounter-include/pull/44/files tests wrong behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you think /pull/44/files tests wrong behavior?
Sorry for choosing wrong words.
The test in https://github.com/Starcounter/starcounter-include/pull/44/files is correct. It tests in the metadata is ignored by the URL builder (which is desired). It does not check if notifications about the metadata are forwarded or skipped (which doesn't matter except for performance).
Why do you think that it matters if we skip forwarding the notifications about the metadata?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. Currently it's only performance, and it's not notified any further due to functionality we lost while upgrading to Polymer 2.0 Juicy/imported-template#43.
scInclude.partial = scInclude.partial; | ||
setTimeout(function() { | ||
expect(old).to.be.not.null; | ||
expect(old).to.be.equal(scInclude.shadowRoot.querySelector("h2")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather compare entire DocumentFragment
to make sure no changes were made to .shadowRoot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean compare old scInclude.shadowRoot
to the current scInclude.shadowRoot
? Wouldn't this test a different thing? In this test I am testing exactly what is needed to make https://github.com/Starcounter/Website/pull/211 work, comparing the root objects would not test that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that way, you are not testing "keep", you are testing "is again/still the markup". I think we should avoid flashing and in case there is a custom element that animates something, for example, we shouldn't break it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, line 89 checks the equality of the DOM node, not just the equality of the node's name.
Perhaps we could check for flashing caused by detaching and attaching the same node, is that what you mean? I guess a way to do that would be to use mutation observer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that one particular node is back there again does not mean it didn't flashed due to:
- detaching and attaching the same node,
- attaching some other nodes,
- removing some other nodes,
- removing entire composition, putting another one, then restoring this back.
Shadow DOM (composition) is a DocumentFragment
and that's why I believe we should treat it as a whole, not just individual elements.
I guess a way to do that would be to use mutation observer.
Great idea. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But would comparing DocumentFragment
would test against flashing? I think we can only test flashing with mutation observer, no?
Anyway, feel free to improve the test. My goal was to add a test for something that failed before. I didn't try to solve anything more than that.
@@ -92,11 +92,12 @@ | |||
done(); | |||
}); | |||
}); | |||
it('should forward Polymer notification to `imported-template`', function () { | |||
|
|||
it('should NOT forward Polymer notification to `imported-template` before it was stamped', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't we taking responsibility of imported-template
here? When I wrote it for the first time, my idea was that sc-include
forwards all data and notifications to imported-template
as soon as it gets it. Not, to block imported-template
from consuming that in the way it needs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this inconsistent:
- the test above (line 96) checks that Polymer notification is forwarded before the template is stamped
- the tests like
describe('should attach to `<imported-template>` concatenated `Html`s as href attribute and property (`/sc/htmlmerger?`+`scope=scope.HTML` for each scope that contains it) and attach partial\'s model only once content is stamped', function () {
For me this is contradicting. The fix that I made 1acb2e9 (with a new test added) caused the test at line 96 to fail, and my conclusion was that the test at line 96 was wrong and should be inverted.
@@ -183,6 +184,26 @@ | |||
}); | |||
}); | |||
}); | |||
|
|||
describe('after `dom-bind` re-set the sub-partial property (just one scope) to the same instance but potentially with new values', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After you removed scope.doesItWork = newVal;
it's no longer "potentially with new values".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test only checks if the notification is forwarded, without really testing any values in it. Perhaps the description should be changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well.. it was also involving testing integration with Polymer's dirty-checking. But Polymer 2.0 does not forward it anyways, so we already change this test to check that it's NOT forwarded
@@ -246,6 +247,7 @@ | |||
* @param {Mixed} value New value | |||
*/ | |||
StarcounterIncludePrototype._notifyPath = function(path, value) { | |||
var modelAlreadyLoaded = this.template.model == this.partial; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rename it to sameModelAlreadyLoaded
as current name gave me lots of confusion in reasoning mentioned below.
…yPath resets an unsaved explicit composition created using CompositionEditor Solution: change the _compositionChanged method to more clearly implement the complex process of selecting which composition to apply. Fix explicit composition caching so it does not get overwritten by fallback composition. Add a test
@atifwaqar reported to me on Slack some problems with partial view replacement. This PR fixes the problem and adds tests for the things that were fixed.
It is explained in more detail in the commit messages.