I would like to use the md-expansion-panel within a custom component e.g. bsm-current-item that has a md-expansion-panel, since it is best practice to make 'dumb' components.
But when you use a md-expansion-panel within a custom component, the mat-expansion-panel-spacing class is not triggered, since _hasSpacing() seems to be false. And the margin isn't added.
bsm-current-item (dumb component)
# Simplied by using the example code
<md-expansion-panel class="md-expansion-demo-width" #myPanel>
<md-expansion-panel-header expandedHeight="104px" collapsedHeight="88px">
<mat-panel-description>This is a panel description.</mat-panel-description>
<mat-panel-title>Panel Title</mat-panel-title>
</md-expansion-panel-header>
This is the content text that makes sense here.
<md-action-row>
<button md-button (click)="myPanel.expanded = false">CANCEL</button>
<button md-button>SAVE</button>
</md-action-row>
</md-expansion-panel>
bsm-playlist (host)
<md-accordion>
<bsm-current-item *ngFor="let playlistItem of playlistItems$ | async | orderBy: 'orderNumber';" [playlistItem]="playlistItem">
</md-accordion>
Wat is the proper way to handle this?
Not creating a custom component with the expansion panel behavior and adding the expansion panel code to bsm-playlist, works, but doesn't seem/feels like a proper solution
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ â–³ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
@angular/cli: 1.3.2
node: 8.1.4
os: win32 x64
@angular/animations: 4.3.6
@angular/cdk: 2.0.0-beta.10-1b6b270
@angular/common: 4.3.6
@angular/compiler: 4.3.6
@angular/core: 4.3.6
@angular/flex-layout: 2.0.0-rc.1
@angular/forms: 4.3.6
@angular/http: 4.3.6
@angular/material: 2.0.0-beta.10-1b6b270
@angular/platform-browser: 4.3.6
@angular/platform-browser-dynamic: 4.3.6
@angular/platform-server: 4.3.6
@angular/router: 4.3.6
@angular/cli: 1.3.2
@angular/compiler-cli: 4.3.6
@angular/language-service: 4.3.6
It looks like it's because @Host only injects MdAccordion if the host component matches the MdAccordion directive. IMO your use case seems valid, but I don't know enough about DI to say that just using @Optional would suffice.
Optional() doesn't seem tot do the trick. (adding it to my custom-component's constructor)
and extending from AccordionItem doesn't work either.
Oh I meant making the change in the expansion panel source.
There is probably a way to have your bsm-current-item component carry the MdAccordion directive down the tree so that the expansion panel can still properly inject it. @crisbeto there aren't many docs on how @Host works. Do you know if that's somehow possible?
You will not be able to forward the @Host properties for the component as you outlined above. The @Host decorator simply limits how far up the tree of injectors injection will be searched for, stopping at the hosting element of the component. In the case outlined in this issue, the host element is bsm-current-item.
The design of mat-accordion and mat-expansion-panel requires the components to be tightly coupled for the management of animations and styling. This is the reason that @Host is present on the constructor for mat-expansion-panel. (Note: that @Host is not present on the cdk-accordion-item as this tight coupling is not necessary for the interactivity of the components.
Thanks @josephperrott for your explanation.
Is this tight coupling in line with the component based design (smart/dumb components) ?
For my use case, this would mean a pretty big host component, which has know-how about the look, feel and behavior of the items (expansion panels with their subcontent => (click) => loads new data to be displayed in the expanded panel. Look and click behavior of these panel-content items, etc.
Could be that my design isn't correct and that I'm missing something (still struggling with the proper way to use/design smart and dumb components). But what would be the proper solution for this use case?
<md-accordion>
<my-custom-item *ngFor="item of items" [item]="item">
</my-custom-item>
</md-accordion>
my-item
# slightly simpliefied
<md-expansion-panel>
<md-expansion-panel-header (click)="loadPlayerStats()" expandedHeight="104px" collapsedHeight="88px">
<md-panel-title>
<md-icon>icon</md-icon
<div class="__title--two-line">
<h3 class="__title __line">
{{ mainTitle }}
</h3>
<p class="__subtitle __line"> {{ subTitle }}</p>
</div>
</md-panel-title>
<md-panel-description>
Played by: {{ player.name }}
</md-panel-description>
</md-expansion-panel-header>
#=================================================
# Panel content
# Data is loaded after header (click)
#=================================================
<md-list class="__player-stats">
<md-list-item class="__hoverable" *ngFor="let player of players$ | async | orderBy: 'firstName'">
<md-icon mdListAvatar class="o-avatar" svgIcon="{{ player.avatar }}"></md-icon>
<div class="__wrapper" mdLine fxLayout="row" fxLayoutGap="1rem" fxLayoutAlign="space-between start">
<div fxFlex="35">
<h3 mdLine>{{ player.firstName }}</h3>
<p mdLine>{{ player.lastName }}</p>
</div>
<div fxFlex="20">
<h5 class="__subheader" mdLine>Stats 1</h5>
<p mdLine>{{ player.stats1Date}}
<md-icon class="__question-set--state __completed">done</md-icon>
</p>
</div>
<div fxFlex="20">
<h5 class="__subheader" mdLine>Statst 2</h5>
<p mdLine>{{ player.stats2Date}}
<md-icon class="__question-set--state __completed">done</md-icon>
{{ player.stats2}}
</p>
</div>
<div fxFlex="20">
<h5 class="__subheader" mdLine>Stats 3</h5>
<p mdLine>
<md-icon class="__question-set--state">remove</md-icon>
</p>
</div>
<div fxFlex="5" fxFlexAlign="end">
<button md-icon-button (click)="showDialog()">
<md-icon>zoom_in</md-icon>
</button>
</div>
</div>
</md-list-item>
</md-list>
#=================================================
</md-expansion-panel>
N.B.
Thinking about moving the (click)="loadPlayerStats()" to the main/host component anyway, sincemy-current-item` shouldn't load any data. => dumb component
@the-ult the tight coupling decision makes sense and IMO you should just make your smart/dumb split elsewhere. Try this:
<md-accordion>
<md-expansion-panel *ngFor="item of items" [item]="item">
<!-- Panel header -->
<md-expansion-panel-header>
<md-panel-title>
<app-panel-title></app-panel-title>
</md-panel-title>
<md-panel-description>
...
</md-panel-description>
</md-expansion-panel-header>
<!-- Panel content -->
<app-player-stats [players]="players$ | async | orderBy: 'firstName'"></app-player-stats>
</md-expansion-panel>
</md-accordion>
You may have to adjust your inputs/outputs appropriately (or use a common service to communicate with the dumb components), but you should be able to architect it just fine!
If you want to discuss app design decisions, maybe post on Stack Overflow or r/Angular2 and link back here. I'd be happy to chat more about it.
Thanks @willshowell that's seems like a great solution.
Creating even smaller components is indeed something seem to forget.
I'll go ahead and close this issue since we have covered the tight coupling requirement for accordions.
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._
Most helpful comment
@the-ult the tight coupling decision makes sense and IMO you should just make your smart/dumb split elsewhere. Try this:
You may have to adjust your inputs/outputs appropriately (or use a common service to communicate with the dumb components), but you should be able to architect it just fine!
If you want to discuss app design decisions, maybe post on Stack Overflow or r/Angular2 and link back here. I'd be happy to chat more about it.