Skip to content
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

feat(ivy): add support for the ngProjectAs attribute #22498

Conversation

pkozlowski-opensource
Copy link
Member

This PR adds support for the ngProjectAs attribute.

The implementation in this PR requires us to store a CSS selector (from the <ng-content>'s select attribute) in both parsed and raw (un-parsed) form. The raw form is needed so we can easily compare it with a value of the ngProjectAs attribute.

@chuckjaz this PR changes the compiler's canonical spec for content projection, so the compiler's implementation will have to be updated accordingly.

@pkozlowski-opensource pkozlowski-opensource added target: major This PR is targeted for the next major release comp: ivy action: review The PR is still awaiting reviews from at least one requested reviewer labels Feb 28, 2018
@@ -539,8 +539,10 @@ function setUpAttributes(native: RElement, attrs: string[]): void {

const isProc = isProceduralRenderer(renderer);
for (let i = 0; i < attrs.length; i += 2) {
isProc ? (renderer as ProceduralRenderer3).setAttribute(native, attrs[i], attrs[i | 1]) :
native.setAttribute(attrs[i], attrs[i | 1]);
if (attrs[i] !== NG_PROJECT_AS_ATTR_NAME) {
Copy link
Contributor

Choose a reason for hiding this comment

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

what about

const attrName = a[i];
const attrValue = a[i+1];
// ... use attrName & attrValue

Copy link
Member Author

Choose a reason for hiding this comment

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

👍 done

@@ -1262,7 +1266,7 @@ export function projectionDef(index: number, selectors?: CssSelector[]): void {
// - there are selectors defined
// - a node has a tag name / attributes that can be matched
if (selectors && componentChild.tNode) {
const matchedIdx = matchingSelectorIndex(componentChild.tNode, selectors !);
const matchedIdx = matchingSelectorIndex(componentChild.tNode, selectors !, rawSelectors !);
Copy link
Contributor

Choose a reason for hiding this comment

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

You should be able to remove the ! from selectors ! (b/c if (selectors))

Copy link
Member Author

Choose a reason for hiding this comment

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

👍 done

@@ -115,13 +115,30 @@ export function isNodeMatchingSelector(tNode: TNode, selector: CssSelector): boo
return false;
}

function isNodeMatchingProjectAs(tNode: TNode, rawSelector: string): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

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

comment ?

Copy link
Member Author

Choose a reason for hiding this comment

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

This function went away with the recent code changes.

const nodeAttrs = tNode.attrs;
if (nodeAttrs != null) {
const ngProjectAsAttrIdx = nodeAttrs.indexOf(NG_PROJECT_AS_ATTR_NAME);
if (ngProjectAsAttrIdx > -1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if (ngProjectAsAttrIdx & 1 === 0) -> add a test

Copy link
Contributor

Choose a reason for hiding this comment

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

Should NG_PROJECT_AS_ATTR_NAME always come first and then we don't have to iterate ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Should NG_PROJECT_AS_ATTR_NAME always come first and then we don't have to iterate ?

It would be a possible runtime optimisation at the expense of more work in the compiler. I will let @chuckjaz to decide.

Copy link
Member Author

Choose a reason for hiding this comment

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

if (ngProjectAsAttrIdx & 1 === 0) -> add a test

This function went away with the recent code changes.

for (let i = 0; i < selectors.length; i++) {
if (isNodeMatchingSelector(tNode, selectors[i])) {
if (isNodeMatchingProjectAs(tNode, rawSelectors[i]) ||
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this logic right ?
I think that when a node is ngProjectedAs we should not isNodeMatchingSelector

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, good catch! You are right, we should probably not match selectors if ngProjectAs is not matching. Changed the logic and added a test. Thnx!

@@ -1245,9 +1247,11 @@ export function directiveRefresh<T>(directiveIndex: number, elementIndex: number
* each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is
* backed by a selector).
*
* @param selectors
* @param selectors A collection of parsed CSS selectors
* @param rawSelectors A collection of CSS selectors in the raw, un-parsed form
Copy link
Contributor

Choose a reason for hiding this comment

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

I think rawSelectors needs more documentation. Why is it needed, What is the format., how to use it. Also why do we have two different formats? Can it be unified?

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe textSelectors ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Renamed to textSelectors

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@chuckjaz chuckjaz left a comment

Choose a reason for hiding this comment

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

The changes to the compiler spec look good.

Copy link
Contributor

@vicb vicb left a comment

Choose a reason for hiding this comment

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

See inline comments

@pkozlowski-opensource
Copy link
Member Author

@mhevery @vicb all the comments addressed, please have another look.

@mhevery mhevery removed the action: review The PR is still awaiting reviews from at least one requested reviewer label Mar 1, 2018
const nodeAttrs = tNode.attrs;
if (nodeAttrs != null) {
const ngProjectAsAttrIdx = nodeAttrs.indexOf(NG_PROJECT_AS_ATTR_NAME);
if (ngProjectAsAttrIdx > -1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is still wrong see my previous comment.
Also please create a github issue to move the ngProjectAs attr first.

// if a node has the ngProjectAs attribute match it against unparsed selector
// match a node against a parsed selector only if ngProjectAs attribute is not present
if (ngProjectAsAttrVal === textSelectors[i] ||
ngProjectAsAttrVal === null && isNodeMatchingSelector(tNode, selectors[i])) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Wouldn't ngProjectAsAttrVal && ngProjectAsAttrVal === textSelectors[i] || isNodeMatchingSelector(tNode, selectors[i]) make more sense ? (with the same behavior)

Copy link
Contributor

Choose a reason for hiding this comment

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

that's actually wrong !
current impl is fine.

@@ -183,4 +184,26 @@ describe('css selector matching', () => {
});
});

describe('getProjectAsAttrValue', function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit describe('reading the ngProjectAs attribute value' => ...
b/c function names change.

Copy link
Contributor

@vicb vicb left a comment

Choose a reason for hiding this comment

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

1 nit on the test name

@pkozlowski-opensource pkozlowski-opensource added the action: merge The PR is ready for merge by the caretaker label Mar 1, 2018
@JoostK
Copy link
Member

JoostK commented Mar 1, 2018

Would it be possible to let the template compiler recognize the ngProjectAs attribute and have it perform the CSS compilation that is then stored on TNode, such that we may compare by compiled selector instead? That would save the need to have a duplicate representation at runtime and would also normalize for whitespace differences etc.

@pkozlowski-opensource
Copy link
Member Author

@JoostK it would be certainly possible but would have drawbacks:

  • we would have to allocate a field for ngProjectAs on every TNode and this would be wasteful (memory-wise), especially that ngProjectAs is super-rare
  • comparing compiled version of CSS selectors would be much more expensive that just comparing strings.

I'm sure that there are ways of improving solution from this PR but I've considered what you are suggesting and decided against it for the reasons outlined above. But definitively, feel free to send follow-up PR(s) when this one lands, if you've got ideas for a better solution!

@pkozlowski-opensource pkozlowski-opensource force-pushed the ivy_ng_project_as branch 2 times, most recently from 6cd602f to 6f5e7a5 Compare March 5, 2018 09:38
@alexeagle alexeagle closed this in 2c75acc Mar 6, 2018
leo6104 pushed a commit to leo6104/angular that referenced this pull request Mar 25, 2018
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes target: major This PR is targeted for the next major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants