Ionic(Angular)でアコーディオンを実装する方法

by

クリエイターとエンジニアを応援するNP-Systems@個人開発

Ionic(Angular)でアコーディオンを実装する方法です。

ポイント

  • Componentを自作して、それを読み込むようにします
  • これによりいろいろな場所で使い回しができるようになります

結果

こんな感じでクリックするとカードが広がるようになります。

環境

Ionic 4, 5, 6で動作検証しています。

流れ

  1. コンポーネントを作成し、HTMLとSCSSを記載します
  2. 読み込みたい場所で作成したコンポーネントをmoduleに登録します
  3. 使いたい場所のHTMLでapp-expandableタグで呼び出します

ソースコード

GItHubに上げました。

https://github.com/NP-Systems/Ionic_tips/tree/id1131-createAccordion/frontend

参考文献

https://www.joshmorony.com/creating-an-accordion-list-in-ionic/

初期状態

IonicのTabsのデフォルトテンプレートから始めます。

appフォルダ以下の構成は下記のようになっています。

.
├── app-routing.module.ts
├── app.component.html
├── app.component.scss
├── app.component.spec.ts
├── app.component.ts
├── app.module.ts
├── explore-container
│   ├── explore-container.component.html
│   ├── explore-container.component.scss
│   ├── explore-container.component.spec.ts
│   ├── explore-container.component.ts
│   └── explore-container.module.ts
├── tab1
│   ├── tab1-routing.module.ts
│   ├── tab1.module.ts
│   ├── tab1.page.html
│   ├── tab1.page.scss
│   ├── tab1.page.spec.ts
│   └── tab1.page.ts
├── tab2
│   ├── tab2-routing.module.ts
│   ├── tab2.module.ts
│   ├── tab2.page.html
│   ├── tab2.page.scss
│   ├── tab2.page.spec.ts
│   └── tab2.page.ts
├── tab3
│   ├── tab3-routing.module.ts
│   ├── tab3.module.ts
│   ├── tab3.page.html
│   ├── tab3.page.scss
│   ├── tab3.page.spec.ts
│   └── tab3.page.ts
└── tabs
    ├── tabs-routing.module.ts
    ├── tabs.module.ts
    ├── tabs.page.html
    ├── tabs.page.scss
    ├── tabs.page.spec.ts
    └── tabs.page.ts

自作コンポーネントの作成

下記のコマンドで自作コンポーネントを作成します。

% ionic g component components/Expandable

app.module.tsと同じ階層にcomponentsフォルダができて新しいコンポーネントが生成されます。

.
├── app-routing.module.ts
├── app.component.html
├── app.component.scss
├── app.component.spec.ts
├── app.component.ts
├── app.module.ts
├── components
│   └── expandable
│       ├── expandable.component.html
│       ├── expandable.component.scss
│       ├── expandable.component.spec.ts
│       └── expandable.component.ts
├── explore-container
│   ├── explore-container.component.html
│   ├── explore-container.component.scss
│   ├── explore-container.component.spec.ts
│   ├── explore-container.component.ts
│   └── explore-container.module.ts
├── tab1
│   ├── tab1-routing.module.ts
│   ├── tab1.module.ts
│   ├── tab1.page.html
│   ├── tab1.page.scss
│   ├── tab1.page.spec.ts
│   └── tab1.page.ts
├── tab2
│   ├── tab2-routing.module.ts
│   ├── tab2.module.ts
│   ├── tab2.page.html
│   ├── tab2.page.scss
│   ├── tab2.page.spec.ts
│   └── tab2.page.ts
├── tab3
│   ├── tab3-routing.module.ts
│   ├── tab3.module.ts
│   ├── tab3.page.html
│   ├── tab3.page.scss
│   ├── tab3.page.spec.ts
│   └── tab3.page.ts
└── tabs
    ├── tabs-routing.module.ts
    ├── tabs.module.ts
    ├── tabs.page.html
    ├── tabs.page.scss
    ├── tabs.page.spec.ts
    └── tabs.page.ts

tab1.module.tsでの読み込み

今回はtab1ページに埋め込むことにするので、tab1.module.tsに下記のように登録します。

import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Tab1Page } from './tab1.page';
import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
import { ExpandableComponent } from "../components/expandable/expandable.component";

import { Tab1PageRoutingModule } from './tab1-routing.module';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    ExploreContainerComponentModule,
    Tab1PageRoutingModule
  ],
  declarations: [Tab1Page,ExpandableComponent]
})
export class Tab1PageModule {}

expandable.component.htmlの変更

 src/components/expandable/expandable.component.htmlのファイルを下記のように変更します。

<p>
  expandable works!
</p>

<div #expandWrapper class='expand-wrapper' [class.collapsed]="!expanded">
    <ng-content></ng-content>
</div>

expandable.component.scssの変更

.expand-wrapper {
  transition: max-height 0.4s ease-in-out;
  overflow: hidden;
  height: auto;
}

.collapsed {
  max-height: 0 !important;
}

expandable.component.tsの変更

Before

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-expandable',
  templateUrl: './expandable.component.html',
  styleUrls: ['./expandable.component.scss'],
})
export class ExpandableComponent implements OnInit {

  constructor() { }

  ngOnInit() {}

}

After

import { Component, AfterViewInit, Input, ViewChild,  ElementRef, Renderer2 } from "@angular/core";

@Component({
  selector: "app-expandable",
  templateUrl: "./expandable.component.html",
  styleUrls: ["./expandable.component.scss"]
})
export class ExpandableComponent implements AfterViewInit {
  @ViewChild("expandWrapper", { read: ElementRef }) expandWrapper: ElementRef;
  @Input("expanded") expanded: boolean = false;
  @Input("expandHeight") expandHeight: string = "150px";

  constructor(public renderer: Renderer2) {}

  ngAfterViewInit() {
    this.renderer.setStyle(this.expandWrapper.nativeElement, "max-height", this.expandHeight);
  }
}

tab1.page.htmlの変更

Before

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Tab 1
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Tab 1</ion-title>
    </ion-toolbar>
  </ion-header>

  <app-explore-container name="Tab 1 page"></app-explore-container>
</ion-content>

After

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Tab 1
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-card (click)="expandItem(item)" *ngFor="let item of items">
    <ion-card-header>
      <ion-card-title>隣のトトロ</ion-card-title>
    </ion-card-header>

    <ion-card-content>
      <app-expandable expandHeight="100px" [expanded]="item.expanded">
        <p>
          とっとろ、と、とーろ
        </p>
        <p>
          とっとろ、と、とーろ
        </p>
      </app-expandable>
    </ion-card-content>
  </ion-card>
</ion-content>

tab1.page.tsの変更

Before

import { Component } from '@angular/core';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})
export class Tab1Page {

  constructor() {}

}

After

import { Component } from '@angular/core';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})
export class Tab1Page {
  public items: any = [];

  constructor() {
    this.items = [
      { expanded: false },
      { expanded: false },
      { expanded: false },
      { expanded: false },
      { expanded: false },
      { expanded: false },
      { expanded: false },
      { expanded: false },
      { expanded: false }
    ];
  }

  expandItem(item): void {
    if (item.expanded) {
      item.expanded = false;
    } else {
      this.items.map(listItem => {
        if (item == listItem) {
          listItem.expanded = !listItem.expanded;
        } else {
          listItem.expanded = false;
        }
        return listItem;
      });
    }
  }
}


About Author: M. Shikishima

@Masaya04997245 会社員をしながら個人でシステム開発をしています.
this is single-default.php
最新記事
同カテゴリの記事 ionic-angular