-
Notifications
You must be signed in to change notification settings - Fork 6.7k
fix(tabs): keep stable tab order (when filtering ng-repeat) #153
Conversation
Thanks, @darwin! Good fix. Could you add some evil test cases for when more elements are between panes, to be sure it doesn't goof up? The index values might mess up in this case, but to see if the tabs still work as intended. Eg: <tabs>
<pane>One</pane>
<div ng-repeat="i in weirdDivs">Hello!</div>
<pane ng-repeat="p in panes | filter:paneIsAvailable"></pane>
<div ng-repeat="i in moreWeirdDivs">I'm gonna mess you up!</div>
<pane>Last pane</pane>
</tabs> |
…more complex test
You are right. Those extra divs mess up the indexing. Now I filter children via .tab-pane class. But it depends on template, I don't know how to distinguish tab panes. Do have a better idea? |
Ok, found a better way. Is it ok now? Let me know if you want me to squash those commits into a single one. |
That works! Though I'm not sure about adding the variable to the scope. It's probably fine though. I'd say definitely rename the variable to And we can squash it on our end before pushing - you'd have to open a new PR with a new branch and squashed commits otherwise. |
It does not have to be stored in the scope. I could store it on raw DOM element similar way. It would be great to find a way if directive's X linking phase was applied on a DOM element. Maybe such information is already available in Angular... |
Actually the more I think about this directive the more I think it should fundamentally change. Instead of using a repeater inside the template we should be using the actual pane element as the tabs and then transcluding their content into the pane content area. This would also help with transclusion of headings. What do you think? |
That sounds interesting Pete. We could also think about transcluding only the currently selected tab's content (load a tab's content when selected, throw it out when not selected and load the new one). The transclusion way would fix this problem and I'm sure some future problems. |
Hmmm, I'm really nervous about the fact that it links directives JavaScript code to DOM structure. I'm not sure if I understand correctly the whole problem / solution, but wouldn't it break if we introduce grouping of tabs as discussed in #150? Ideally directive's JavaScript code should know nothing about CSS and DOM structure. I know it is hard and practically impossible in many cases but this is something we should strive for. I need to look into this more but I would like to search for an alternative solution. |
@darwin This is great work and test are good so you are on something. But I would really like to discuss it a bit more and try really hard to keep CSS / DOM knowledge out of JavaScript. The way I like to think about it is this: let's say that I would like to use a directive with the http://foundation.zurb.com/ or similar. Can I do this by only changing a template? |
I created more general implementation. Now the only assumption is that The implementation is more complex. I've introduced a way how to sort panes by computing their DOM "order" in relation to |
…, does not assume panes to be on the same DOM level
Sorry @darwin! I think the real way to fix this is to make the headers be the actual elements instead of the tab contents. So then the headers will always be in the right order without any of this confurmuddledness 😕 (made up word). I may be able to work on a refactor soon... |
Ok, no hard feelings. You may close it if you want. |
* Tabs transclude to title elements instead of content elements, so the ordering is always correct (#153) * Rename `<tabs>` to `<tabset>`, `<pane>` to `<tab>` (#186) * Add `<tab-heading>` directive, which is a child of a `<tab>`. Allows HTML in tab headings (#124) * Add `select` attribute callback when tab is selected (#141) * Only the active tab's content is actually ever in the DOM
* Tabs transclude to title elements instead of content elements, so the ordering is always correct (#153) * Rename `<tabs>` to `<tabset>`, `<pane>` to `<tab>` (#186) * Add `<tab-heading>` directive, which is a child of a `<tab>`. Allows HTML in tab headings (#124) * Add `select` attribute callback when tab is selected (#141) * Only the active tab's content is actually ever in the DOM
* Tabs transclude to title elements instead of content elements, so the ordering is always correct (Closes #153) * Rename `<tabs>` to `<tabset>`, `<pane>` to `<tab>` (Closes #186) * Add `<tab-heading>` directive, which is a child of a `<tab>`. Allows HTML in tab headings (Closes #124) * Add `select` attribute callback when tab is selected (Closes #141) * Only the active tab's content is now actually ever in the DOM
* Rename 'tabs' directive to 'tabset', and 'pane' directive to 'tab'. The new syntax is more intuitive; The word pane does not obviously represent a subset of a tab group. (Closes #186) * Add 'tab-heading' directive, which is a child of a 'tab'. Allows HTML in tab headings. (Closes #124) * Add option for a 'select' attribute callback when a tab is selected. (Closes #141) * Tabs transclude to title elements instead of content elements. Now the ordering of tab titles is always correct. (Closes #153) * Only the active tab's content is ever present in the DOM. This is another plus of transcluding tabs to title elmements, and provies a performance increase. BREAKING CHANGE: The 'tabs' directive has been renamed to 'tabset', and the 'pane' directive has been renamed to 'tab'. To migrate your code, follow the example below. Before: <tabs> <pane heading="one"> First Content </pane> <pane ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </pane> </tabs> After: <tabset> <tab heading="one"> First Content </tab> <tab ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </tab> </tabset>
* Rename 'tabs' directive to 'tabset', and 'pane' directive to 'tab'. The new syntax is more intuitive; The word pane does not obviously represent a subset of a tab group. (Closes #186) * Add 'tab-heading' directive, which is a child of a 'tab'. Allows HTML in tab headings. (Closes #124) * Add option for a 'select' attribute callback when a tab is selected. (Closes #141) * Tabs transclude to title elements instead of content elements. Now the ordering of tab titles is always correct. (Closes #153) * Only the active tab's content is ever present in the DOM. Provides an increase in performance. BREAKING CHANGE: The 'tabs' directive has been renamed to 'tabset', and the 'pane' directive has been renamed to 'tab'. To migrate your code, follow the example below. Before: <tabs> <pane heading="one"> First Content </pane> <pane ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </pane> </tabs> After: <tabset> <tab heading="one"> First Content </tab> <tab ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </tab> </tabset>
* Rename 'tabs' directive to 'tabset', and 'pane' directive to 'tab'. The new syntax is more intuitive; The word pane does not obviously represent a subset of a tab group. (Closes #186) * Add 'tab-heading' directive, which is a child of a 'tab'. Allows HTML in tab headings. (Closes #124) * Add option for a 'select' attribute callback when a tab is selected. (Closes #141) * Tabs transclude to title elements instead of content elements. Now the ordering of tab titles is always correct. (Closes #153) BREAKING CHANGE: The 'tabs' directive has been renamed to 'tabset', and the 'pane' directive has been renamed to 'tab'. To migrate your code, follow the example below. Before: <tabs> <pane heading="one"> First Content </pane> <pane ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </pane> </tabs> After: <tabset> <tab heading="one"> First Content </tab> <tab ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </tab> </tabset>
* Rename 'tabs' directive to 'tabset', and 'pane' directive to 'tab'. The new syntax is more intuitive; The word pane does not obviously represent a subset of a tab group. (Closes #186) * Add 'tab-heading' directive, which is a child of a 'tab'. Allows HTML in tab headings. (Closes #124) * Add option for a 'select' attribute callback when a tab is selected. (Closes #141) * Tabs transclude to title elements instead of content elements. Now the ordering of tab titles is always correct. (Closes #153) BREAKING CHANGE: The 'tabs' directive has been renamed to 'tabset', and the 'pane' directive has been renamed to 'tab'. To migrate your code, follow the example below. Before: <tabs> <pane heading="one"> First Content </pane> <pane ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </pane> </tabs> After: <tabset> <tab heading="one"> First Content </tab> <tab ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </tab> </tabset>
* Rename 'tabs' directive to 'tabset', and 'pane' directive to 'tab'. The new syntax is more intuitive; The word pane does not obviously represent a subset of a tab group. (Closes #186) * Add 'tab-heading' directive, which is a child of a 'tab'. Allows HTML in tab headings. (Closes #124) * Add option for a 'select' attribute callback when a tab is selected. (Closes #141) * Tabs transclude to title elements instead of content elements. Now the ordering of tab titles is always correct. (Closes #153) BREAKING CHANGE: The 'tabs' directive has been renamed to 'tabset', and the 'pane' directive has been renamed to 'tab'. To migrate your code, follow the example below. Before: <tabs> <pane heading="one"> First Content </pane> <pane ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </pane> </tabs> After: <tabset> <tab heading="one"> First Content </tab> <tab ng-repeat="apple in basket" heading="{{apple.heading}}"> {{apple.content}} </tab> </tabset>
The pane directive should call tabsCtrl.addPane with index, because link function is not guaranteed to be called sequentially. DOM panes may appear or disappear on one-by-one basis in more advanced scenarios with applied ng-repeat filtering.