Is there any way to control scrolling on segments? In my case the slider and segments depends on each other and when you swipe slides, overflowwing segments does not slide, but active segment will be correctly choosed
My view and controller code:
<ion-segment scrollable mode="md" (ionChange)="segmentChanged()" [(ngModel)]="segment" color="warning">
<ion-segment-button mode="md" value="0">
<p>Description</p>
</ion-segment-button>
<ion-segment-button mode="md" value="1">
<p>Interconnections</p>
</ion-segment-button>
<ion-segment-button mode="md" value="2">
<p>Declensions</p>
</ion-segment-button>
</ion-segment>
<ion-slides (ionSlideDidChange)="slideChanged()" pager="true">
<ion-slide>
First
</ion-slide>
<ion-slide>
second
</ion-slide>
<ion-slide>
third
</ion-slide>
</ion-slides>
segmentChanged() {
this.slider.slideTo(this.segment);
}
async slideChanged() {
this.segment = await this.slider.getActiveIndex();
}
The segment itself works fine, but when swiping the active segment goes behind the screen.
I am also facing the same issue and finally solved the issue with below code.
you must define the unique "id" for each "ion-segment-button"
<ion-segment-button SwipedTabs *ngFor="let category of tabs ; let i = index"
value={{category}} (click)="selectTab(i)" **id="segment-{{i}}**">
<ion-label>{{category}}</ion-label>
</ion-segment-button>
.ts code
async slideChanged() {
this.activeIndex= await this.slider.getActiveIndex();
document.getElementById("segment-" + activeIndex).scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});
}
hope it helps.
For anyone who is looking for Ionic + React + TypeScript solution:-
const [selectedTab, setSelectedTab] = useState<string>("0");
const sliderRef = useRef<HTMLIonSlidesElement>(null);
function onTabChange(value: string) {
if (value === "" || value == null || value === undefined) return;
sliderRef.current?.slideTo(parseInt(value));
}
async function onSlideChange() {
let slideIndex = await sliderRef?.current?.getActiveIndex();
if(slideIndex == null || slideIndex === undefined)
return;
setSelectedTab(slideIndex.toString());
document.getElementById("tab-" + slideIndex)?.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center",
});
}
<IonSegment scrollable value={selectedTab} onIonChange={(e) => onTabChange(e.detail.value ?? "")}>
<IonSegmentButton value="0" id="tab-0">
<IonLabel>Tab 0</IonLabel>
</IonSegmentButton>
....
</IonSegment>
<IonSlides ref={sliderRef} options={slideOpts} onIonSlideDidChange={() => {onSlideChange();}}>
<IonSlide>
<h1>Slide 0</h1>
</IonSlide>
....
</IonSlides>
document.getElementById("your-id").scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});
<ion-segment scrollable [(ngModel)]="currentTab">
<ion-segment-button value="subject" id="subject">
<ion-label>ASSUNTO</ion-label>
</ion-segment-button>
<ion-segment-button value="book" id="books">
<ion-label>LIVROS</ion-label>
</ion-segment-button>
</ion-segment>
Works great!
Interesting question.
It's not supported out of the box. I'm not sure if it's possible to do because, as I understand it, there is no way to get at the insides of the ion-segment web component unless there is an API exposed.
This encapsulation is how the components can be dropped into the page and not conflict with other stuff.
If it's super important to your project you could consider creating your own segment from copying the code of the segment and segment-button components into your own separate version?
Check this out, it should help Ionic 4 Segments and Slides
The .html can be like this:
<ion-app>
<ion-header>
<ion-toolbar color="primary">
<ion-title>Ionic 4 Segment</ion-title>
</ion-toolbar>
<ion-toolbar color="primary">
<ion-segment scrollable>
<ion-segment-button value="0" checked>
<ion-icon name="call"></ion-icon>
<ion-label>Call</ion-label>
</ion-segment-button>
<ion-segment-button value="1">
<ion-icon name="cloud-upload"></ion-icon>
<ion-label>Publish</ion-label>
</ion-segment-button>
<ion-segment-button value="2">
<ion-icon name="paper"></ion-icon>
<ion-label>Topic</ion-label>
</ion-segment-button>
<ion-segment-button value="3">
<ion-icon name="code-working"></ion-icon>
<ion-label>Query</ion-label>
</ion-segment-button>
<ion-segment-button value="4">
<ion-icon name="open"></ion-icon>
<ion-label>Open</ion-label>
</ion-segment-button>
<ion-segment-button value="5">
<ion-icon name="search"></ion-icon>
<ion-label>Search</ion-label>
</ion-segment-button>
<ion-segment-button value="6">
<ion-icon name="create"></ion-icon>
<ion-label>Write</ion-label>
</ion-segment-button>
<ion-segment-button value="7">
<ion-icon name="book"></ion-icon>
<ion-label>Read</ion-label>
</ion-segment-button>
<ion-segment-button value="8">
<ion-icon name="trash"></ion-icon>
<ion-label>Trash</ion-label>
</ion-segment-button>
</ion-segment>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-slides>
<ion-slide class="slide-1">
Call
</ion-slide>
<ion-slide class="slide-2">
Publish
</ion-slide>
<ion-slide class="slide-3">
Topic
</ion-slide>
<ion-slide class="slide-1">
Query
</ion-slide>
<ion-slide class="slide-2">
Open
</ion-slide>
<ion-slide class="slide-3">
Search
</ion-slide>
<ion-slide class="slide-1">
Write
</ion-slide>
<ion-slide class="slide-2">
Read
</ion-slide>
<ion-slide class="slide-3">
Trash
</ion-slide>
</ion-slides>
</ion-content>
</ion-app>
Then the js like this
var segment = document.querySelector('ion-segment');
var slides = document.querySelector('ion-slides');
segment.addEventListener('ionChange', (ev) => onSegmentChange(ev));
slides.addEventListener('ionSlideDidChange', (ev) =>
onSlideDidChange(ev));
// On Segment change slide to the matching slide
function onSegmentChange(ev) {
slideTo(ev.detail.value);
}
function slideTo(index) {
slides.slideTo(index);
}
// On Slide change update segment to the matching value
async function onSlideDidChange(ev) {
var index = await slides.getActiveIndex();
clickSegment(index);
}
function clickSegment(index) {
segment.value = index;
}
I hope this helps anyone
Related
I am learning ionic. So i have added data to an array declared in a service. When i go back to the lists page, ngOnInit will not execute if i have been to that page before. To load the data i need to use ionViewWillEnter or ionViewDidEnter. However, when i move the function call to the ionViewWillEnteri get the following error:
ERROR TypeError: Cannot read property '0' of undefined
at DiscoverPage_Template (template.html:39)
at executeTemplate (core.js:9544)
at refreshView (core.js:9413)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
at refreshView (core.js:9463)
at refreshEmbeddedViews (core.js:10533)
at refreshView (core.js:9437)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
The only way i can get rid of the above error is by calling the function in both ngOnInIt and ionViewWillEnter.
loadedPlaces: Place[];
listedLoadedPlaces: Place[]; //for virtual scroll
constructor(private placesService: PlacesService, private menuCntrl: MenuController) { }
ngOnInit() {
console.log('ngOnInIt');
this.loadData();
}
ionViewWillEnter(){
console.log('ionViewWillEnter');
this.loadData();
}
loadData(){
this.loadedPlaces = this.placesService.places; //getter property
//for the virtual scroll
this.listedLoadedPlaces = this.loadedPlaces.slice(1);
}
I have also tried declaring an empty array and then removing the function call from ngOnInit. In this case i get following issue:
core.js:6157 ERROR TypeError: Cannot read property 'title' of undefined
at DiscoverPage_Template (template.html:39)
at executeTemplate (core.js:9544)
at refreshView (core.js:9413)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
at refreshView (core.js:9463)
at refreshEmbeddedViews (core.js:10533)
at refreshView (core.js:9437)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
My environment
node: v14.15.5
npm: 6.14.11
angular: 11.2.0
ionic: 6.13.1
How can i solve this issue?
Update:HTML
<ion-header>
<ion-toolbar>
<!--menu drawer-->
<ion-buttons slot="start">
<ion-menu-button menu="menu1"></ion-menu-button>
</ion-buttons>
<ion-title>Discover Places</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-segment value="all" (ionChange)="segmentChanged($event)">
<ion-segment-button value="all">All Places</ion-segment-button>
<ion-segment-button value="bookable">Bookable Places</ion-segment-button>
</ion-segment>
<ion-grid>
<!--open the side drawer manually -->
<ion-row>
<ion-button fill="clear" (click)="onMenuDrawerOpen()">
Menu
<ion-icon slot="start" name="apps-outline"></ion-icon>
<ion-icon slot="end" name="checkmark-done-outline" color="primary"></ion-icon>
</ion-button>
</ion-row>
<!--Featured place-->
<ion-row>
<ion-col size="12" size-sm="8" offset-sm="2" class="ion-text-center">
<ion-card>
<ion-card-header>
<ion-card-title>{{ loadedPlaces[0].title }}</ion-card-title>
<ion-card-subtitle>{{ loadedPlaces[0].price | currency }} / Night</ion-card-subtitle>
</ion-card-header>
<ion-img [src]="loadedPlaces[0].imageUrl"></ion-img>
<ion-card-content>
<div>{{ loadedPlaces[0].description}}</div>
</ion-card-content>
<div class="ion-text-right">
<!--fill Clear mean no background -->
<ion-button fill="clear" color="primary" routerDirection="forward" [routerLink]="['/', 'places', 'tabs', 'discover', 'place', loadedPlaces[0].id, 'detail']">
More
<ion-icon slot="start" name="star"></ion-icon>
<ion-icon slot="end" name="arrow-forward-circle-outline" color="primary"></ion-icon>
</ion-button>
</div>
</ion-card>
</ion-col>
</ion-row>
<!--Other places-->
<ion-row>
<ion-col size="12" size-sm="8" offset-sm="2" class="ion-text-center">
<!--since using scroll, removed the for loop from the ion-item moved it to the ion-virtual-scroll-->
<!--it takes the items property -->
<!--get the approxItemHeight using the dev tools and picking the height of an ion-item. The default is 40px-->
<ion-virtual-scroll [items]="listedLoadedPlaces" approxItemHeight="60px">
<!--<ion-list>-->
<!--Excluding the featured item above-->
<!--<ion-item *ngFor="let place of loadedPlaces.slice(1); let i = index" [routerLink]="['/', 'places', 'tabs', 'discover', 'place', place.id, 'detail']" detail>-->
<ion-item [routerLink]="['/', 'places', 'tabs', 'discover', 'place', place.id, 'detail']"
detail
*virtualItem="let place">
<ion-thumbnail slot="start">
<ion-img [src]="place.imageUrl"></ion-img>
</ion-thumbnail>
<ion-label>
<h2>{{ place.title}}</h2>
<p>{{ place.description }}</p>
</ion-label>
</ion-item>
<!--</ion-list>-->
</ion-virtual-scroll>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
I had to check for the null. Changing to following fixed the issue, did it for all the properties:
loadedPlaces[0]?.title
Folks:
I have an ionic app that has a page where user can post comments on a story. If a user posts new comment, the comment is displayed in the page. Comments are fetched from a REST API using the "let comment of postComments" fragment with *ngFor.
<ion-content *ngIf="!searchUser" #myContent padding [scrollEvents]="true">
<ion-refresher slot="fixed" (ionRefresh)="reloadPage($event)">
<ion-refresher-content pullingIcon="arrow-dropdown" refreshingSpinner="circles" refreshingText="Refreshing..." >
</ion-refresher-content>
</ion-refresher>
<div class="post-content">
<ion-item lines="none" no-padding>
<ion-avatar slot="start">
<img [src]=" (profileUrl == null) ? 'assets/img/emptyperson.jpg' : profileUrl">
</ion-avatar>
<p class="user-post-text">
<strong>{{
username
}}</strong><span>
{{ description }}
</span>
</p>
</ion-item>
<div class="border"></div>
<ion-item-sliding class="comments-container" *ngFor="let comment of postComments; let i = index; let c = count;" [id]="'slidingItem' + i">
<ion-item no-padding lines="none">
<ion-avatar slot="start">
<img [src]=" (comment.profileUrl == null) ? 'assets/img/emptyperson.jpg' : comment.profileUrl">
</ion-avatar>
<p>
<strong>{{
comment.username
}}</strong>
{{ comment.comment }}
</p>
<ion-button
slot="end"
fill="clear"
(click)="likeButton(comment.id, comment.currentUserLike)"
[disabled]="disableLikeButton"
>
<ion-icon
*ngIf="!comment.currentUserLike"
slot="icon-only"
name="heart-outline"
color="black"
></ion-icon>
<ion-icon
*ngIf="comment.currentUserLike"
slot="icon-only"
name="heart"
color="danger"
></ion-icon>
</ion-button>
<br/>
</ion-item>
<ion-item-options *ngIf="isPostMine && (comment.userId != userdetails.id)">
<ion-item-option color="danger" >
<button color="danger" [disabled]="disableDeleteButton" (click)="removeComment(comment.id,i)"><ion-icon class="inside-icon" name="trash"></ion-icon></button>
</ion-item-option>
<ion-item-option color="primary" >
<button color="primary" (click)="reportOffensive(comment.id,i)"><ion-icon class="inside-icon" name="remove-circle"></ion-icon> </button>
</ion-item-option>
</ion-item-options>
<ion-item-options *ngIf="isPostMine && (comment.userId == userdetails.id)">
<ion-item-option color="danger" >
<button color="danger" [disabled]="disableDeleteButton" (click)="removeComment(comment.id,i)"><ion-icon class="inside-icon" name="trash"></ion-icon></button>
</ion-item-option>
</ion-item-options>
<ion-item-options *ngIf="!isPostMine && (comment.userId != userdetails.id)">
<ion-item-option color="primary" >
<button color="primary" (click)="reportOffensive(comment.id,i)"><ion-icon class="inside-icon" name="remove-circle"></ion-icon> </button>
</ion-item-option>
</ion-item-options>
<ion-item-options *ngIf="!isPostMine && (comment.userId == userdetails.id)">
<ion-item-option color="danger" >
<button color="danger" [disabled]="disableDeleteButton" (click)="removeComment(comment.id,i)"><ion-icon class="inside-icon" name="trash"></ion-icon></button>
</ion-item-option>
</ion-item-options>
<p class="all-comments">
<span class="grey-text"> {{ comment.createdAt | timeAgo }}</span>
<span *ngIf="comment.numLikes > 0" class="grey-text tab" (click)="userList(comment.id,'CommentLikes')">{{comment.numLikes}} likes</span>
</p>
</ion-item-sliding>
</div>
</ion-content>
<ion-footer>
<ion-item lines="none">
<ion-avatar slot="start">
<img src="{{ userProfileUrl }}" />
</ion-avatar>
<ion-textarea autocapitalize="sentences" maxLength="255" type="text"
[(ngModel)]="commentContent" (ionChange)="getUsers($event)"
rows="1"
placeholder="Add a comment"
></ion-textarea>
<ion-icon class="blueicon" slot="end" name="send" (click)="addComment()"></ion-icon>
</ion-item>
</ion-footer>
In my typescript, I have a function addComment() which refreshes the value of postComments so that the page automatically reloads.
addComment() {
if (this.commentContent.trim()) {
this.showLoader("Posting Comment ...");
this.userPostComment.comment = this.commentContent;
this.userPostCommentJSON = JSON.stringify(this.userPostComment);
//console.log("Comment JSON is "+this.userPostCommentJSON);
this.https.post(this.serviceUrl+'comments',this.userPostCommentJSON,this.httpOptions)
.subscribe(
(res: any)=>{
this.commentContent = "";
this.refresh();
setTimeout(() => {
console.log("In Timeout");
this.refreshPostComments("");
this.events.publish('postComment:created', {
id: res.id,
time: new Date()
});
this.hideLoader();
this.commentContent = "";
},2000)
},
err => {
this.hideLoader();
this.showAlert(err);
}
);
}
}
This is working like charm, when I get to the Comments page from the story.
Here is my problem. If I navigate to the Comments page from a oneSignal push notification, everything works fine up to the point where I enter a new comment. When I enter a new Comment, the comment is created in the database using the RESTAPI (works fine), the refreshPostComments method is called in typescript (works fine), but my view does not get updated till I physically click the comment box again.
I don't know what I am doing wrong. But in my app.component.ts, I check for oneSignal notification clicked and if clicked, I do this
if (localStorage.getItem('auth-user') != undefined || null) {
if (additionalData.type == "comment") {
await console.log("Going to Comments");
this.router1.navigateForward(['/comments', {
userId: parseInt(additionalData.userId),
username: additionalData.username,
userPostId: parseInt(additionalData.userPostId),
description: additionalData.description,
profileNameKey: additionalData.profileNameKey
}]);
}
That works. Takes me to the Comments Page. But once I am in the page, I can do virtually everything else like peach, except if I add a comment, then it does not reflect in real time.
Any pointers on what I am missing?
JR
Found my solution. I think Angular’s Change detection is triggered by certain events, and when I was navigating to the page from OneSignal's notification - whatever OneSignal was using, it was not getting on Angular's radar. So I had to import ChangeDetectorRef and do a manual detectChanges() to make sure ngFor would detect the changes as soon as they occur.
Phew. Spent almost a half a day on this. So much for OneSignal and Angular's quirks with OneSignal. And Ionic had nothing to do with it.
Regards,
JR
I currently have an Ion-Segment with two switchCases. That is Management & Market. However I want to include a FAB button to run different functions depending on what SwitchCase the user is at.
Ive already tried placing the FAB in the SwitchCase Options,however the FAB seems to be scrolling on with the page instead of sticking.
The fab should run different functions for the different switch cases.
//CODE FOR THE SEGMENT
<ion-header>
<ion-segment [(ngModel)]="options" color="grey">
<ion-segment-button value="Management">
Management
</ion-segment-button>
<ion-segment-button value="Market">
Crop Market
</ion-segment-button>
</ion-segment>
</ion-header>
//CODE FOR IMPLEMENTATION OF THE SEGMENT
<ion-content>
<div [ngSwitch]="options" class="management-class"> //FIRST SWITCH CASE
<div *ngSwitchCase="'Management'">
<//MANAGEMENT CONTENT FOR THIS SWITCH CASE>
</div>
</div>
<div [ngSwitch]="options" class="management-class">//SECOND SWITCH CASE
<div *ngSwitchCase="'Market'">
<//MARKET CONTENT FOR THIS SWITCH CASE>
</div>
</div>
//This is the ion-fab button to that switches functions based on the users current switch case.
<ion-fab right bottom>
<button *ngIf="this.options=Market" ion-fab (click)="addToMarket()" color="primary" mini><img src="assets/imgs/add1.png"></button>
<button *ngIf="this.options=Management" ion-fab (click)="plantNew()" color="primary" mini><img src="assets/imgs/add1.png"></button>
</ion-fab>
</ion-content>
Use ionChange event on your segment like:
<ion-segment (ionChange)="segmentChanged($event)">
<ion-segment-button value="friends">
<ion-label>Friends</ion-label>
</ion-segment-button>
<ion-segment-button value="enemies">
<ion-label>Enemies</ion-label>
</ion-segment-button>
</ion-segment>
in your component.ts
segmentChanged(e){
console.log(e.detail.value)
this.segmentValue = e.detail.value;
}
change you fab on value:
<ion-fab right bottom>
<button *ngIf="segmentValue == 'friends'" ion-fab (click)="addToMarket()" color="primary" mini><img src="assets/imgs/add1.png"></button>
<button *ngIf="segmentValue == 'enemies'" ion-fab (click)="plantNew()" color="primary" mini><img src="assets/imgs/add1.png"></button>
</ion-fab>
Im Using Ionic3.I want to change my ion-card background color whenever the click function fires, i have some problem it does not work for me, any help on this would be great.Thank You
Thanks
html
<ion-card [color]="buttonColor" (click)="someAction()" tappable>
<ion-card-content >
<p class="item-nme">01.Account Creation Success</p>
<div item-end class="item-mark"><img src="../assets/imgs/checked.png"></div>
</ion-card-content>
</ion-card>
.ts
export class WelcomePage {
private buttonColor: string = "primary";
someAction() {
this.buttonColor = "light";
}
}
you need to use ngStyle or style.background. ion-card doesn't have direct color attribute.
<ion-card [ngStyle]="{'background':buttonColor}" (click)="someAction()" tappable>
<ion-card-content >
<p class="item-nme">01.Account Creation Success</p>
<div item-end class="item-mark"><img src="../assets/imgs/checked.png"></div>
</ion-card-content>
</ion-card>
Using style:
<ion-card [style.background]="buttonColor" (click)="someAction()" tappable>
<ion-card-content >
<p class="item-nme">01.Account Creation Success</p>
<div item-end class="item-mark"><img src="../assets/imgs/checked.png"></div>
</ion-card-content>
</ion-card>
I'm not a programmer, as my guy left, and I'm left debugging the code:
Currently in an ion-item I have an ion-avatar which has displays a circular image. As best as I can tell, it is using the class="round_image" the following code:
<ion-thumbnail class="round-image" item-right>
But where does round-image come from, and how can i get a regular rectangular image?
Please advise.
Here is the entire code:
<ion-title padding>Events</ion-title>
<!--<ion-toolbar>
</ion-toolbar> -->
</ion-header>
<ion-content>
<ion-segment [(ngModel)]="genderSegment" color="danger" padding>
<ion-segment-button value="men" (click)="updateGenderSegment(1)">
Men
</ion-segment-button>
<ion-segment-button value="women" (click)="updateGenderSegment(2)">
Women
</ion-segment-button>
<ion-segment-button value="both" (click)="updateGenderSegment(3)">
Both
</ion-segment-button>
</ion-segment>
<ion-list text-wrap>
<ion-item-group *ngFor="let group of groups">
<ion-item-divider dark><h2>{{ group.label }}</h2></ion-item-divider>
<button ion-item detail-none *ngFor="let event of group.events" [navPush]="eventDetailsPage" [navParams]="{eventId: event.$key}">
<ion-thumbnail class="my-image" item-right>
<div image-cache class="photo" [ngStyle]="{ 'background-image': 'url(' + (event.user | async)?.photoURL + ')'}"></div>
</ion-thumbnail>
<h2><strong>{{ event.title }}</strong></h2>
<p>By: <strong><i>{{ (event.user | async)?.name }}</i></strong></p>
<p>{{ event.category }}</p>
</button>
</ion-item-group>
</ion-list>
<!--<ion-fab hideWhen="ios" right bottom>
<button ion-fab color="danger" (click)="addEvent()">
<ion-icon name="create" ></ion-icon>
</button>
</ion-fab>
-->
<ion-infinite-scroll (ionInfinite)="loadMore($event)">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>
I'd recommend you to learn the basics of HTML and CSS. Check out this link http://www.w3schools.com/
Now, back to your question.
2 options.
1.
Remove the class="round-image" since I believe ion-thumbnail will be squared by default, if this isn't the case, move on to 2.
2.
Your page is called 'Events' so depending on how clean your programmer was, there should be a events.scss, events.component.scss or events.css file
scss and css are used for styling your page. So if you have a round image it will be defined here.
In that file (if it exists, else it's hidden in some other scss or css file) there should be a .round-image (ctrl+f -> .round-image). There will be a property called border-radius. This is the amount your borders will 'curve'. So setting it to 0 will create a square/rectangle image.
If they are rectangle and you want it to be a square you could use this SO question How to "crop" a rectangular image into a square with CSS? The answer provided will prevent your image from 'squishing'*.
*Squishing example: if it's a profile image which is higher than wide, the person in the profile image will look fat when you force it into a square.
It looks like you should remove class="round-image"
<ion-thumbnail class="round-image" item-right>
should be
<ion-thumbnail item-right>