twinssbc / Ionic2-Calendar

A calendar component based on Ionic framework
https://ionic-calendar-demo.stackblitz.io
MIT License
387 stars 197 forks source link

Calendar month is bugged inside a modal #591

Open Quentius opened 3 years ago

Quentius commented 3 years ago

The calendar inside my modal is bugged.

Description: The Arrows next to the date (Februari 2021 as example) Ain't working on the screenshot. To make the swiping work, and actually be able to click on dates I need to swipe the calendar manually to the month next. Thats where the current date is.

It is important to check the dates in the pictures Example swipe: https://ibb.co/QN6TMfg How it is right now: https://ibb.co/gzRgWqh How it should be: https://ibb.co/ZT49tLX

Typescript:

import {AfterViewInit, Component, OnInit} from '@angular/core';
import {ModalController} from '@ionic/angular';
import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-cal-modal-activity',
  templateUrl: './cal-modal-activity.page.html',
  styleUrls: ['./cal-modal-activity.page.scss'],
})
export class CalModalActivityPage implements AfterViewInit {
  calendar = {
    mode: 'month',
    selected: null,
    currentDate: new Date()
  };
  eventSource = [];
  viewTitle: string;
  currentDate = new Date();
  startUpdate: any;
  endUpdate: any;

  startDate: Date = this.currentDate; // TODO Not working yet. Hour + 1, min + 15
  endDate = this.currentDate.setHours( + 1);
  // TODO Fix that user can select own time (Check randomevents for the code)
  event = {
    title: '',
    desc: '',
    startTime: this.currentDate,
    endTime: '',
  };
  onInit() {
    console.log(this.endDate);
  }

  constructor(private modalCtrl: ModalController) { }

  ngAfterViewInit() {
    const hidden = document.getElementById('hidden');
    hidden.style.display = 'none';
    document.getElementById('hidden2').setAttribute('style', 'display: none');
  }
  save() {
    this.modalCtrl.dismiss({event: this.event});
  }
  onViewTitleChanged(title) {
    this.viewTitle = title;
  }
  onTimeSelected(ev) {
    console.log('ev: ', ev);
    this.event.startTime = new Date(ev.selectedTime);
    this.startDate = new Date(ev.selectedTime);
  }
  close() {
    this.modalCtrl.dismiss();
  }
  showCalendar() {
    const hidden = document.getElementById('hidden');
    if (hidden.style.display === 'none') {
      hidden.style.display = 'block';
      document.getElementById('calendar').setAttribute('style', 'display: block');
      document.getElementById('hidden2').setAttribute('style', 'display: block');
    } else {
      hidden.style.display = 'none';
      document.getElementById('calendar').setAttribute('style', 'display: none');
      document.getElementById('hidden2').setAttribute('style', 'display: none');
    }
  }
  next() {
    this.calendar.currentDate = new Date(this.calendar.currentDate.setMonth(this.calendar.currentDate.getMonth() + 1));
  }
  back() {
    this.calendar.currentDate = new Date(this.calendar.currentDate.setMonth(this.calendar.currentDate.getMonth() - 1));
  }
  updateStart(startUpdate) {
    const datepipe: DatePipe = new DatePipe('en-US');
    const newDate = (datepipe.transform(startUpdate, 'd MMM. y HH:mm'));
    console.log(newDate);
  }
  updateEnd(endUpdate) {
    const datepipe: DatePipe = new DatePipe('en-US');
    const newDate = (datepipe.transform(endUpdate, 'd MMM. y HH:mm'));
    console.log(newDate);
  }
}

HTML:

<ion-toolbar>
    <ion-buttons slot="start">
        <ion-button (click)="close()">
            <ion-icon name="close" slot="icon-only"></ion-icon>
        </ion-button>
    </ion-buttons>
    <ion-title>New event</ion-title>
    <ion-buttons slot="end">
        <ion-button (click)="save()">
            <ion-icon name="checkmark" slot="icon-only"></ion-icon>
        </ion-button>
    </ion-buttons>
</ion-toolbar>

<ion-content>
    <ion-item lines="none">
        <ion-label position="stacked">Title</ion-label>
        <ion-input [(ngModel)]="event.title" type="text"></ion-input>
    </ion-item>
    <ion-item lines="none">
        <ion-label position="stacked">Description</ion-label>
        <ion-input [(ngModel)]="event.desc" type="text"></ion-input>
    </ion-item>
    <div style="width: 100%; height: 1.5%;color: transparent;"><h2 style="margin-top: 0;"></h2></div>
    <ion-item lines="none">
        <ion-label position="fixed">Whole day</ion-label>
        <ion-toggle slot="end">
        </ion-toggle>
    </ion-item>
    <ion-item lines="none" class="ion-no-padding start" (click)="showCalendar()">
        <ion-label class="startlabel" position="fixed">Start</ion-label>
        <h5 slot="end">{{startDate | date: 'd MMM. y'}}</h5>
    </ion-item>
    <ion-item id="hidden" lines="none" class="ion-no-padding">
        <ion-label class="startlabel" position="fixed">Time</ion-label>
        <ion-datetime slot="end" placeholder="{{startDate | date: 'HH:mm'}}" displayFormat="HH:mm" (ionChange)="updateStart(this.startUpdate)" [(ngModel)]="startUpdate"></ion-datetime>
    </ion-item>
    <ion-item id="hidden2" lines="none" class="ion-no-padding">
        <ion-label class="startlabel">{{ viewTitle }}</ion-label>
        <ion-button class="block" fill="clear" (click)="back()">
            <ion-icon class="swipe" name="arrow-back" slot="icon-only"></ion-icon>
        </ion-button>
        <ion-button class="block" fill="clear" (click)="next()">
            <ion-icon class="swipe" name="arrow-forward" slot="icon-only"></ion-icon>
        </ion-button>
    </ion-item>
        <calendar
                id="calendar"
                [eventSource]="eventSource"
                [calendarMode]="calendar.mode"
                [currentDate]="calendar.currentDate"
                (onTitleChanged)="onViewTitleChanged($event)"
                (onTimeSelected)="onTimeSelected($event)"
        ></calendar>
    <ion-item lines="none">
        <ion-label position="fixed">End</ion-label>
        <ion-datetime slot="end" placeholder="{{endDate | date: 'HH:mm'}}" displayFormat="HH:mm" min="1990" (ionChange)="updateEnd(this.endUpdate)" [ngModel]="endUpdate" minuteValues="0,5,10,15,20,25,30,35,40,45,50,55"></ion-datetime>
    </ion-item>
</ion-content>

SCSS

* {
  color: #e6e6e6;
  --color: #e6e6e6;
  --background: #161616;
}
ion-toolbar {
  --background: #466c46;
  color: #e6e6e6;
}
ion-button {
  --background: 0;
}
:host ::ng-deep {
  .event-detail-container {
    display: none;
  }
  .monthview-container {
    height: auto !important;
  }
  .monthview-primary-with-event {
    background-color: white !important;
  }
  .monthview-selected {
    background-color: #578054 !important;
  }
  .monthview-primary-with-event, .calendar-event-inner{
    background-color: rgba(30, 44, 29, 0.86) !important;
    &.monthview-selected {
      background-color: rgba(87, 128, 84, 0.64) !important;
    }
  }
  .monthview-current {
    background: rgba(255, 255, 255, 0.14)!important;
    &.monthview-selected {
      background-color: #578054 !important;
    }
  }
  td, th {
    border: 0 !important;
  }
  .monthview-datetable {
    border: 1px solid rgba(36, 36, 36, 0.33)!important;
    background: rgba(52, 52, 52, 0.14)!important;
  }
  .event-detail-container { //De events display
    display: none!important;
  }

}
ion-datetime {
  text-align: right;
  padding: 0;
}
ion-content {
  margin: 5%;
}
ion-toggle {
  --background: #252525;
  --background-checked: #578054;
}
#hidden, #calendar {
  display: none;
}
ion-icon.swipe {
  color: #578054;
}
ion-item, ion-input, :host ::ng-deep.monthview-container {
  --background: #1a1a1a;
  background: #1a1a1a;
}
ion-item {
  border-bottom: 1px solid rgba(118, 118, 118, 0.81);
  //margin: 0 4% 0 5.5%;
}
ion-input {
  --padding-top: 2px!important;
  --padding-bottom: 4px!important;
}
h5 {
  font-size: 17px;
}
#calendar {
  width: 90%;
  margin: auto 5% 5% 5%;
}
.start {
  //margin: 0 4% 0 5.5%;
  border-top: 1px solid rgba(118, 118, 118, 0.81);
}
.startlabel {
  padding-left: 16px;
}

