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

Transclude type multi-element, use this for ng-repeat #2531

Closed
wants to merge 9 commits into from

Conversation

lgalfaso
Copy link
Contributor

The patch adds a new transclude type multi-element to be able to handle transclusions that include more elements that the one with the directive. The transclusion type multi-element works in the following way.

When a directive is defined of the type multi-element, then Angular looks for matching attributes named "-start" and -end" and transcludes the entire block. If element where the directive is present does not contain the attribute "-start" then the transclusion works just as the transclusion type "element"

E.g

The directive ng-repeat is of type multi-element when compiling the template below, the compiler will look for the attribute ng-repeat-start. Given that this is present, it will look for a matching hg-repeat-end attribute. When found the entire block will be used for the linking function

<ul>
  <li ng-repeat-start ng-repeat="item in items">{{item.name}};</li>
  <li>{{item.number}};</li>
  <li ng-repeat-end>{{item.color}};</li>
</ul>

feat(ng-repeat): Make ng-repeat of transclude type multi-element

Add a new transclude type multi-element to be able to handle
transclusion that includes more elements than the one where
the directive is present
Make the changes to ngRepeat so it is of transclude type multi-element

Closes angular#1891
Remove a tab from the code
Fix an issue with nested multi-element directives that are siblings
at the DOM level
@IgorMinar
Copy link
Contributor

I skimmed through this PR and it looks really good! Some feedback:

keep up the good work!

