I'm trying to make a dual infinite scroll case when new items are added as the user scrolls either all the way up or all the way down.
<ion-infinite-scroll (ionInfinite)="scrolledUp($event)" position="top">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
<ion-list>
<ion-item *ngFor="let item of items">
<!-- item content -->
<ion-item>
</ion-list>
<ion-infinite-scroll (ionInfinite)="scrolledDown($event)" position="bottom">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
The problem is that the list opens initially at the bottom. Of course I can scrollIntoView the first item on ionViewDidEnter but it wouldn't look nice and would confuse the users.
I noticed it's the one with position=top that is responsible for the initial position at the bottom.
Is there a way to set the initial scroll position for the ion-infinite-scroll without having to explicitly scroll the list to top?
As I initially suspected the infinite scroll component does force a scroll to bottom if position is set to top. It occurs in the componentDidLoad method as shown below:
async componentDidLoad() {
const contentEl = this.el.closest('ion-content');
if (contentEl) {
await contentEl.componentOnReady();
this.scrollEl = await contentEl.getScrollElement();
}
this.thresholdChanged();
this.disabledChanged();
if (this.position === 'top') {
writeTask(() => {
if (this.scrollEl) {
this.scrollEl.scrollTop = this.scrollEl.scrollHeight - this.scrollEl.clientHeight;
}
});
}
}
As the component provides no means to intervene in this behavior and without an option to extend or otherwise customize the default stencil components, I turned to a somewhat hacky way to achieve the desired behavior.
Therefore, you first need to remove the position=top part from the ion-infinite-scroll declaration in your view so that the scroll part of the above code won't trigger. You would also provide a name for the ion-infinite-scroll so that it can be referenced from your page class.
<ion-infinite-scroll #top (ionInfinite)="scrolledUp($event)">
Then you make a reference to #top above in your page.
#ViewChild('top') topScroller;
Then in the ionViewDidEnter life cycle hook, you can set the position for the scroller as top.
setTimeout(() => {
this.topScroller.position = 'top';
}, 200);
It has to be in a time out because the topScroller variable is still not assigned when ionViewDidEnter is called and the next life cycle hook is ionViewWillLeave which won't be that useful for our purpose.
Related
I have a list containing users, and i have an item that i want it to redirect me to a modal page.
My problem is as soon as my page is available, i get an infinite pop up of my modal, instead of going on my ion-select and select the item so i can get the pop up.
html
<ion-select interface="popover" (ngModelChange)="onChange($event)">
<ion-option>Bacon</ion-option>
<ion-option [value]="openConfig()"></ion-option>Black Olives</ion-option>
<ion-option>Extra Cheese</ion-option>
<ion-option>Mushrooms</ion-option>
<ion-option>Pepperoni</ion-option>
<ion-option>Sausage</ion-option>
</ion-select>
ts
onChange(value: any) {
if (value === 'openConfig') {
this.openConfig()
}
}
openConfig() {
this.modalCtrl.create('ConfigModal').present;
console.log('heeey')
}
Setting the [value] in the template is actually calling your openConfig() function, creating an infinite loop on the page load. To do what you're trying to do here you don't need to reference your openConfig function in the template at all.
ion-select uses the output event ionChange, which outputs the value of the ion-option selected. So the normal way to do this in Ionic 3 would be something like this:
html:
<ion-content padding>
<ion-select interface="popover" [ngModel]="option" (ionChange)="onChange($event)">
<ion-option>Bacon</ion-option>
<ion-option>Black Olives</ion-option>
<ion-option>Extra Cheese</ion-option>
<ion-option>Mushrooms</ion-option>
<ion-option>Pepperoni</ion-option>
<ion-option>Sausage</ion-option>
</ion-select>
</ion-content>
js:
onChange(value: any) {
if (value === 'Black Olives') {
this.openConfig()
}
}
openConfig() {
this.modalCtrl.create(ConfigModal).present();
}
Note that the value of an ion-option is simply the text of the label. So that's what you should check for in your "onChange" function.
You have a couple of other unrelated typos, but I believe this addresses the question of your infinite loop. Hope this helps!
i have the following code.
home.html:
<ion-list [virtualScroll]="newDates" approxItemHeight="50px">
<ion-item *virtualItem="let date" id="i">
{{date.holiday}}
</ion-item>
</ion-list>
home.ts:
scrollTo(){
let yOffset = document.getElementById('25').offsetTop;
this.content.scrollTo(0, yOffset);
}
For Example i have to scroll down to i=25
The following method works for normal list without VirtualScroll.
VirtualScroll doesnt display the elements which are not seen on the view hence we get an error yOffset is null
The virtual scroll component that the ionic contains is a very strange and bug-filled component. In an app I've even developed I tried to use it, but I ended up preferring to use angular2-virtual-scroll.
I think you could try this component instead of the current one you are using.
I know this is an old thred but I was unable to find the answer to the same question so I will post my answer. The scrollTo element does not work because the element does not exist yet on the page. Only the visible ones exit in the DOM witch is the whole idea behind virtual scrolling I think.
So here is how I more or less solved the problem:
y = 0;
#ViewChild(IonContent) content: IonContent;
async scrollTo(elementId: string) {
if (!document.getElementById(elementId)) {
await this.content.scrollToPoint(0, this.y, 0);
setTimeout(() => {
this.y = this.y + 100;
return this.scrollTo(elementId);
}, 0);
}
if (document.getElementById(elementId)) {
document.getElementById(elementId).scrollIntoView();
}
}
Basically the function scrolls down 100 px checks if the element exists and if now scrolls down more untill it finds it.
I am sure there is more to be added like what if it reached the end if the list and didn't find the element...it will probably go in a infinite loop...
Nevertheless this might serve somebody...
Cheers!
this is Ionic version 1 question:
i have 4 tabs, each of the tab have it's content, one of them have a list inside it...
so if we have a list inside 3rd tab, then we choose the list and go to single page with back button...
after i tab the back button, it back to 1st tab, not to 3rd tab...
it seems that it cannot remember the position by default, i have tried some workaround like this...
i set the tab just before it enter the previous page:
$scope.$on("$ionicView.beforeEnter", function (event, data) {
$ionicTabsDelegate.select($scope.maintabindex);
});
with this on-select on each tab:
<ion-tab title="Forum" icon-on="ion-tab-fa-headphones" icon-off="ion-tab-fa-headphones" on-select="tabSelected()" href="#/app/home">
and then i have this function:
$scope.tabSelected = function () {
$scope.maintabindex = $ionicTabsDelegate.selectedIndex();
};
the problem is, the on-select method executed after i back to the parent page (*with tab page), so it cannot working well, because $ionicTabsDelegate.selectedIndex() on-select always give 0 index after i back from the single page to the tabbed page
any idea?
The ionic go back button should work in those scenarios, be sure that the child's view route is using the same ui-view as the parent tab. If that is the case and it is still not working, it's better for you to override the backbutton on the childs view to go to the parent.
Like this:
<ion-nav-bar ng-controller="MyCtrl">
<ion-nav-back-button class="button-clear"
ng-click="myGoBack()">
<i class="ion-arrow-left-c"></i> Back
</ion-nav-back-button>
</ion-nav-bar>
function MyCtrl($scope, $ionicHistory) {
$scope.myGoBack = function() {
$ionicHistory.goBack();
};
}
You can even replace the myGoBack() function with $state.go('parent-tab-view')
In my Ionic 1.3.1 app I am using the ion-slides component to display sections of a questionnaire:
<ion-slides options="vm.options" slider="data.slider">
<ion-slide-page ng-repeat="s in ::vm.sections">
<div class="bar bar-header bar-positive">
<button class="button button-small button-icon icon ion-chevron-left"
ng-click="vm.previous()"
ng-show="::$index !== 0"></button>
<h1 class="title">{{::s.text}}</h1>
<button class="button button-small button-icon icon ion-chevron-right"
ng-click="vm.next()"
ng-show="::($index + 1) < vm.sections.length"></button>
</div>
<ion-content class="has-header">
<div ng-if="s.include" ng-show="s.show">
<!--My content-->
</div>
</ion-content>
</ion-slide-page>
</ion-slides>
In my controller I listen for the slideChangeStart:
$scope.$on("$ionicSlides.slideChangeStart", function (event, data) {
alert('slideChangeStart');
vm.activeIndex = slider.activeIndex;
vm.propertySurvey.CurrentSectionIndex = vm.activeIndex;
//include is used on an ng-if so elements are removed from the dom
//and the number of watchers is reduced
//also, the slider displays the contents of the
//previous slide unless they are explicitly hidden
vm.sections[slider.previousIndex].include = false;
vm.sections[slider.previousIndex].show = false;
initialiseQuestions(vm.activeIndex);
});
When I click on my previous and next buttons, which call slider.slidePrev() or slider.slideNext(), the slideChangeStart event fires - and I see the alert. But if I slide the page using a gesture - the header changes as I expect but the event does not fire. I've tried switching the swipe off in my options without success (not my preferred solution anyway):
vm.options = {
noSwiping: true,
effect: 'fade'
}
UPDATE
I tried using slideChangeEnd but that event isn't firing either.
So I moved all my code into a single gotoSlide(index) method that I call from my next, previous and pagerClick methods - so that I don't rely on the ion-slides event.
And I added Ionic on-swipe directives to my ion-slide-page component to see if they would work:
<ion-slide-page ng-repeat="s in ::vm.sections"
on-swipe-left="vm.next()"
on-swipe-right="vm.previous()">
That made the swipe work in the Ripple emulator (where it wasn't working at all before), but it still doesn't work on any of my android devices. Again, the swipe events don't seem to fire.
I am using the Crosswalk plugin
The problem goes away if I remove the effect option. So this works:
vm.options = {
noSwiping: false,
speed: 300
}
And, since these are the default values anyway, I ended up removing the options object altogether.
NB replacing 'fade' with 'slide' did not work
Reference:
http://ionicframework.com/docs/api/directive/ionSlides/
I had a similar problem, and the above answer did not work for me, so I thought I'd share what did:
Per the Swiper API (which underlies ion-slides), you can add event based call backs directly to the widget.
The following worked for me (assuming the scope.slider object...)
//the 'slideChangeStart' event has a slider argument in its callback
scope.slider.on('slideChangeStart', function(slider) {
//do stuff - maybe you want to store the activeIndex
scope.indexIReallyCareAbout = slider.activeIndex;
});
The Swiper API gives a list of events that can named in the 'on' callback function under the header Callbacks
I have a draggable modal window and users move it around. They want it to open at the original position, every time they close the window and reopen again. How can I do that? Thanks.
You can use bootstrap modal events
show.bs.modal This event fires immediately when the show instance method is called. If caused by a click, the clicked element is available as the relatedTarget property of the event.
by default the bootstrap modal slide-down from top and if position of modal changes while dragging, it can be rest to original position with show.bs.modal when close and re-open next time.
$('selector').on('show.bs.modal', function () {
// do something...
});
Demo Fiddle modal slide and open in middle
In above fiddle, added a custom position to modal so modal slide and open in middle of the page
.modalnewposition {
-webkit-transform: translateX(-0%) translateY(25%);
-moz-transform: translateX(-0%) translateY(25%);
-ms-transform: translateX(-0%) translateY(25%);
transform: translateX(-0%) translateY(25%);
}
HTML
<div id="myModal" class="modal fade modalnewposition" role="dialog">
Then created another class
.original {
transform: translate(0px, 0px);
transition: transform 0.3s ease-out 0s;
transition: opacity 0.15s linear 0s;
}
Using show.bs.modal I added the original selector to modal so it adjust it's position before showing and override the modalnewposition selector so modal slide and open at top instead in middle of page.
$(document).ready(function() {
$('#myModal').on('show.bs.modal', function () {
$('.modal').addClass("original");
});
});
Demo Fiddle modal slide and open at top
Above example is just to demonstrate that what you asked is achievable with show.bs.modal function.
Add this code to js and execute it every time modal window closes:
var scrollableContentOfModal = document.getElementById(...);// change it accordingly
document.querySelector(scrollableContentOfModal).scrollTop = 0;