MOBILE-3651 core: Fix scroll to element function
parent
1620dd47ea
commit
f682d89e67
|
@ -47,6 +47,7 @@ export class AddonModQuizPreflightModalComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
formBuilder: FormBuilder,
|
formBuilder: FormBuilder,
|
||||||
|
protected elementRef: ElementRef,
|
||||||
) {
|
) {
|
||||||
// Create an empty form group. The controls will be added by the access rules components.
|
// Create an empty form group. The controls will be added by the access rules components.
|
||||||
this.preflightForm = formBuilder.group({});
|
this.preflightForm = formBuilder.group({});
|
||||||
|
@ -115,7 +116,12 @@ export class AddonModQuizPreflightModalComponent implements OnInit {
|
||||||
|
|
||||||
if (!this.preflightForm.valid) {
|
if (!this.preflightForm.valid) {
|
||||||
// Form not valid. Scroll to the first element with errors.
|
// Form not valid. Scroll to the first element with errors.
|
||||||
if (!CoreDomUtils.instance.scrollToInputError(this.content)) {
|
const hasScrolled = CoreDomUtils.instance.scrollToInputError(
|
||||||
|
this.elementRef.nativeElement,
|
||||||
|
this.content,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasScrolled) {
|
||||||
// Input not found, show an error modal.
|
// Input not found, show an error modal.
|
||||||
CoreDomUtils.instance.showErrorModal('core.errorinvalidform', true);
|
CoreDomUtils.instance.showErrorModal('core.errorinvalidform', true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,9 +681,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy {
|
||||||
* @param slot Slot of the question to scroll to.
|
* @param slot Slot of the question to scroll to.
|
||||||
*/
|
*/
|
||||||
protected scrollToQuestion(slot: number): void {
|
protected scrollToQuestion(slot: number): void {
|
||||||
if (this.content) {
|
CoreDomUtils.instance.scrollToElementBySelector(
|
||||||
CoreDomUtils.instance.scrollToElementBySelector(this.content, '#addon-mod_quiz-question-' + slot);
|
this.elementRef.nativeElement,
|
||||||
}
|
this.content,
|
||||||
|
'#addon-mod_quiz-question-' + slot,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -104,7 +104,11 @@ export class CoreLinkDirective implements OnInit {
|
||||||
if (href.charAt(0) == '#') {
|
if (href.charAt(0) == '#') {
|
||||||
// Look for id or name.
|
// Look for id or name.
|
||||||
href = href.substr(1);
|
href = href.substr(1);
|
||||||
CoreDomUtils.instance.scrollToElementBySelector(this.content, '#' + href + ', [name=\'' + href + '\']');
|
CoreDomUtils.instance.scrollToElementBySelector(
|
||||||
|
this.element.closest('ion-content'),
|
||||||
|
this.content,
|
||||||
|
`#${href}, [name='${href}']`,
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
QueryList,
|
QueryList,
|
||||||
Type,
|
Type,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
|
ElementRef,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
|
@ -111,6 +112,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected content: IonContent,
|
protected content: IonContent,
|
||||||
|
protected elementRef: ElementRef,
|
||||||
) {
|
) {
|
||||||
// Pass this instance to all components so they can use its methods and properties.
|
// Pass this instance to all components so they can use its methods and properties.
|
||||||
this.data.coreCourseFormatComponent = this;
|
this.data.coreCourseFormatComponent = this;
|
||||||
|
@ -402,7 +404,11 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
|
|
||||||
if (this.moduleId && typeof previousValue == 'undefined') {
|
if (this.moduleId && typeof previousValue == 'undefined') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
CoreDomUtils.instance.scrollToElementBySelector(this.content, '#core-course-module-' + this.moduleId);
|
CoreDomUtils.instance.scrollToElementBySelector(
|
||||||
|
this.elementRef.nativeElement,
|
||||||
|
this.content,
|
||||||
|
'#core-course-module-' + this.moduleId,
|
||||||
|
);
|
||||||
}, 200);
|
}, 200);
|
||||||
} else {
|
} else {
|
||||||
this.content.scrollToTop(0);
|
this.content.scrollToTop(0);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
|
import { Component, ViewChild, ElementRef, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
|
||||||
import { IonContent, IonRefresher } from '@ionic/angular';
|
import { IonContent, IonRefresher } from '@ionic/angular';
|
||||||
|
|
||||||
|
@ -81,6 +81,8 @@ export class CoreLoginEmailSignupPage implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected fb: FormBuilder,
|
protected fb: FormBuilder,
|
||||||
|
protected elementRef: ElementRef,
|
||||||
|
protected changeDetector: ChangeDetectorRef,
|
||||||
) {
|
) {
|
||||||
// Create the ageVerificationForm.
|
// Create the ageVerificationForm.
|
||||||
this.ageVerificationForm = this.fb.group({
|
this.ageVerificationForm = this.fb.group({
|
||||||
|
@ -272,8 +274,17 @@ export class CoreLoginEmailSignupPage implements OnInit {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (!this.signupForm.valid || (this.settings?.recaptchapublickey && !this.captcha.recaptcharesponse)) {
|
if (!this.signupForm.valid || (this.settings?.recaptchapublickey && !this.captcha.recaptcharesponse)) {
|
||||||
// Form not valid. Scroll to the first element with errors.
|
// Form not valid. Mark all controls as dirty to display errors.
|
||||||
const errorFound = await CoreDomUtils.instance.scrollToInputError(this.content);
|
for (const name in this.signupForm.controls) {
|
||||||
|
this.signupForm.controls[name].markAsDirty();
|
||||||
|
}
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
|
|
||||||
|
// Scroll to the first element with errors.
|
||||||
|
const errorFound = CoreDomUtils.instance.scrollToInputError(
|
||||||
|
this.elementRef.nativeElement,
|
||||||
|
this.content,
|
||||||
|
);
|
||||||
|
|
||||||
if (!errorFound) {
|
if (!errorFound) {
|
||||||
// Input not found, show an error modal.
|
// Input not found, show an error modal.
|
||||||
|
|
|
@ -1119,24 +1119,26 @@ export class CoreDomUtilsProvider {
|
||||||
/**
|
/**
|
||||||
* Scroll to a certain element using a selector to find it.
|
* Scroll to a certain element using a selector to find it.
|
||||||
*
|
*
|
||||||
|
* @param container The element that contains the element that must be scrolled.
|
||||||
* @param content The content that must be scrolled.
|
* @param content The content that must be scrolled.
|
||||||
* @param selector Selector to find the element to scroll to.
|
* @param selector Selector to find the element to scroll to.
|
||||||
* @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll.
|
* @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll.
|
||||||
* @param duration Duration of the scroll animation in milliseconds.
|
* @param duration Duration of the scroll animation in milliseconds.
|
||||||
* @return True if the element is found, false otherwise.
|
* @return True if the element is found, false otherwise.
|
||||||
*/
|
*/
|
||||||
async scrollToElementBySelector(
|
scrollToElementBySelector(
|
||||||
content: IonContent,
|
container: HTMLElement | null,
|
||||||
|
content: IonContent | undefined,
|
||||||
selector: string,
|
selector: string,
|
||||||
scrollParentClass?: string,
|
scrollParentClass?: string,
|
||||||
duration?: number,
|
duration?: number,
|
||||||
): Promise<boolean> {
|
): boolean {
|
||||||
// @todo: This function is broken. Scroll element cannot be used because it uses shadow DOM so querySelector returns null.
|
if (!container || !content) {
|
||||||
// Also, traversing using parentElement doesn't work either, offsetParent isn't part of the parentElement tree.
|
return false;
|
||||||
try {
|
}
|
||||||
const scrollElement = await content.getScrollElement();
|
|
||||||
|
|
||||||
const position = this.getElementXY(scrollElement, selector, scrollParentClass);
|
try {
|
||||||
|
const position = this.getElementXY(container, selector, scrollParentClass);
|
||||||
if (!position) {
|
if (!position) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1152,16 +1154,13 @@ export class CoreDomUtilsProvider {
|
||||||
/**
|
/**
|
||||||
* Search for an input with error (core-input-error directive) and scrolls to it if found.
|
* Search for an input with error (core-input-error directive) and scrolls to it if found.
|
||||||
*
|
*
|
||||||
|
* @param container The element that contains the element that must be scrolled.
|
||||||
* @param content The content that must be scrolled.
|
* @param content The content that must be scrolled.
|
||||||
* @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll.
|
* @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll.
|
||||||
* @return True if the element is found, false otherwise.
|
* @return True if the element is found, false otherwise.
|
||||||
*/
|
*/
|
||||||
async scrollToInputError(content?: IonContent, scrollParentClass?: string): Promise<boolean> {
|
scrollToInputError(container: HTMLElement | null, content?: IonContent, scrollParentClass?: string): boolean {
|
||||||
if (!content) {
|
return this.scrollToElementBySelector(container, content, '.core-input-error', scrollParentClass);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.scrollToElementBySelector(content, '.core-input-error', scrollParentClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue