Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

add a select attribute on tabs pane to trigger a callback #141

Closed
wants to merge 4 commits into from

Conversation

revolunet
Copy link

Hi :)

I needed to have a callback on tab selection so i added a select attribute on the tabs pane directive.

eg: <pane select="updateImage(tab.thumbnail)">

Comments appreciated.

Thanks !

Julien

@pkozlowski-opensource
Copy link
Member

@revolunet thnx for the PR, it looks good. Before merging it I would like to ask if you saw that tabs got the active property that you could $watch and execute any logic when the watched value becomes true. Would this work for your use case?

@revolunet
Copy link
Author

@pkozlowski-opensource i dont have any idea how i could watch easily the pane.active property for each pane of the tabs as the active attribute is related to pane and not tabs. i would set multiple watchers ?

in my case, i'm in a ng-repeat :

<pane ng-repeat="tab in tabs" heading="{{ tab.title }}" active="tab.active" select="changeThumbnail(tab.thumbnail)">...</pane>

Also, can you please explain why heading attribute have double curly braces syntax and not active attribute ? looks like inconsistency to me, isnt it ?

thanks :)

@pkozlowski-opensource
Copy link
Member

@revolunet yeh, you are right, I was playing with the $watch solution and although I've managed to make it work it is ugly...

Will squash commits and merge it unless others got different idea...

@revolunet
Copy link
Author

thanks :)

fix me please : why that different syntax between heading and active attributes ?
(example copied from your demo page)

@pkozlowski-opensource
Copy link
Member

Regarding different syntax: {{ }} are used for interpolation, so in the cases where you want to have several dynamic parts evaluated withing one attribute. It allows you to write heading="My heading {{$index}}" instead of heading="'My heading ' +$index". This is the same syntax used by ngSrc for example.

The syntax without curly is used where there is only one value to evaluate. This is typically used for non-string values (boolean, event handlers etc.). So this "inconsistency" exists in AngularJS already and is about offering the most user-friendly syntax.

@revolunet
Copy link
Author

ok thanks for clarification :)
so i f i understand well, both attributes behave the same and we could use header="tab.title" without curly braces ?

@darwin
Copy link

darwin commented Feb 18, 2013

I have an alternative proposal:

  • id of active pane should be a bindable variable on tabs directive. Say active-id (optional)
  • pane ids may be defined on pane directives via pane-id (optional)
  • by default each pane will get numeric id (ordering in parent tabs)
    <tabs active-id="model.activePaneId">
        <pane heading="Static title" pane-id="static-pane">Static content</pane> <!-- pane-id == "static-pane" -->
        <pane heading="Static title2">Static content</pane> <!-- pane-id == 1 -->
        <pane ng-repeat="pane in panes" pane-id="{{pane.id}}" heading="{{pane.title}}" active="pane.active">{{pane.content}}</pane>
    </tabs>

I believe this is more flexible. @revolunet's solution works fine for ng-repeat but must be repeated manually for static panes. Also his approach does not support $watch and binding. His example would reduce to active-id="something" and then setting $watch on something. When using default index-based ids, he can lookup active pane directly with the index. In more complex scenarios it is up to the user to handle mapping between ids and panes. Alternatively more complex data structure could be stored in pane-id.

If you agree I could try to implement it. I need it for my code anyway. Right now I have to do this in my controllers:

  $scope.findActivePane = ->
    _.find @panes, (pane) -> pane.active

  checkIfActivePaneHasChanged = ->
    activePane = $scope.findActivePane()
    return false unless activePane
    dirty = activePane.id isnt $scope.activePaneId
    $scope.activePaneId = activePane.id if dirty
    dirty

  $scope.$watch checkIfActivePaneHasChanged, ->
    $location.path "/settings/#{$scope.activePaneId}"

@pkozlowski-opensource
Copy link
Member

@darwin I agree that it is a bit more flexible but AngularJS gives as so much more ability to work with objects as compared to relaying on ids that I almost never use them in AngularJS web apps.

Couldn't we do something like this in a pane:

<pane on-select="myexpression"> where the expression would be evaluated when a pane is selected. Then, in this expression you could do either myfunction('customId') or myfunction($index) (if used with ngRepeat) or even activeTab = 'myId' + $index. Should be really easy to implement with $parse.

How does it sound?

@darwin
Copy link

darwin commented Feb 18, 2013

Looks good. But I still think that you want to do this handling on tabs directive. It seems more logical to me. And the main argument it that you don't want to repeat on-select for each pane. In most cases it would contain the same expression.

@darwin
Copy link

darwin commented Feb 18, 2013

Maybe my "id" naming is confusing. Maybe you can look at it as "payload". Whatever you put there, you get back.

@darwin
Copy link

darwin commented Feb 18, 2013

@pkozlowski-opensource Please look at implementation of tabbable directive in angular.js repo:
https://github.com/angular/angular.js/blob/master/src/bootstrap/bootstrap.js#L52

They expose my "id" on tabbable directive via ngModel and call it "value", here is the code how they get it:
https://github.com/angular/angular.js/blob/master/src/bootstrap/bootstrap.js#L109

Actually I've switched from their implementation to yours. I'm happy, yours is much more cleaner. But I'm really missing a single place where I can hook to observe tab changes. I don't want to attach handler to each pane individually. I think it should be on tabs and be flexible for different scenarios (binding, static value, function call, etc.).

@revolunet
Copy link
Author

@darwin : i understand your two-way data binding for the tabs.active attribute. you could use pane index number (0, 1, 2...) or, as you say, if they have pane-id defined, their custom id directly instead of the natural index. Will be a nice addition.

I thinks the pane select attribute is still useful though as its very easy to use as you dont need to setup watchers to trigger something.

@seanpdoyle
Copy link

👍

This would be a very desirable feature.

ajoslin added a commit that referenced this pull request Mar 30, 2013
* 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
ajoslin added a commit that referenced this pull request Mar 31, 2013
* 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
@pkozlowski-opensource
Copy link
Member

@revolunet We've got a PR now from @ajoslin that addresses many issues with tabs, this one being one of them. As such I'm going to close this PR and focus on Andy's one (#287). Would be totally awesome if you could have a look at the code from #287 to see if it works for you.

@revolunet
Copy link
Author

Hi! awesome work, and yes it fits perfectly with my needs :)

Thanks guys !

ajoslin added a commit that referenced this pull request Apr 6, 2013
* 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
ajoslin added a commit that referenced this pull request Apr 8, 2013
* 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>
ajoslin added a commit that referenced this pull request Apr 9, 2013
* 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>
ajoslin added a commit that referenced this pull request Apr 28, 2013
* 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>
ajoslin added a commit that referenced this pull request May 9, 2013
* 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>
ajoslin added a commit that referenced this pull request May 9, 2013
* 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>
@saschaklatt
Copy link

It seems like the select callback of all tabs will be triggered when the route changes. So everytime I leave the view that includes the tabs a select event from every single tab is fired. Is this a bug?

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.

None yet

5 participants