When using ion-segment, ion-segment-button with ngFor,
Once the length of dataArray are increased, the
ion-segment-button corresponded increased entries of dataArray
can not be clicked.
<ion-segment [(ngModel)]="valve.selectDay">
<ion-segment-button *ngFor="let info of dataArray" value="{{info.day}}">{{info.day+1}}天</ion-segment-button>
</ion-segment>
Once push entries into the end of dataArray
,the newly ion-segment-button can be click with selection properly.
Hello, thanks for opening an issue with us! Would you be able to provide a plunker that demonstrates this issue? Thanks for using Ionic!
@jgw96 Here is the demo,
http://plnkr.co/edit/Or0Ik1S1HtvUVq3YYdhC
Once you change the period days from 3 days
to 4 days
.
Click the 4TH DAY
would have no effect.
As a workaround I manually trigger rebinding logic of Segment component after updating my view state:
import {Segment} from 'ionic-angular';
@Component()
export class MyComponent {
@ViewChild(Segment)
private segment: Segment;
private ngOnInit() {
this.fetchData().then(data => {
// Data processing...
// Hotfix for dynamic ion-segment-buttons issue
setTimeout(() => {
if (this.segment) {
this.segment.ngAfterViewInit();
}
});
});
}
}
@mnasyrov Can you modify your hot fix works with the fork of
http://plnkr.co/edit/Or0Ik1S1HtvUVq3YYdhC
@lygstate it's a rather trivial, I just copy-pasted the workaround above. http://plnkr.co/edit/GyCLkQ0aFgFpPr9QUSiO
👍
Is it hard to fix it?
still open? :-( i've just noticed this bug also both:
it works normally if instead i use static HTML instead of an *ngFor loop
+1
+1
+1
I ran into the same problem and solved it by adding a click event like this:
<\/ion-segment>
and then you can define the setInfo() function and a selectedDay variable at the component as something like:
selectedDay;
if (dataArray[0].info.day != nil) {
selectedDay = dataArray[0].info.day;
}
...
setInfo(day) {
selectedDay = day+1;
}
I am not really sure how your code is since the demo is different than the original problem, but the logic behind is adding a click event in order to change the segmented button content so that it matches the button's value.
Hi everyone,
I found a more simplest workaround for this bug. You can add a ngIf
on the ion-segment
like the following :
<ion-segment *ngIf="businessCollection" [(ngModel)]="businessId">
<ion-segment-button *ngFor="let business of businessCollection" value="{{ business.id }}">{{ business.title }}</ion-segment-button>
</ion-segment>
cacheService.getAllBusiness().subscribe(data => this.businessCollection = data);
As the DOM is rebuilded only when the businessCollection
array is filled (in the subscribe), it seem to fix the issue.
@blckshrk this gives me an exception:
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'
@mebibou Can't help you without the code ;-)
The main problem with ion-segment and *ngFor disappear about 6 weeks ago with ionic actualization. Latest version also works well. Can you close the issue?
Still have the issue with dynamically rebuilding ion-segment-button
items in Ionic v3.2.1.
Example:
<ion-segment [(ngModel)]="groupIndex">
<ng-template ngFor [ngForOf]="groups" let-group let-index="index">
<ion-segment-button [value]="index">
{{group.title}}
</ion-segment-button>
</ng-template>
</ion-segment>
As workaround I use a directive which fix all segments in an app:
import {ContentChildren, Directive, QueryList, Self} from '@angular/core';
import {Segment, SegmentButton} from 'ionic-angular';
import {Subscription} from 'rxjs';
// TODO: Hotfix for dynamic ion-segment-buttons issue (https://github.com/driftyco/ionic/issues/6923).
@Directive({
selector: 'ion-segment'
})
export class IonSegmentHotfix {
private subscriptions: Subscription;
@ContentChildren(SegmentButton)
buttons: QueryList<SegmentButton>;
constructor(@Self() private segment: Segment) {
}
ngAfterContentInit() {
this.subscriptions = this.buttons.changes.subscribe(() => this.onChildrenChanged());
}
ngOnDestroy() {
if (this.subscriptions) {
this.subscriptions.unsubscribe();
this.subscriptions = null;
}
}
private onChildrenChanged() {
setTimeout(() => {
this.segment.ngAfterContentInit();
this.segment._inputUpdated();
});
}
}
@mnasyrov thanks for this, this.segment._inputUpdated();
will also update the selected tab, was missing this one
Ran into similar problem. In my case, I have to dynamically create segment buttons after fetching some date from backend, so basically my code looks like
ionViewDidLoad() {
this.service.get().subscribe(
res => { this.segmentArray = res; this.selectedSegment = segmentArray[0]; }
);
<ion-segment [(ngModel)]="selectedSegment">
<ion-segment-button *ngFor="let index of segmentArray" value="{{segmentArray[index]}}">
segmentArray[index]
</ion-segment-button>
</ion-segment>
Then I cannot properly select segment button which didn't show correct appearance
None of above workaround fix my issue
I'm on
global packages:
@ionic/cli-utils : 1.4.0
Cordova CLI : 7.0.1
Ionic CLI : 3.4.0
local packages:
@ionic/app-scripts : 1.3.7
@ionic/cli-plugin-cordova : 1.4.0
@ionic/cli-plugin-ionic-angular : 1.3.1
Cordova Platforms : ios 4.4.0
Ionic Framework : ionic-angular 3.4.0
System:
Node : v6.11.0
OS : macOS Sierra
Xcode : Xcode 8.3.3 Build version 8E3004b
ios-deploy : 1.9.1
ios-sim : 5.0.13
npm : 3.10.10
My temp solution
<ion-segment *ngIf="generatedArray">
<ion-segment-button *ngFor="let item of generatedArray" value="{{generatedArray.someValue1]}}" (tap)="contendChanged(generatedArray.someValue1)">
generatedArray.someValue2
</ion-segment-button>
</ion-segment>
(tap) work and you can send data to a function.
in ionic V3.6 Segment class has no more function ngAfterViewInit()。 Use ngAfterContentInit() instead.
If you use an ngFor or *ngIf structural directive to generate your <ion-segment-button>
elements, and you change either:
1) the number of buttons in the segment or
2) their values ...
The result is that it *appears as if you cannot select updated segment buttons ...
But you can - it all works fine except for applying the segment-activated
CSS class to the selected button and removing it from all the others. The options, data model, and DOM all update fine, which is why none of my messing w/ngOnChanges
or ChangeDetectionRef .detectChanges()
had any impact - everything was already up to date except for the CSS!
The segment-activated
class gets stuck on whichever element existed in the previous list, if any.
If your updated segment data source has identical values & number of buttons, no problem.
But, if all the segment values change, the segment-activated
class won't appear for any of them.
After waaay too long screwing around with this, I finally hacked together this ugly approach to fix the problem, and it'll do since I've wasted so much time on it ...
<ion-segment formArrayName="segmentArray">
<ion-segment-button *ngFor="let option of options; let i=index;" [value]="i" (tap)="setOption(i, $event)">{{option}}</ion-segment-button>
</ion-segment>
public setOption(index, event) {
if (this.options[index] != null) {
this.selectedSegment = this.options[index];
//note you have to use "tap" or "click" - if you bind to "ionSelected" you don't get the "target" property
let segments = event.target.parentNode.children;
let len = segments.length;
for (let i=0; i < len; i++) {
segments[i].classList.remove('segment-activated');
}
event.target.classList.add('segment-activated');
}
It's ugly & ol'skool, but it gets the job done and I have no more time to make it fancy ...
Would be nice if somebody could take a look at how the CSS classes get applied after changes to the <ion-segment>
component and fix this.
Thanks ~
@mccumb01 thank you so much
I had to do a more RxJS-friendly version which I based on @mccumb01's excellent comment. Rather than event.target
to get the segment, I use ViewChild
. Below is the general idea. Who's looking forward to a new Stencil segment?
<ion-segment #mySegment>
<ion-segment-button
*ngFor="let segment of segments$ | async; let i = index; trackBy:trackByFn;"
(click)="selectedCpmIntSubject.next(i)">
{{segment.title}}
</ion-segment-button>
</ion-segment>
@ViewChild('mySegment') mySegment: ElementRef;
//...
selectedSegmentSubject = new BehaviorSubject(null);
selectedSegment$ = this.selectedSegmentSubject
.distinctUntilChanged()
.do(selectedSegment => {
// do stuff
});
//...
setSegment(selectedIndex) {
const segments: any[] = Array.from(
this.mySegment.nativeElement.children
);
segments.forEach( (segment, segmentIndex) => {
if (segmentIndex === selectedIndex) {
segment.classList.add('segment-activated');
} else {
segment.classList.remove('segment-activated');
}
});
}
//...
trackByFn = (idx, obj): string => obj.$key;
Still not fixed? We run into this issue in every project and it seems very sillier and sillier to employ the above workarounds again and again.
+1
Please Try with [id]="mcq.ques_id" value="{{mcq.ques_id}}" and (ionSelect)
<ion-segment-button *ngFor="let mcq of mcqdata; let i=index;"
[id]="mcq.ques_id" value="{{mcq.ques_id}}" (ionSelect)="selectsegment(mcq,i)" [ngClass]="{'active': selectedIdx == i}">
<strong>Queastion{{i+1}}</strong>
</ion-segment-button>
Ts
And call selectsegment first time with index 0 in constructor for page loads first time for ngClass and buttom changed accordint to you scss class
this.selectsegment(mcq,0)
then on click segment button
selectsegment(mcq,index){
this.selectedIdx = index;
}
scss
You can use ngClass for button active by and used according to active index
.active{
font-size: 13px;
opacity: 1;
color: color($colors, appmaincolor) !important;
font-weight: bold;
border-bottom: 5px solid color($colors, appmaincolor) !important;
}
@mccumb01
Thanks for your solution.The only issue i am facing right now is i am unable to make first ionic segment button active.After click it's working properly.
How can i make first segment button active when page loads first time.
Mycode:
<ion-toolbar color="tabbarcolor" *ngIf="tabBarStatus">
<ion-segment [(ngModel)]="selectedcategory" >
<ion-segment-button (click)="setOption(i, $event)" [value]="i" *ngFor=" let category of displayingCategories; let i=index; " [style.width]="category.width" [style.opacity]="getOpacity(category.status)">
{{category.category}}
</ion-segment-button>
</ion-segment>
</ion-toolbar>
+1
2018 and still exists :/
+1
Thanks for the issue and sorry for the lack of response! This issue has recently been fixed in Ionic for the v4 release. I just tested it on our core
branch and I can see it working now:
I'll try to come back and update this issue when we have something for you all to test out.
Related commits:
https://github.com/ionic-team/ionic/commit/242960dc29e9efdc180108925a45680cb3191a7a
https://github.com/ionic-team/ionic/commit/1e6e481958fea99e5c29628d0339cccde1305302
And when do you plan to release v4 with this fix?
Could one apply the fix before v4 release? If so, how?
Ran into the same problem.
Weird that such issue still exists after 2 years... let's hope it's fixed soon!
This has been fixed as part of Ionic v4. I will update the issue when we have a version out that is ready to be tested. Locking for now.
Version 4 beta has been released, for more information please see this blog post: https://blog.ionicframework.com/announcing-ionic-4-beta/
Closing this issue as resolved with v4, thank you!
Most helpful comment
Hi everyone,
I found a more simplest workaround for this bug. You can add a
ngIf
on theion-segment
like the following :As the DOM is rebuilded only when the
businessCollection
array is filled (in the subscribe), it seem to fix the issue.