質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.64%

ionicカレンダーアプリ 日付ごとにイベントを判別できるようにしたい。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 325

Gento

score 28

前提・実現したいこと

カレンダーアプリを制作しており、日付ごとのページでイベント操作をできるようにしたいです。

※参考画像
イメージ説明
イメージ説明

補足情報(FW/ツールのバージョンなど)

カレンダーのライブラリー: ionic2-calendar

ionic info

Ionic:

   Ionic CLI                     : 5.2.5 (/usr/local/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.7.1
   @angular-devkit/build-angular : 0.801.3
   @angular-devkit/schematics    : 8.1.3
   @angular/cli                  : 8.1.3
   @ionic/angular-toolkit        : 2.0.0

Cordova:

   Cordova CLI       : 9.0.0 (cordova-lib@9.0.1)
   Cordova Platforms : none
   Cordova Plugins   : no whitelisted plugins (0 plugins total)

Utility:

   cordova-res : 0.6.0 
   native-run  : 0.2.8 

System:

   NodeJS : v10.15.1 (/usr/local/bin/node)
   npm    : 6.4.1
   OS     : macOS Mojave
   Xcode  : Xcode 10.2.1 Build version 10E1001

参考にしたサイト: 

idを使った画面遷移を参考にした

カレンダーとFirebaseの接続を参考にした

発生している問題・エラーメッセージ

日付ごとにイベントを判別できない。

イベントのidをパラメーターとして渡すために以下のようにしました。

<ion-header>
  <ion-toolbar color="danger">
      <ion-buttons slot="start">
          <ion-menu-button></ion-menu-button>
        </ion-buttons>
    <ion-title>
      {{ viewTitle }}
    </ion-title>
    <ion-buttons slot="end">
        <ion-button (click)="today()">Today</ion-button>
      </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content padding>

    <calendar [eventSource]="eventSource"
    [calendarMode]="calendar.mode"
    [currentDate]="calendar.currentDate"
    (onCurrentDateChanged)="onCurrentDateChanged($event)"
    (onEventSelected)="onEventSelected($event)"
    (onTitleChanged)="onViewTitleChanged($event)"
    (onTimeSelected)="onTimeSelected($event)"
    [locale]="calendar.locale"
    *ngFor="let dayEvent of (dayEvents | async)"
    [routerLink]="['/day', dayEvent.id]"
    >
</calendar>

<ion-list>
  <ion-item *ngFor="let dayEvent of (dayEvents | async)">
    <!-- 全イベントのidが取れる -->
   <p>{{ dayEvent.id }}</p>
  </ion-item>
</ion-list>
</ion-content>

すると、イベントの数だけカレンダーが作成されます(*ngForなので当たり前ですが)

Firebaseにイベントが3つあるとして、idが a, b, c とします。 するとカレンダーが3つ作成され、一番上のカレンダーのどこを押してもidがaとなり、真ん中のカレンダーのどこを押してもidがbとなり、、、

という感じです。

カレンダーが1つで、かつ日付ごとにイベントを判別できない状態です。

仮に5日にイベントがあったとして、5日を押すと5日のページに遷移し、イベントありと表記されている。6日を押すと6日のページに遷移するが、イベントなしと表記されている。

このような状態にしたいです。

該当のソースコード

home.page.html

<ion-header>
  <ion-toolbar color="danger">
      <ion-buttons slot="start">
          <ion-menu-button></ion-menu-button>
        </ion-buttons>
    <ion-title>
      {{ viewTitle }}
    </ion-title>
    <ion-buttons slot="end">
        <ion-button (click)="today()">Today</ion-button>
      </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content padding>

    <calendar [eventSource]="eventSource"
    [calendarMode]="calendar.mode"
    [currentDate]="calendar.currentDate"
    (onCurrentDateChanged)="onCurrentDateChanged($event)"
    (onEventSelected)="onEventSelected($event)"
    (onTitleChanged)="onViewTitleChanged($event)"
    (onTimeSelected)="onTimeSelected($event)"
    [locale]="calendar.locale"
    [routerLink]="['/day', dayEvent.id]"
    >
</calendar>

<ion-list>
  <ion-item *ngFor="let dayEvent of (dayEvents | async)">
    <!-- イベントのidは取れている -->
   <p>{{ dayEvent.id }}</p>
  </ion-item>
</ion-list>
</ion-content>

home.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
import { map, take } from 'rxjs/operators';

export interface DayEvent {
  id?: string;
  title: string;
  endTime: Date;
  startTime: Date;
  allDay: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class HomeService {
  // 取得したコレクションを格納
  private dayCollection: AngularFirestoreCollection<DayEvent>;
  // コレクションのストリームを格納
  private dayEvents: Observable<DayEvent[]>;

  eventSource = [];
  selectedDate = new Date();

  constructor(
    private afs: AngularFirestore
  )
  {
    // コレクションを取得してdayCollectionに格納
    this.dayCollection = this.afs.collection<DayEvent>(`events`);
    this.dayEvents = this.dayCollection.snapshotChanges().pipe(
      map(actions => {
        return actions.map(a => {
          const event = a.payload.doc.data();
          const id = a.payload.doc.id;
          return { id, ...event };
        });
      })
    );
  }

  getEvents(): Observable<DayEvent[]> {
    return this.dayEvents;
}

  getEvent(id: string) {
    return this.dayCollection.doc<DayEvent>(id).valueChanges().pipe(
      take(1),
      map(dayEvent => {
        dayEvent.id = id;
        console.log('getEventで取った ' + dayEvent.id);
        return dayEvent;
      })
    );
  }

  addNewEvent() {
    const start = this.selectedDate;
    const end = this.selectedDate;
    end.setMinutes(end.getHours() + 1);
    const event = {
       title: 'Event #' + start.getMinutes(),
       startTime: start,
       endTime: end,
       allDay: true
    };
    console.log('イベント登録' + start);
     // this.db.collection(`users/${this.authService.userId}/events`);
    return this.afs.collection(`events`).add(event);
  }

  deleteEvent(id: string): Promise<void> {
    return this.afs.collection(`events`).doc(id).delete();
  }

}

home.page.ts

import { AngularFirestore } from '@angular/fire/firestore';
import { HomeService, DayEvent } from './home.service';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
  eventSource = [];
  viewTitle;
  calendar = {
    mode: 'month',
    currentDate: new Date(),
    locale: 'ja-JP'
  };

  // ここでのプロパティがhtmlで使われる
  private dayEvents: Observable<DayEvent[]>;


  constructor(
    private router: Router,
    private homeService: HomeService,
    private db: AngularFirestore
  ) {
    // Firebaseからイベント取得
    this.db.collection(`events`).snapshotChanges().subscribe(colSnap => {
      this.eventSource = [];
      colSnap.forEach(snap => {
       const event: any = snap.payload.doc.data();
       event.id = snap.payload.doc.id;
       event.startTime = event.startTime.toDate();
       event.endTime = event.endTime.toDate();
       this.eventSource.push(event);
        });
    });

    }


  ngOnInit() {
    this.dayEvents = this.homeService.getEvents();
  }

  onViewTitleChanged(title) {
    this.viewTitle = title;
  }



  onEventSelected(event) {
    console.log('Event selected:' + event.startTime + '-' + event.endTime + ',' + event.title);
  }

  // 選択した日付を取得&サービスのaddNewEventを設定
  onTimeSelected(ev) {
    const selected = new Date(ev.selectedTime);
    console.log('選択した日は ' + selected);
    this.homeService.selectedDate = selected;
  }

  onCurrentDateChanged(event: Date) {
  }

  reloadSource(ev) {
    console.log('range changed: startTime: ' + ev.startTime + ', endTime: ' + ev.endTime);
  }


  today() {
    this.calendar.currentDate = new Date();
  }
}

day.page.html

<ion-header>
  <ion-toolbar>
      <ion-buttons slot="start">
          <ion-back-button defaultHref="/home"></ion-back-button>
        </ion-buttons>
        <ion-buttons slot="end">
          <ion-button (click)="addNewEvent()">登録</ion-button>
        </ion-buttons>
    <ion-title>
      Dayページ
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-button (click)="deleteEvent()">削除</ion-button>
  <!-- ここのidのイベントが削除される -->
  <p>{{ dayEvent.id }}</p>
</ion-content>

day.page.ts

import { HomeService, DayEvent } from './../home/home.service';
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { ToastController } from '@ionic/angular';


@Component({
  selector: 'app-day',
  templateUrl: './day.page.html',
  styleUrls: ['./day.page.scss'],
})

export class DayPage implements OnInit {
  eventSource = [];
  viewTitle;
  selectedDate = new Date();

  dayEvent: DayEvent = {
    title: '',
    startTime: this.selectedDate,
    endTime: this.selectedDate,
    allDay: true
  };
  id = null;

  constructor(
    private homeService: HomeService,
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private toastCtrl: ToastController,
  ) {
  }

  ngOnInit() {
    // idを取得
    this.id = this.activatedRoute.snapshot.paramMap.get('id');
  }

  ionViewWillEnter() {
    if (this.id) {
     this.homeService.getEvent(this.id).subscribe(dayEvent => {
        this.dayEvent = dayEvent;
      });
    }
  }


  addNewEvent() {
    this.homeService.addNewEvent().then(() => {
      this.router.navigateByUrl('/');
    });
  }

  deleteEvent() {
    this.homeService.deleteEvent(this.dayEvent.id).then(() => {
      this.router.navigateByUrl('/');
      this.showToast('削除しました');
    },
      err => {
        this.showToast('問題が発生しました。削除できなかったです。');
      });
  }

  showToast(msg) {
    this.toastCtrl.create({
      message: msg,
      duration: 2000
    }).then(toast => toast.present());
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • xenbeat

    2019/08/28 20:26

    home.service.tsにどのようにコードを書けば良いかわかったのであれば前回の質問をクローズ頂けますか。
    https://teratail.com/questions/208362

    キャンセル

  • Gento

    2019/08/29 14:29

    わかりました。

    キャンセル

回答 1

checkベストアンサー

+1

ngforのループに番号持たせてみてはどうでしょうか?

ngFor="let item of items; let i = index

iにて日付処理する。という感じで。

たぶん僕はこんな感じで逃げてると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/29 14:35

    ngForでループを回している限り、イベントの数だけカレンダーが作成されると思うのですが、、、

    しかし、ループ1つ1つに処理をできるというのは、参考になりました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる