4

I want to use ngb-datepicker in material Input . I applied datepicker to mat input successfully. but there is an issue with month and year dropdowns.

I tried following snippet :

<mat-form-field>
<input matInput placeholder="Select Valid Till" name="GodownValidTill" [(ngModel)]="GodownValidTill" ngbDatepicker #d2="ngbDatepicker" required readonly>
<mat-icon style="float:right;height:20px;width:20px;" (click)="d2.toggle()" svgIcon="calendar" title="Calendar"></mat-icon>
</mat-form-field>

Is there any solution for perfect working ?

Tejas
  • 381
  • 1
  • 7
  • 25

3 Answers3

8

I tried to reproduce the issue and found that by default ngbDatePicker is appends just below the input field it is attached to. And when we use matInput, the actual input field gets wrapped around various material classes which are causing the problem with the date picker.

So, what we can do is, we can append the date picker to the body using the container property of ngbDatepicker like this. container property currently only supports "body" as a value.

    <mat-form-field>
            <input matInput type="text" ngbDatepicker container="body" #d="ngbDatepicker" (click)="d.open()" placeholder="Date Picker with issue"/>
    </mat-form-field>

here is the stackblitz demo for the same. I hope this will help.

HirenParekh
  • 3,655
  • 3
  • 24
  • 36
2

I am not sure what exactly is the problem, but I have no problem integrating both libraries together. I have used almost the exact same code as you.

<mat-form-field class="example-full-width">
  <input matInput placeholder="Favorite food"  name="GodownValidTill" [(ngModel)]="GodownValidTill" (ngModelChange)="onSelect($event)" ngbDatepicker #d="ngbDatepicker" required readonly>
  <mat-icon (click)="d.toggle()">calendar-today</mat-icon>
</mat-form-field>

Functionality wise, there are no issues. But of course, you will need to make your own changes to the CSS/styling. I have reproduced a demo over here.

Instead of using both libraries together, why not just use either Angular Boostrap, or Angular Material? This way, you dont need to manage the different packages. Both libraries have their own Datepicker components (Material Datepicker/Ngbootstrap Datepicker).

wentjun
  • 40,384
  • 10
  • 95
  • 107
  • Thanks for fast reply .... please check your demo...I think you have noticed the issue in month and year dropdown...so just tell me whether it has a solution ?...In fact I dont like mat-datepicker ...so I want to use ngb-datepicker – Tejas Jun 12 '19 at 12:57
  • @Tejas sorry mate, but may I know what is the exact issue you are facing regarding the month and year dropdown? I have no issues on my end, as i can select and use the dates/months – wentjun Jun 12 '19 at 14:08
  • Kindly click on June Month or 2019 Year and then select another month or year....You will not be able to select different month eg. May or Dec. – Tejas Jun 13 '19 at 05:00
  • the most closer than I get is a custom header, see my stackblitz in https://stackblitz.com/edit/angular-z2zfjx-rxync5?file=app%2Fdatepicker-custom-header-example.ts – Eliseo Oct 06 '19 at 23:48
1

Another aproach is formated the material datepicker. see the final result

The first step is create a custom Header,

<div class="mat-calendar-header">
  <div class="mat-calendar-controls">
    <button mat-icon-button type="button" class="mat-calendar-previous-button arrow"
            [disabled]="!previousEnabled()" (click)="customPrev()"
            [attr.aria-label]="prevButtonLabel">
    </button>
    <select [ngModel]="dateMonth" (ngModelChange)="dateMonth=$event;select()"class="custom-select">
    <option *ngFor="let month of months;let i=index" [value]="i">{{month}}</option>
    </select>
    <select [ngModel]="dateYear" (ngModelChange)="dateYear=+$event;select()" class="custom-select" style="width:70%" >
    <option *ngFor="let i of [-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10]" [value]="dateYear+i">{{dateYear+i}}</option>
    </select>
    <button mat-icon-button type="button" class="mat-calendar-next-button arrow"
            [disabled]="!nextEnabled()" (click)="customNext()"
            [attr.aria-label]="nextButtonLabel">
    </button>
  </div>
</div>

With a .css to take a look similar to ngb-datepicker

.custom-select {
    display: inline-block;
    width: 100%;
    height: calc(1.5em + .75rem + 2px);
    padding: .375rem 1.75rem .375rem .75rem;
    font-size: .8rem;
    font-weight: 200;
    line-height: 1.5;
    color: #495057;
    vertical-align: middle;
    background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right .75rem center/8px 10px no-repeat #fff;
    border: 1px solid #ced4da;
    border-radius: .25rem;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
}
.arrow{
  margin-top: -.25rem;
}

If our header extends MatCalendarHeader, we can implements OnInit -to put the value to the combo-box of Year and Month, and add ElementRef y Renderer2. I use Renderer2 to add/remove the class removeRow

.removeRow tbody tr:first-child td{
  display:none
}

This allow us to not display the first row when the date of the first day of the month is Sundoay,Monday or Thrusday -in the material-date-picker it show the month, but we are showing it in the combo-box.

export class ExampleHeader extends MatCalendarHeader<any> implements OnInit {
  calendar:any
  months=[
    "January","February","March","April","May","June","July","August","September","October","November","December"]

  dateYear:any
  dateMonth:any

  constructor(_intl:MatDatepickerIntl,
      _calendar: MatCalendar<any>, _dateAdapter: DateAdapter<any>,
      @Inject(MAT_DATE_FORMATS) _dateFormats: MatDateFormats, cdr: ChangeDetectorRef,private el:ElementRef,private render:Renderer2){
    super(_intl,_calendar,_dateAdapter,_dateFormats,cdr)
        this.calendar=_calendar;
        this.dateYear=this.calendar.activeDate?this.calendar.activeDate.getFullYear():new Date().getFullYear()
        this.dateMonth=this.calendar.activeDate?this.calendar.activeDate.getMonth():new Date().getMonth()
  }
  select()
  {
    this.calendar.activeDate=new Date(this.dateYear,this.dateMonth)
    this.refreshCombo(this.dateMonth,this.dateYear)
  }
  ngOnInit()
  {
    this.refreshCombo(this.calendar.activeDate.getMonth(),this.calendar.activeDate.getFullYear())
  }
  refreshCombo(month,year)
  {
     this.dateMonth=month;
     this.dateYear=year;
     const element=this.el.nativeElement.parentElement
     if (new Date(year,month,1).getDay()<=2)
      this.render.addClass(element,'removeRow')
     else
      this.render.removeClass(element,'removeRow')
  }

  /** Handles user clicks on the previous button. */
  customPrev(): void {
    this.previousClicked()
    this.refreshCombo(this.calendar.activeDate.getMonth(),this.calendar.activeDate.getFullYear());
  }

  /** Handles user clicks on the next button. */
  customNext(): void {
    this.nextClicked()
     this.refreshCombo(this.calendar.activeDate.getMonth(),this.calendar.activeDate.getFullYear());
 }
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • thanks @Eliseo ,this also can be a correct solution...but if we just put container attribute in input tag for correct result then we don't need to write huge code for custom controls – Tejas Oct 09 '19 at 05:59