@@ -418,9 +419,12 @@ function $CompileProvider($provide) {
* rootElement must be set the jqLite collection of the compile root. This is
* needed so that the jqLite collection items can be replaced with widgets.
* @param {number=} max directive priority
* @param {boolean=} if the max priority should only apply to the first element in the list.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo:

@param {boolean=} if the max ...

should be:

@param {boolean=} limitMaxPriorityToFirstElement if the max ...

Also, I think that it would be useful to explain why this parameter has been added.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will make the change to @param but I am not sure about adding the full explanation on why this new parameter was added. Just for completeness, it would be something like this

When extracting a multi-element block, we need to remove the block and compile it to create the linking function for the entire block. Now, before the patch, then calling this compile function, sometimes we call it with one element and a maxPriotity (this was for transclusion = 'element') or with multiple elements and no maxPriotity. With multi-element transclusion, we need to handle another case, this is to get multiple elements but there should be a limit on the priority but just to the first element.

Note: In a strict sense it should be possible to remove this parameter and make the assumption that the maxPriority only applies to the first element when nodeList has multiple elements, but I think it makes clearer that, even when there is some redundancy

@petebacondarwin
Copy link
Contributor

I am a bit worried about the animation stuff. I think the ng-repeat directive as it stands doesn't take into account that the template now may contain multiple elements, which all need to be animated.

lgalfaso added 2 commits May 4, 2013 17:32
Avoid cloning elements during a multi-element transclude
Put only one comment that will be sent to the linking function and
remove all the placeholde elements
Cleanup the containsAttr method with an implementation from @petebacondarwin
Fixes on the unit tests to follow angular#2492
Document the transclude type 'multi-element' at the docs
Fix a typo on the method compileNodes
Move the important unit tests from ngRepeatStartSpec to ngRepeatSpec
Add more tests at compile for the new multi-element transclude
@lgalfaso
Copy link
Contributor Author

lgalfaso commented May 4, 2013

Merge from trunk to get the modification on ngRepat for #2492
Implement the changes as requested from @IgorMinar and @petebacondarwin
Document the transclude type 'multi-element'

@mhevery
Copy link
Contributor

mhevery commented May 6, 2013

I think as it stands now it would allow weird things like this:

<div a a-start></div>
<div b b-start></div>
<div a-end></div>
</div b-end></div>

This of course is non-sense. I think the grouping should not be associated with any one directive so what we really want is this

<div ng-repeat ng-with-next></div>
<div ng-with-next></div>
<div></div>

Also the grouping should make it clear that any directives define on the ng-with-next can only be accessed after transclusion. Here the ng-with-next is pseudo directive for the compiler not a real directives.

Also I don't think that directives should allow for special multi-transclude. There is only one transclude, in some cases it just happens to have multiple elements. So it is not something which needs to be specified explicitly.

What do you guys think of this?

@lgalfaso
Copy link
Contributor Author

lgalfaso commented May 6, 2013

@mhevery It should not allow

<div a a-start></div>
<div b b-start></div>
<div a-end></div>
<div b-end></div>

as the compilation process will do the following:

  • detect the directive a and the attribute a-start
  • extract the block of elements from a-start to a-end
<div a a-start></div>
<div b b-start></div>
<div a-end></div>
  • put these elements into a new <div></div>
  • recurse a compile call into the content of this div
  • when performing the recursion and processing b with b-start, it will not find a b-end and throw an exception

The reason on why a new transclude type multi-element makes it sure that the directive is able to handle the fact that the linking function can add more than one element. In most cases, this is just a few changes (as it was the case for ng-repeat), but without this, it can break a lot of existing directives that are not ready for this

Fix for the extended `remove` function that is used during
`multi-element` transclusion to also remove the element from its
parent when there is one
@petebacondarwin
Copy link
Contributor

@mhevery - the main issue that I see with ng-with-next is that it doesn't support nesting of ng-repeat. Also, I find it less intuitive and a bit verbose if you have more than two elements to transclude. I generally agree with @lgalfaso's comments.

@petebacondarwin
Copy link
Contributor

@mhevery - it would be beautiful if we could make it so that any directive that transcludes can support multiple top level nodes in it transclusion (something like ng-transclude-start, ng-transclude-end) but as @lgalfaso points out this would require lots of directives to change the way that they handle transclusion - i.e. they would need to know that they need to move or remove multiple elements.

@lgalfaso
Copy link
Contributor Author

lgalfaso commented May 8, 2013

Even when I think that the new transclude type multi-element would be a fine addition, it looks like it will not solve all the issues with ng-repeat
Here are two examples (that have the same limitation).

Keep in mind that the templates are not valid, but are just examples of issues

Nested repeats that start at the same element

<dl>
  <dt ng-repeat="group in groups" ng-repeat-start ng-repeat="item in group">{{item}}</dt>
  <dd ng-repeat-end>{{group}}</dd>
</dl>

Nested repeats that end at the same element

<dl>
  <dt ng-repeat="group in groups" ng-repeat-start>{{group}}</dt>
  <dd ng-repeat="item in group" ng-repeat-start>{{item.id}}</dd>
  <dd ng-repeat-end ng-repeat-end>{{item.name}}</dd>
</dl>

Now, in a strict sense, I think this is a limitation on ng-repeat (and for sure, something that should not be part of this PR)

If this PR moves forward, will look into how to solve these issues

@petebacondarwin
Copy link
Contributor

@lgalfaso - multiple ng-repeats on a single element are not very reliable even now, so I wouldn't worry about this.
http://plnkr.co/edit/ciKUFzlydMwUWoCOXKnM?p=preview

@petebacondarwin
Copy link
Contributor

  • Contributor signed CLA now or in the past (if you just signed, leave a comment here with your real name)
  • Feature is focused on a core value of the framework
  • PR is approved by 2+ senior team members and no committer is blocking the change
  • API is compatible with existing Angular apis and relevant standards (if applicable)
  • No breaking change
  • PR contains unit tests
  • PR contains e2e tests ( is this change big enough to warrant e2e tests )
  • PR contains documentation update
  • PR passes all tests on Travis (sanity)
  • PR passes all tests on ci.angularjs.org (cross-browser compatibility)
  • PR is rebased against recent master
  • PR is squashed into one commit per logical change
  • PR's commit messages are descriptive and allows us to autogenerate release notes (required commit message format)
  • All changes requested in review have been implemented

@petebacondarwin
Copy link
Contributor

@lgalfaso - this will need to be rebased onto master since the animation css class names have changed.

@petebacondarwin
Copy link
Contributor

Currently failing to build: http://ci.angularjs.org/job/angular.js-pete/153/console

@petebacondarwin
Copy link
Contributor

Getting better - after a few tweaks - but of course Internet Explorer is complaining: http://ci.angularjs.org/job/angular.js-pete/154/console

style($compile): clarify argument name

Merge branch 'master' into ng-repeat-start

Conflicts:
	test/ng/directive/ngRepeatSpec.js
@lgalfaso
Copy link
Contributor Author

@petebacondarwin I do not have IE8 to test this, would it be possible to know if 6861142 passed at IE8?

@petebacondarwin
Copy link
Contributor

@lgalfaso
Copy link
Contributor Author

Ok, let me look for an IE8 box image and figure this out

Fix for [].splice on IE8 that has the second argument as mandatory
Fix for arrays on IE8 that adds an extra element on trailing comma
@lgalfaso
Copy link
Contributor Author

@petebacondarwin 0fae109 fixes the IE8 issues

transcludeContent.push(cursor);
cursor = cursor.nextSibling;
} while(count > 0 && cursor);
if (count > 0) throw Error('Unmatched ' + transcludeStart + '.');
Copy link
Contributor

Choose a reason for hiding this comment

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

it should be throw new Error...

without new the stack trace will contain an extra frame which looks confusing.

Add a `new` when throwing an error
@lgalfaso
Copy link
Contributor Author

Thanks @IgorMinar added the new when throwing these errors

@ghost ghost assigned mhevery May 23, 2013
@mhevery
Copy link
Contributor

mhevery commented May 24, 2013

This PR is now superseded by this one: #2783

Very good idea, I have built upon your idea and implemented a more generic version.

Closing

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

Successfully merging this pull request may close these issues.

4 participants