Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix($compile): don't add replaced attributes twice to $attrs #14737

Closed
wants to merge 4 commits into from

Conversation

Narretz
Copy link
Contributor

@Narretz Narretz commented Jun 8, 2016

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
bugfix

What is the current behavior? (You can also link to an open issue here)
In replace directives, attribute values from the template are added twice to the replaced element when both the replaced element and the template element have the attribute. This does not affect the DOM, as it normalizes duplicate values. The values are however duplicated in the $attrs object that is available to directive link functions.

Does this PR introduce a breaking change?
No

Please check if the PR fulfills these requirements

Other information:
@gkalpak I fixed this while working on another attribute-related PR.

Fixes #8159
Closes #5597

In replace directives, attribute values from the template are added twice to the replaced element when both the replaced element and the template element have the attribute. This does not affect the DOM, as it normalizes duplicate values. The values are however duplicated in the $attrs object that is available to directive link functions.

Fixes angular#8159
Closes angular#14737
@Narretz Narretz force-pushed the fix-attribute-duplicates branch from 05d56de to 724273a Compare June 8, 2016 20:47
})
});

inject(function($compile, $rootScope) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't $compile and $rootScope already available inside the top-level `describe's scope ?
Why is everyone injecting it ? 😛

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried removing it - injection seems necessary

@Narretz
Copy link
Contributor Author

Narretz commented Jun 9, 2016

I just thought about this a bit more, and with this change we now have two different strategies of updating the element attributes for class & style. Before this change, classes and styles from "src" (new element) would always be added with safeAddClass() and $element.attr('style') respectively. Now we only add them with dst.$set() if they are also in dst, but use the other technique when they are only in src.

And there's also the question why dst attrs are using attr.$set, but src attributes are not.

@Narretz
Copy link
Contributor Author

Narretz commented Jun 9, 2016

The whole mergeTemplateAttributes fn is a crazy patchwork of commits.
History:
8af4fde#diff-0242b0d33ee2848e02ae5bf32cc2b645R511
f49eaf8
fb99b53
e1254b2#diff-a732922b631efed1b9f33a24082ae0dbR1591
1ab6e90

@Narretz
Copy link
Contributor Author

Narretz commented Jun 9, 2016

Oh, look, you can replace the whole special-casing for the src attributes!

// You will get an "InvalidCharacterError: DOM Exception 5" error if you
// have an attribute like "has-own-property" or "data-has-own-property", etc.
if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
dst.$set(key, value, true, srcAttr[key]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. This will also do some more validation/sanitizing (e.g. for src attributes etc). I guess it's a good thing, but maybe also a breaking change.

I wasn't able to find where in the previous code were these new dst attributes (the ones copied from src) added to the actual element 😞 - we might duplicating some work (not that it's a big deal).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ... interesting. The attributes are previously added with the replaceWith function, which basically overwrites the dst element with everything from the src element. mergeTemplateAttributes is then really just for merging, not plain adding. So using dst.$set actually does work we already did in replaceWith(), and since replaceWith adds all template attributes that don't need merging, we should be able to remove $element.attr('style', value) and safeAddClass($element, value).

replaceWith() already adds all attributes from the template,
which means if no merging took place, we don't have to set the attributes again
@petebacondarwin
Copy link
Contributor

Holy Cow! Now that is a neat bit of refactoring.
I haven't tested it but since you have only added tests then I guess it is good to go!

petebacondarwin pushed a commit that referenced this pull request Jun 9, 2016
In replace directives, attribute values from the template are added twice
to the replaced element when both the replaced element and the template
element have the attribute. This does not affect the DOM, as it normalizes
duplicate values. The values are however duplicated in the $attrs object
that is available to directive link functions.

Fixes #8159
Closes #14737
dst[key] = value;
dstAttr[key] = srcAttr[key];

if (key !== 'class' && key !== 'style') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a breaking (and unnecessary) change 😃
Why not set dstAttr[key] for class and style ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gkalpak - I don't believe this is a BC. The previous behaviour was not to set distAttr for class and style, so doing so would actually be a BC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, you are right, it didn't. Any idea why?
It seems like an oversight (bug?)...

Anyway, this is consistent, so no harm 😃

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Directive with 'replace: true' and template duplicates classes in the attrs parameter of the link fn
4 participants