Is it possible to change html segment template depending which segment button is clicked?


#1

When segment button is clicked I would like to get data from some other html and add it to the the segment html.

Is it something that is possible? I had a look at the documentation page, however, I could not find anything related to this?

  <ion-segment [(ngModel)]="segment" >
    <ion-segment-button value="pain" (ionSelect)="goToPainPage()">Pain</ion-segment-button>
    <ion-segment-button value="anxiety" (ionSelect)="goToAnxietyPage()">Anxiety</ion-segment-button>
  </ion-segment>

#2

Can you achieve what you’re trying to do with ngIf?


#3

What I’m trying to achieve is when a user clicks on a segment button, on the same page just after the segment buttons an html sections is displayed.

Perhaps, trying to achieve something like with modals, just on the same page with segments buttons visible.

I’m not sure where I could use ngIf?


#4

Since you appear to be using the segment buttons for navigation, I’m not sure if what I am thinking will work in your case, but here’s how I do something similar to GitHub’s preview function:

 <template [ngIf]="viewMode === 'writing'">
    <ion-item>
      <ion-textarea [(ngModel)]="body"></ion-textarea>
    </ion-item>
  </template>

  <template [ngIf]="viewMode === 'preview'">
    <ion-item>
      <div item-left>
        <md-comment-body [mdsrc]="body"></md-comment-body>
      </div>
    </ion-item>
  </template>
</ion-list>

<ion-segment [(ngModel)]="viewMode">
  <ion-segment-button value="writing">
    <ion-icon name="create"></ion-icon>
    書く
  </ion-segment-button>
  <ion-segment-button value="preview">
    <ion-icon name="eye"></ion-icon>
    試写
  </ion-segment-button>
</ion-segment>

From the user’s perspective, the toggle buttons cause the write and preview part of the DOM to appear to swap, which sounds somewhat similar to what you are seeking.


#5

You are absolutely right, that is exactly what I am trying to do!

I modified your code for it to fit my problem and got the following:

 <ion-content padding>

   <ion-segment [(ngModel)]="segment" >
     <ion-segment-button value="pain" (ionSelect)="goToPainPage()">Pain</ion-segment-button>
     <ion-segment-button value="anxiety" (ionSelect)="goToAnxietyPage()">Anxiety</ion-segment-button>
   </ion-segment>

<template [ngIf]="segment === 'pain'">
  <ion-card>
    <ion-card-content>
      <canvas width = "200" height = "140" #lineCanvas></canvas>
    </ion-card-content>
  </ion-card>

  <ion-list>
    <ion-item-sliding *ngFor="let record of painRecords">
        <ion-label id = 'painScore'>{{record.painScore}}</ion-label>
        <ion-item>{{record.date}}</ion-item>

        <ion-item-options side="right">
          <button ion-button color="danger" (click)="delete(item)">Delete</button>
        </ion-item-options>
    </ion-item-sliding>
  </ion-list>
 </template>


 <template [ngIf]="segment === 'anxiety'">

 </template>


</ion-content>

However I get the following error:
EXCEPTION: Uncaught (in promise): TypeError: undefined is not an object (evaluating ‘_this.lineCanvas.nativeElement’)

When I close error dialog, the solutions seems to be exactly what I need.

Do you have any idea about the error message?

I was not getting the error message without the buttons.

delete(item) is not yet implemented.


#6

Yes, that’s one of those tricky Angular pitfalls. I assume you’re using @ViewChild to try to grab that canvas element. You can’t do that with a normal property if the item in question is coming and going via ngIf. I believe you can work around it using a custom setter, something like this:

private _painLineCanvas: ViewContainerRef;

@ViewChild('lineCanvas') set painLineCanvas(plc: ViewContainerRef) {
  this._painLineCanvas = plc;
}

#7

I tried, however, still getting the same error.


#8

It works for me, so perhaps it would help to see how you are using that canvas on the TypeScript end.


#9

private _painLineCanvas: ViewContainerRef;

  @ViewChild('lineCanvas') set painLineCanvas(plc: ViewContainerRef) {
    this._painLineCanvas = plc;
  }

  lineChart: any;
  painRecords = [];

  constructor(public navCtrl: NavController,
              public storage: Storage,
              public dateProvider: DateProvider) {


  }

  getReports() {
    return this.storage.get('painRecords').then(data => {
        return JSON.parse(data);
    });
  }

  ionViewDidEnter() {
    Promise.all([this.getReports()]).then(records => {
      let labels:any[] = [];
      let painScores:any[] = [];

      let painRecords = records[0];

      for(let i = 0; i < painRecords.length; i++){
        let date: Date = new Date(painRecords[i].date);
        labels.push(date.getDate() + " " + this.dateProvider.returnMonth(date.getMonth()));
        painScores.push(Number(painRecords[i].painScore));
      }

      this.lineChart = new Chart(this. _painLineCanvas.nativeElement, {
       // parameters for the chart
    })

    this.storage.get('painRecords').then(data => {
      let records = JSON.parse(data);
      this.painRecords = [];

      for(let i = records.length - 1; i >=0; i--){
        let date = records[i].date;
        records[i].date = this.dateProvider.getDate(date);
        this.painRecords.push(records[i]);
      }
    })
  }

I forgot to change lineCanvas to _painLineCanvas. However when I did, I got another error: Property ‘nativeElement’ does no exist on type ‘ViewContainerRef’. This was type error in my editor.


#10

Got it. ionViewDidEnter is not the right place to be trying to access the canvas (it may not exist in the DOM then). Try moving the creation of lineChart out of there and into the painLineCanvas setter like so:

@ViewChild('lineCanvas') set painLineCanvas(plc: ViewContainerRef) {
  this._painLineCanvas = plc;
  if (plc) {
    this.lineChart = new Chart(plc.getNativeElement(), {
       // parameters for the chart
    });
  }
}

#11

Will my canvas be created/updated every time I enter the view though? That’s exactly what I need.

The reason why I placed my canvas there is because I had troubles updating it, so I though I would create it every time I enter the view.


#12

More often. Every time the pain segment is activated. If that’s not what you want, you could probably switch from using ngIf to binding [hidden] instead (or some class with display: none). I expect that would keep both the pain and anxiety segments (and whatever others you have) always in the DOM.


#13

Try ionViewDidLoad instead.