The red surrounded line is the issue. Is there a way to remove it? Screenshot_1

twinssbc commented 3 years ago

@Quentius If I understand correctly, you mean you can't use the arrow button to change the month to previous/next month? I tried your code, it seems working. I notice you did a lot of pure JavaScript DOM manipulations, I suggest not to do that as Angular may not notice these changes. You also override some CSS styles, try to remove them to see if it works. If you want to hide the event detail section, there's a showEventDetail option.

davidecampello commented 3 years ago

Hello! I have the same problem. I'm not doing any manual dom manipulation and I'm using the component inside a modal... Have you found a solution? I can try to provide a demo in case. The problem for me appear after the second time I open the modal

davidecampello commented 3 years ago

Maybe the problem is that the calendar store some data into a service and they are not reinitialized when a component is destroied and recreated?

davidecampello commented 3 years ago

Hello, i created a small stackblitz example to show the problem: https://stackblitz.com/edit/ionic-5-angular-10-start-template-x8pmlu?file=src/app/tab1/calendar-modal.component.ts Calendar work fine the first time but after it show a wrong month and slides work in "free" mode. Thanks for the help

davidecampello commented 3 years ago

Hi @twinssbc! Can you please look if the problem is simple to solve now that there is a stackbliz? Many Thanks for your time

Quentius commented 3 years ago

Screencast-from-29-07-21-15_57_30 Here is a video of my issue. If i open the modal i cant select a date without first swiping to the next month.

twinssbc commented 3 years ago

@davidecampello @Quentius The reason because of this issue is the calendar was loaded before the page is fully rendered. To solve it, you could add ngif to hide the calendar at the beginning, and set it to true in the ionViewDidEnter event.

For example,

This is the template, notice I use showCalendar to control the visibility

    <ion-header>
      <ion-toolbar>
        <ion-title>{{ viewTitle }}</ion-title>
        <ion-buttons slot="end">
          <ion-button (click)="today()">
            Today
          </ion-button>
          <ion-button (click)="changeMode('month')">M</ion-button>
          <ion-button (click)="changeMode('week')">W</ion-button>
          <ion-button (click)="changeMode('day')">D</ion-button>
          <ion-button (click)="loadEvents()">Load Events</ion-button>
          <ion-button (click)="close()">close</ion-button>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>

    <ion-content *ngIf="showCalendar">
    <calendar
        [calendarMode]="calendar.mode"
        [currentDate]="calendar.currentDate"
        (onCurrentDateChanged)="onCurrentDateChanged($event)"
        (onEventSelected)="onEventSelected($event)"
        (onTitleChanged)="onViewTitleChanged($event)"
        (onTimeSelected)="onTimeSelected($event)"
        [step]="calendar.step"
      >
      </calendar>
    </ion-content>

Then in the component ts

  showCalendar:boolean;

  ionViewDidEnter() {
    this.showCalendar = true;
  }
davidecampello commented 3 years ago

Thank you very much @twinssbc, I'll try and let you know

cfirmo33 commented 3 years ago

@davidecampello @Quentius

  showCalendar:boolean;

  ionViewDidEnter() {
    this.showCalendar = true;
  }

Thanks guys, it´s worked fine.

sriptter commented 3 years ago

@davidecampello @Quentius

  showCalendar:boolean;

  ionViewDidEnter() {
    this.showCalendar = true;
  }

Thanks guys, it´s worked fine.

Hi. this works also for me but i'm facing another issue which is i can't acess the calendar programaticaly in the ts file using viewchild

zzyong666 commented 2 years ago

image

ngAfterViewInit(){ setTimeout(() => { this.myCalendar.update(); }) }

Use this method after init. it´s worked fine.