Ionic 2/4 Infinite Scroll going upwards...not downwards, for a chat application. Weird spacing and weird behavior - ionic-framework

I'm on one of my last pieces of functionality for my chat application in my Ionic app where I'm trying to infinitely scroll to the "first" message created between two individuals, similar to any other chat app you've used, be it Facebook, Skype, Slack, etc.
The problem I'm running into is a few things.
When I add the infinite scroll code above the <ul> that houses all the messages, I get a giant 200px height whitespace (I think to account for the loading spinner). Here is my code:
<ion-infinite-scroll (ionInfinite)="scrolled($event)"
threshold="10px"
position="top">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
<ul>
<li *ngFor="let message of messages; let i = index">
... other code to build the message
</li>
</ul>
I have my chat setup to grab just the first 10 messages for the particular conversation, with the idea being that once it his the "top" infinite scroll area, it queries for the next 10, etc. The problem is that when the chats load, there is a momentary pause before the scroller gets scrolled down to the bottom of the screen to show the last message that was sent. However, in that brief moment, the scroller is already at the top of the page which seems to indicate to the <ion-infinite-scroll> to fire the scrolled() function.
Has anyone else run into something like this? The messages load asynchronously once the page is rendered, so I'm trying to figure out the best way to prevent the <ion-infinite-scroll> from firing on page load. Is there something simple I'm missing here?
Thanks in advance! :)

For #2, you can disable the scroll while loading your first batch of chats.
Import ViewChild and InfiniteScroll:
import { ViewChild } from '#angular/core';
import { InfiniteScroll } from 'ionic-angular';
Then create a property for infiniteScroll:
#ViewChild(InfiniteScroll) infiniteScroll: InfiniteScroll;
Then disable it, load your stuff, do the scroll, and re-enable:
ionViewDidLoad(): void {
// Disable to give time for loading & scrolling
this.infiniteScroll.enable(false);
loadFirstTenChats().then(() => {
// Fiddle with the timing depending on what you are doing.
// If you have footers or other dynamic content to worry about.
setTimeout( () => this.content.resize(), 100);
// Scroll to the bottom once the size has been calculated:
setTimeout( () => this.content.scrollToBottom(100), 200);
// The scroll above has to be finished before enabling or
// else the IS might think it needs to load the next batch.
setTimeout( () => this.infiniteScroll.enable(true), 1000);
});
}
For #1, I don't know why it takes up so much space, but if the above works, it will be scrolled off page and won't be noticed.

To solve #1, you can use disabled property of the ion-infinite-scroll component. When true, it won't take any space. You can have a logic for when your paginated content ends, then disable the ion-infinite-scroll.
<ion-infinite-scroll [disabled]="true"></ion-infinite-scroll>
And to solve #2, you can use the same above logic. On page load you can disable the ion-infinite-scroll, and after the scroll to bottom event, you enable the ion-infinite-scroll.

Reverse infinite scroll for Chat functionality works fine for me. This is what my code looks like
HTML
<ion-infinite-scroll (ionInfinite)="doInfiniteChat($event)" position="top">
<ion-infinite-scroll-content loadingText="Loading more chats...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
and you have to reverse your array which you are using in *ngFor
<div *ngFor="let msg of msgArray.reverse()">
The above code worked fine. Thanks

Update Your Code from
<ion-infinite-scroll (ionInfinite)="scrolled($event)"
threshold="10px"
position="top">
to
<ion-infinite-scroll (ionInfinite)="scrolled($event)"
threshold="10px"
position="bottom">

Related

Does Ionic 5 has swipe events?

I have an Ionic page on which I'd like to be redirected to a custom page when swiping left. Just redirecting me back isn't good enough, since I can have that page open from a deep link, meaning I have no back on my window's history.
I saw that there's a gesture API, but it's a bit too much for such a common cause. Also, I saw that on previous versions there where swipe left/right events, but no reference for it on Ionic 4/5.
Doesn't Ionic 5 has an on-swipe-left event?
Another solution, if you didn't want to add a hammerjs dependency and just use Ionic would be to use the official ion-slides component to manage your pages as slides.
I've implemented this on a couple of apps and it works really well.
In your example, you would want to setup an <ion-slides> container and then your 'pages' would exist inside as <ion-slide> components.
You can then easily tap into the (ionSlideDidChange) event as follows:
<ion-slides
*ngIf="!isLoading && pages"
[options]="slideOptions"
(ionSlideDidChange)="onSlideChange()"
class="em-height-full"
#pages
>
Then in your .js or .ts file just create a method like:
async onSlideChange() {
this.pageIndex = await this.slides.getActiveIndex();
}
Then you could track the page with pageIndex. So in your case, you'd open the page from the deepLink (maybe have it route to 'page 2') and then when you swipe left on the slide component you could go to page 1.
You are looking for this: https://ionicframework.com/docs/utilities/gestures
Adding HammerJS is not a great idea since you might encounter buggy scenarios.

onDragOver very slow with Angular Material tabs

I'm using Angular 6 and Angular Material. I'm attempting to implement a simple drag and drop list inside of a Material tab (). Drag and drop works fine outside of a material tab, but inside the tab it's very slow (18 seconds to DnD 1 item in a list of 5 items). The onDragEvent fires many many times even when the mouse isn't moving. I've tried detaching (onDragStart) and reattaching (onDrop) the change detector. It doesn't make a difference.
Here's the html:
<div (dragover)="onDragOver($event)">
<div *ngFor="let item of listItems, let i = index"
(drop)="onDrop($event, i)"
[draggable]="true"
(dragstart)="onDragStart(i)">
{{item}}
</div>
</div>
Here are the functions:
onDragStart(i) {
}
onDragOver(event) {
console.log('1')
event.preventDefault();
}
onDrop(event, i) {
}
Thank you.
It appears that as I dragged the item around the screen- even briefly, the Angular engine was overfiring and queuing up a ridiculous number of calls of onDrop and/or onDragEnter. And that when angular hooks those events, it also assumes that something in the angular components has changed and it runs through the change detection routines.
Each... time.
What I did that fixed my problem was to turn off change detection when dragging began and turn it back on when dragging was complete.
Here is a brief hint at what I did:
import ChangeDetectorRef from '#angular/core'
inject ChangeDetector into your constructor:
constructor(private cdr: ChangeDetectorRef)
call detach from the dragStart:
this.cdr.detach();
call reattach from the drop and dragEnd:
this.cdr.reattach();
If your component is buried deep on your page, you may have to detach change detection at a higher level in order to see results.
i was experiencing a similar issue, where dragging when there were many elements on the page was unbearably slow. I found that removing the (dragover) angular binding, and replacing it with pure javascript instantly solved the problem
i switched
(dragover)="this.onDragOver($event)"
to
ondragover="onDragOver(event)"
you also need to declare the function in index.html instead of in your typescript file or html template (hence pure javascript)

Clicking Hastily Causes Routing Issue

I have a simple Ionic v1 app that displays a list of items. Clicking on an item navigates the user to a new view displaying info about the item.
If the user selects a second before the first item is loaded, the app will navigate to both views respectively, which is an issue.
Is there a way to prevent this via an ionic config or angular?
Note I am using Ionic Native Transitions, but this issue seems to be independent
*Edit * I know I can use something like a 'loading' modal to prevent clicks, but then I'll have to do this for every single list in the application that loads data, so that doesn't sound ideal
I ended up utilizing inoicNativeTransition.beforeTransition in order to render a modal-style backdrop that prevents users from clicking for 300ms
$rootScope.$on('ionicNativeTransitions.beforeTransition', function(){
$rootScope.stateTransitioning = true;
$timeout(function(){
$rootScope.stateTransitioning = false;
}, 300)
});
HTML
<div ng-show="stateTransitioning === true" id="inivisible-backdrop"> </div>
I don't hide the backdrop on the success of the transition, because there seems to be an inconsistent lag between when the view is changed and the transition is marked as 'successful'. 300ms seems to work just fine

$ionicScrollDelegate freezes the scroll

In my controller i am using $firebaseArray and $ionicScrollDelegate to show messages in a chat. when my $firebaseArray is loaded, i am using the scroll delegate to scroll to the bottom and it works fine, no issues there.
But when a new child is added, i am again using scroll delegate to scroll to bottom, that scrolls down without any issues but it freezes the scroll, i.e i am not able to scroll back to the top
code
chat.html
<ion-content delegate-handle="mainScroll">
//chat list
</ion-content>
chat controller
app.controller('chatCtrl',function($scope,$firebaseArray,$ionicScrollDelegate){
var chatRef = new Firebase("my ref");
$scope.messages = $firebaseArray(chatRef);
$scope.messages.$loaded().then(function(){
$ionicScrollDelegate.$getByHandle('mainScroll').scrollBottom(true);
});
chatRef.on('child_added', function(childSnapshot, prevChildKey) {
$ionicScrollDelegate.$getByHandle('mainScroll').scrollBottom(true);
});
});
I believe you are using android phone to test right? If android I'm pretty sure there have a bug by using animation to scroll to bottom. Have you try to remove the animation by delete 'true' value on the function? Ex:
$ionicScrollDelegate.$getByHandle('mainScroll').scrollBottom(true);
to
$ionicScrollDelegate.$getByHandle('mainScroll').scrollBottom();
Try It.. hope it helps you.
UPDATE (SOLUTION):
Okay I found the solution.. It seem we need to set 'jsScrolling: true' instead of by default is 'jsScrolling: false' for android.
Please modified/edit this on 'ionic-angular.js'
Hope it helps others :)

iOS 7 Safari: OS locks up for 4 seconds when clicking/focusing on a HTML input

UPDATE: The issue seems to stem from having many select elements on a page. How random is that?
So here's the issue. On iOS 7 Safari, when tapping the a text input on my site, the keyboard opens then freezes the OS for about 2-5 seconds then finally scrolls to the input. After this happens once, it never happens again until you refresh the page. I've looked all over the place, and yes, iOS 7 Safari is super buggy, but lets try and see if we can figure this out.
Note: This does not happen in any other mobile browser or any previous iOS Safari. It happens both on the ios 7 iphone and ios 7 ipad.
I will list everything my friend and I have tried so far:
Removed the ability to add event handlers in jQuery. (Note: all our event handlers are assigned through jQuery except for unload and onpageshow).
Removed the jQuery autocomplete script from the inputs.
Removed all JavaScript from the inputs.
Removed all third-party libraries being added on the page by rejecting the domains on the Mac.
Switched back to previous jQuery versions. The last one we could actually use before nothing worked was 1.7.0.
Switched back to previous jQuery UI versions.
Changed input event handling to delegate and live, instead of on('click')
Removed all CSS classes.
Removed all CSS from the page. Note: The response time for the OS this went down to 1-2 seconds but still happened.
Does anyone have any ideas?
Thanks a bunch!
(There are some somewhat-effective solutions, see near the end of the list)
At my company we are also suffering from this. We filed an issue with Apple but have heard mum.
Here are some interesting jsfiddles to help illustrate some of the issues, it definitely seems to revolve around the number of hidden fields, and textareas do not seem to be affected.
From debugging efforts, my guess is that there is some functionality trying to detect if an input is a credit card or phone number or some special kind which seems to cause the locking behavior. This is just one hypothesis though..
Summary:
On a page with a form containing named input elements inside containers that are marked "display: none", the first press on an input in that form has a very noticeable delay (20sec-2min) between the keyboard coming up and the input being focused. This prevents users from using our web app due to the enormous time spent with the ui frozen waiting for the keyboard to respond. We have debugged it in various scenarios to try and discern what is going on, and it appears to be from a change in how iOS7 parses the DOM versus how it did on iOS6, which has none of these issues.
From debugging within Safari's Inspector with the iPad connected, we found that iOS7 provides much more information about the (program)'s activities, to the point that we found that _CollectFormMetaData is the parent of the problem. Searching for meta data causes massive churn that increases more than linearly along with the number of hidden containers containing inputs. We found that _isVisible and _isRenderedFormElement are called far more than they reasonably should be. Additionally, if it helps, we found some detection functions relating to credit cards and address books were large time consumers.
Here are some jsFiddles for illustration. Please view them in Safari on an iPad running iOS6 and then on an iPad running iOS7:
http://jsfiddle.net/gUDvL/20/ - Runs fine on both
http://jsfiddle.net/gUDvL/21/ - Just noticeable delay on iOS 7
http://jsfiddle.net/gUDvL/22/ - More noticeable delay on iOS 7
http://jsfiddle.net/gUDvL/29/ - VERY noticeable delay on iOS 7
http://jsfiddle.net/gUDvL/30/ - Same as 29 but with none hidden - no delay on iOS 7
http://jsfiddle.net/gUDvL/38/ - Same as 29 but further exacerbated
http://jsfiddle.net/gUDvL/39/ - 99 hidden inputs, one visible, one separately visible
http://jsfiddle.net/gUDvL/40/ - 99 hidden textareas, one visible, one separately visible
http://jsfiddle.net/gUDvL/41/ - 99 hidden inputs, one visible, one separately visible, all
with the autocomplete="off" attribute
http://jsfiddle.net/gUDvL/42/ - 99 hidden inputs, one visible, one separately visible. Hidden by position absolute and left instead of display.
http://jsfiddle.net/gUDvL/63/ - Same as gUDvL/43/ but with autocomplete, autocorrect, autocapitalize, and spellcheck off
http://jsfiddle.net/gUDvL/65/ - Same as gUDvL/63/ but with cleaned up indentation (seems slower on iPad)
http://jsfiddle.net/gUDvL/66/ - Same as gUDvL/65/ but with display none via css again instead of DOMReady jQuery
http://jsfiddle.net/gUDvL/67/ - Same as gUDvL/66/ but with TedGrav's focus/blur technique
http://jsfiddle.net/gUDvL/68/ - Same as gUDvL/66/ but with css driven text-indent instead of display:block again (noticeable improvement - reduction to 2-3 secs for initial focus)
http://jsfiddle.net/gUDvL/69/ - Same as gUDvL/68/ but with TedGrav's focus/blur re-added
http://jsfiddle.net/gUDvL/71/ - Same as gUDvL/66/ but with js adding a legend tag before each input. (noticeable improvement - reduction to 2-3 secs for initial focus)
<input type="text" autocomplete="off" /> (links to jsfiddle.net must be accompanied by code..)
(We should note that having the iPad connected to a Mac with Safari's debugger engaged dramatically emphasizes the delays.)
Steps to Reproduce:
Load any of the above jsfiddles on the iPad
Press an input to gain focus
Watch screen until you can type
Expected Results:
Expect to be able to type as soon as the keyboard pops up
Actual Results:
Watch the keyboard pop up and the screen freeze, unable to scroll or interact with Safari for a duration. After the duration, focus is given as expected. From then on no further freezes are experienced when focusing on inputs.
tl;dr technique summary
So overall there are a couple proposed fixes from various answers:
Don't hide the divs with display: none - use something like text-indent
Short circuit Apple's metadata scanning logic - many form tags or legend tags seem to do the trick
Auto focus/blur - Did not work for me but two people reported it did
Related threads at Apple:
https://discussions.apple.com/thread/5468360
There seems to be a problem with how IOS handles the touch-event for inputs and textareas. The delay gets larger when the DOM gets larger. There is however not a problem with the focus event!
To work around this problem you can override the touchend event and set focus to the input/textarea.
document.addEventListener("touchend", function (e) {
if (e.target.nodeName.toString().toUpperCase() == 'INPUT' || e.target.nodeName.toString().toUpperCase() == 'TEXTAREA') {
e.preventDefault();
e.target.focus();
}
});
This will however create a new problem. It will let you scroll the page while touching the input/textarea, but when you let go, the site will scroll back to the original position.
To fix this, you just need to check if any scrolling has occured, and surround the preventDefault and target.focus with an if statement.
To set the original position, you can use the touchstart event.
document.addEventListener("touchstart", function (e) {
... //store the scrollTop or offsetHeight position and compare it in touchend event.
}
EDIT Me and a colleague have improved it a little bit, and it works like a charm.
var scroll = 0;
document.addEventListener("touchstart", function (e) {
scroll = document.body.scrollTop;
});
document.addEventListener("touchend", function (e) {
if (scroll == document.body.scrollTop) {
var node = e.target.nodeName.toString().toUpperCase();
if (node == 'INPUT' || node == 'TEXTAREA' || node == 'SELECT') {
e.preventDefault();
e.target.focus();
if(node != 'SELECT') {
var textLength = e.target.value.length;
e.target.setSelectionRange(textLength, textLength);
}
}
}
});
Struggled with this issue as well within an ios fullscreen which was inserting /removing pages containing a single input element. Was experiencing delays up to 30 seconds with only a single visible text input element on the page (and within the entire DOM). Other dynamically inserted pages with single or multiple text inputs in the same webapp were not experiencing the input delay. Like others have mentioned, after the initial delay, the input field would behave normally on subsequent focus events (even if the dynamic page containing the input element was removed from the DOM, then dynamically re-rendered/inserted back into the DOM).
On a hunch based on the above behaviour, tried the following on page load:
$("#problem-input").focus();
$("#problem-input").blur();
While the above executes immediately with no delay, the end result is no subsequent delays when the input gets focus via user interaction. Can't explain the reason behind this working, but it appears to work consistently for my app while other suggested fixes have failed.
I have the same freezeing problem.
I am not sure we're in the same situation.
here is my demo:http://tedzhou.github.io/demo/ios7sucks.html
In my page, i use a <p> element with onclick attribute as a button.
When user click on the button, page change to a textarea.
Then a click on it will freezes the browser.
The time freezing spent relevent to the numbers of the dom elements.
In my pages, there are 10000 elements, which make it freeze by 10+ seconds.
We can solve the problem by switching the <p> element to the real <button>, or reducing the nums of dom elements.
ps: sorry for my poor english. LOL
The main issue for me was with hidden fields. Made the form hang for 10-15 seconds.
I managed to get around by positioning the hidden form fields off the screen.
To hide:
position: absolute;
left: -9999px;
To show:
position: relative;
left: 0;
Met the same problem in quite complex application having many inputs.
Attached debugger to Safari iOS7 via USB and logged UI events. I see "touchend" event coming as soon as I am clicking on textarea (or any input) and in 10-20 seconds after that I see "click" being dispatched.
Clearly it is a bug in Safary as on other devices like Android or iOS6 there is no problem with the very same application.
It happens not only in iOS but in safari 7 for MAC OS (Maverics) too, I have found that the problem happens when you use a lot of div tags to contain inputs (or selects) within a form:
<div> <select>...</select> </div>
<div> <select>...</select> </div>
...
I changed the layout of my selects to use ul/li and fieldsets instead of divs and the freezze time was reduced drastically.
<ul>
<li><select>...</select></div>
<li><select>...</select></div>
</ul>
Here are two examples in jsfiddle:
freezze for 5 seconds
http://jsfiddle.net/k3j5v/5/
freeze for 1 second
http://jsfiddle.net/k3j5v/6/
I hope it might help someone
For me, this issue was being caused by user inputs being hidden on the page with display:none.
The workaround I used: instead of hiding inputs with display:none, I used jQuery's detach() method on document ready to 'hide' all the user inputs that were not being used. Then append() the inputs when they were needed.
That way no inputs had display:none on when the page was first loaded and so no delay occurred on the initial user interaction.
We had the same or a similar problem at my company. Whenever we displayed a large number of drop down lists and then a user clicked on a drop down, IOS 7 would freeze the page for a minute or two. After it unfroze, everything would work properly from that point forward.
This affected all input types. The large number of drop downs were actually hidden on first load - the user would initiate the display of the drop downs. Until the drop downs were displayed - everything would work fine. As soon as they were displayed, the next input click, even an input that had been working properly, now would cause the browser to freeze.
As others have noted, it seems that IOS 7 has a problem when parsing the visible inputs in the DOM after the user first interacts with an input. When the number and/or complexity of the elements/options/DOM are higher, the freeze is more pronounced.
Because it always froze on the initial user interaction, we decided to initiate a hidden user action as soon as we displayed the list of drop downs. We created a transparent button (it could not be hidden - it had to be "displayed") and initiated a click on it as soon as the user opened the drop down list. We thought that this would make IOS start parsing the DOM quicker, but found that it actually fixed the problem completely.
I have encountered this problem as well since I noticed many people are still having a problem with this I thought I'd put my solution.
Basically my solution is server side hiding of elements.
My page is ASP.NET so I wrapped my divs with the inputs with Panels and set these panels as Visible false.
This way if I click on an input the safari can't see all the other controls since they are hidden server side.
Of course if you want to make this work a little like clientside jquery you'll need automatic postback and an updatepanel somewhere.
This solution requires an effort but still its better than actually trying to fix a safari bug.
Hope this helps.
My answer might be slightly off the main topic, but I did arrive here after some searching as the scenario "feels" similar.
Issue:
My issue felt like a lockup in iOS, but not quite, since other elements on the page were still interactive. I had an <input type="search" /> element, that would not focus when I clicked into the field. But it would eventually catch focus after about 4-5 taps on the screen.
Additional Info:
My project is a hybrid app: WebView inside of an iOS app. The site is built with Twitter Bootstrap.
Solution:
I happened to also have the autofocus attribute set on the element. I tried removing that and it worked... no more consecutive taps to get the field to focus.
iOS 12.1.1 - December 2018
Here is a simple fix that worked in my case:
window.scrollTo(0,0) // attached to 'blur' event for the input fields
While it may not be ideal in terms of UX (especially if you have a form with many fields), it's definitely better than having 10+ second freezing time.
have you tried to turn off "Password & Autofill" > "Credit Cards" into Safari settings ?
After this operation it works fine. This isn't a final solution but maybe the problem's reason on iOS.