diff --git a/src/addons/block/timeline/components/timeline/timeline.ts b/src/addons/block/timeline/components/timeline/timeline.ts index e61a52f5c97..6969c63c361 100644 --- a/src/addons/block/timeline/components/timeline/timeline.ts +++ b/src/addons/block/timeline/components/timeline/timeline.ts @@ -39,10 +39,10 @@ import { CoreLogger } from '@singletons/logger'; }) export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent { - sort = new FormControl(); + sort = new FormControl(AddonBlockTimelineSort.ByDates); sort$!: Observable; sortOptions!: AddonBlockTimelineOption[]; - filter = new FormControl(); + filter = new FormControl(AddonBlockTimelineFilter.Next30Days); filter$!: Observable; statusFilterOptions!: AddonBlockTimelineOption[]; dateFilterOptions!: AddonBlockTimelineOption[]; diff --git a/src/addons/calendar/pages/edit-event/edit-event.ts b/src/addons/calendar/pages/edit-event/edit-event.ts index a30321547f8..653aa35b956 100644 --- a/src/addons/calendar/pages/edit-event/edit-event.ts +++ b/src/addons/calendar/pages/edit-event/edit-event.ts @@ -78,9 +78,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { // Form variables. form: FormGroup; - typeControl: FormControl; - groupControl: FormControl; - descriptionControl: FormControl; + typeControl: FormControl; + groupControl: FormControl; + descriptionControl: FormControl; // Reminders. remindersEnabled = false; @@ -103,9 +103,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { this.form = new FormGroup({}); // Initialize form variables. - this.typeControl = this.fb.control('', Validators.required); - this.groupControl = this.fb.control(''); - this.descriptionControl = this.fb.control(''); + this.typeControl = this.fb.control(null, Validators.required); + this.groupControl = this.fb.control(null); + this.descriptionControl = this.fb.control('', { nonNullable: true }); this.form.addControl('name', this.fb.control('', Validators.required)); this.form.addControl('eventtype', this.typeControl); this.form.addControl('categoryid', this.fb.control('')); @@ -322,11 +322,11 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { this.form.controls.name.setValue(event.name); this.form.controls.timestart.setValue(CoreTimeUtils.toDatetimeFormat(event.timestart * 1000)); - this.form.controls.eventtype.setValue(event.eventtype); + this.typeControl.setValue(event.eventtype as AddonCalendarEventType); this.form.controls.categoryid.setValue(event.categoryid || ''); this.form.controls.courseid.setValue(courseId || ''); this.form.controls.groupcourseid.setValue(courseId || ''); - this.form.controls.groupid.setValue(event.groupid || ''); + this.groupControl.setValue(event.groupid || null); this.form.controls.description.setValue(event.description); this.form.controls.location.setValue(event.location); @@ -410,7 +410,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave { try { await this.loadGroups(courseId); - this.groupControl.setValue(''); + this.groupControl.setValue(null); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'Error getting data.'); } diff --git a/src/addons/mod/assign/feedback/comments/component/comments.ts b/src/addons/mod/assign/feedback/comments/component/comments.ts index 6271598e2c8..c5f268f2337 100644 --- a/src/addons/mod/assign/feedback/comments/component/comments.ts +++ b/src/addons/mod/assign/feedback/comments/component/comments.ts @@ -34,7 +34,7 @@ import { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/cl }) export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedbackPluginBaseComponent implements OnInit { - control?: FormControl; + control?: FormControl; component = AddonModAssignProvider.COMPONENT; text = ''; isSent = false; @@ -76,7 +76,7 @@ export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedb } }); } else if (this.edit) { - this.control = this.fb.control(this.text); + this.control = this.fb.control(this.text, { nonNullable: true }); } } finally { this.loaded = true; diff --git a/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts b/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts index ca9afe172b2..f91d185c9bd 100644 --- a/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts +++ b/src/addons/mod/assign/submission/onlinetext/component/onlinetext.ts @@ -31,7 +31,7 @@ import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handle }) export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignSubmissionPluginBaseComponent implements OnInit { - control?: FormControl; + control?: FormControl; words = 0; component = AddonModAssignProvider.COMPONENT; text = ''; @@ -94,7 +94,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS }); } else { // Create and add the control. - this.control = this.fb.control(this.text); + this.control = this.fb.control(this.text, { nonNullable: true }); } // Calculate initial words. diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts index 63f44e1b17f..ab834b428c2 100644 --- a/src/addons/mod/forum/components/post/post.ts +++ b/src/addons/mod/forum/components/post/post.ts @@ -83,7 +83,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges @ViewChild('replyFormEl') formElement!: ElementRef; - messageControl = new FormControl(); + messageControl = new FormControl(null); uniqueId!: string; defaultReplySubject!: string; diff --git a/src/addons/mod/forum/pages/new-discussion/new-discussion.ts b/src/addons/mod/forum/pages/new-discussion/new-discussion.ts index 6eb86ce9a29..8dc2a15638e 100644 --- a/src/addons/mod/forum/pages/new-discussion/new-discussion.ts +++ b/src/addons/mod/forum/pages/new-discussion/new-discussion.ts @@ -70,7 +70,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea @ViewChild(CoreEditorRichTextEditorComponent) messageEditor!: CoreEditorRichTextEditorComponent; component = AddonModForumProvider.COMPONENT; - messageControl = new FormControl(); + messageControl = new FormControl(null); groupsLoaded = false; showGroups = false; hasOffline = false; diff --git a/src/addons/mod/glossary/pages/edit/edit.ts b/src/addons/mod/glossary/pages/edit/edit.ts index d5d9a3ed878..5705cf25744 100644 --- a/src/addons/mod/glossary/pages/edit/edit.ts +++ b/src/addons/mod/glossary/pages/edit/edit.ts @@ -58,7 +58,7 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave { courseId!: number; loaded = false; glossary?: AddonModGlossaryGlossary; - definitionControl = new FormControl(); + definitionControl = new FormControl(null); categories: AddonModGlossaryCategory[] = []; showAliases = true; editorExtraParams: Record = {}; diff --git a/src/addons/mod/lesson/services/lesson-helper.ts b/src/addons/mod/lesson/services/lesson-helper.ts index b12f4efc24c..2fd99808985 100644 --- a/src/addons/mod/lesson/services/lesson-helper.ts +++ b/src/addons/mod/lesson/services/lesson-helper.ts @@ -369,7 +369,7 @@ export class AddonModLessonHelperProvider { }; // Init the control. - essayQuestion.control = this.formBuilder.control(''); + essayQuestion.control = this.formBuilder.control('', { nonNullable: true }); questionForm.addControl(essayQuestion.textarea.name, essayQuestion.control); } @@ -635,7 +635,7 @@ export type AddonModLessonInputQuestion = AddonModLessonQuestionBasicData & { export type AddonModLessonEssayQuestion = AddonModLessonQuestionBasicData & { useranswer?: string; // User answer, for reviewing. textarea?: AddonModLessonTextareaData; // Data for the textarea. - control?: FormControl; // Form control. + control?: FormControl; // Form control. }; /** diff --git a/src/addons/mod/url/services/url.ts b/src/addons/mod/url/services/url.ts index 2e9ac211133..04722cb3652 100644 --- a/src/addons/mod/url/services/url.ts +++ b/src/addons/mod/url/services/url.ts @@ -157,14 +157,14 @@ export class AddonModUrlProvider { url = url || ''; const matches = url.match(/\//g); - const extension = CoreMimetypeUtils.getFileExtension(url); + const extension = CoreMimetypeUtils.guessExtensionFromUrl(url); if (!matches || matches.length < 3 || url.slice(-1) === '/' || extension == 'php') { // Use default icon. return ''; } - const icon = CoreMimetypeUtils.getFileIcon(url); + const icon = CoreMimetypeUtils.getExtensionIcon(extension ?? ''); // We do not want to return those icon types, the module icon is more appropriate. if (icon === CoreMimetypeUtils.getFileIconForType('unknown') || diff --git a/src/addons/mod/wiki/pages/edit/edit.ts b/src/addons/mod/wiki/pages/edit/edit.ts index e1ab5857ddd..816bd2ffa12 100644 --- a/src/addons/mod/wiki/pages/edit/edit.ts +++ b/src/addons/mod/wiki/pages/edit/edit.ts @@ -47,7 +47,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave { courseId?: number; // Course the wiki belongs to. title?: string; // Title to display. pageForm: FormGroup; // The form group. - contentControl: FormControl; // The FormControl for the page content. + contentControl: FormControl; // The FormControl for the page content. canEditTitle = false; // Whether title can be edited. loaded = false; // Whether the data has been loaded. component = AddonModWikiProvider.COMPONENT; // Component to link the files to. @@ -74,7 +74,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave { constructor( protected formBuilder: FormBuilder, ) { - this.contentControl = this.formBuilder.control(''); + this.contentControl = this.formBuilder.control('', { nonNullable: true }); this.pageForm = this.formBuilder.group({}); } diff --git a/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts b/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts index 2c53eea33d4..95f62fd6d8f 100644 --- a/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts +++ b/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts @@ -73,7 +73,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe assessmentStrategyLoaded = false; notSupported = false; feedbackText = ''; - feedbackControl = new FormControl(); + feedbackControl = new FormControl(null); overallFeedkback = false; overallFeedkbackRequired = false; component = ADDON_MOD_WORKSHOP_COMPONENT; diff --git a/src/addons/qtype/essay/component/essay.ts b/src/addons/qtype/essay/component/essay.ts index d216b87f444..197389dbd08 100644 --- a/src/addons/qtype/essay/component/essay.ts +++ b/src/addons/qtype/essay/component/essay.ts @@ -32,7 +32,7 @@ import { CoreFileEntry } from '@services/file-helper'; }) export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent { - formControl?: FormControl; + formControl?: FormControl; attachments?: CoreFileEntry[]; uploadFilesSupported = false; @@ -52,7 +52,7 @@ export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent { /** * Create the Form control. * * @returns Form control. */ - protected createFormControl(field: AuthEmailSignupProfileField): FormControl { + protected createFormControl(field: AuthEmailSignupProfileField): FormControl { const formData = { value: CoreUtils.isTrueOrOne(field.defaultdata), disabled: this.disabled, }; - return new FormControl(formData, this.required && !field.locked ? Validators.requiredTrue : null); + return new FormControl(formData, { + validators: this.required && !field.locked ? Validators.requiredTrue : null, + nonNullable: true, + }); } } diff --git a/src/addons/userprofilefield/datetime/component/datetime.ts b/src/addons/userprofilefield/datetime/component/datetime.ts index c65fec86f71..17e28354a56 100644 --- a/src/addons/userprofilefield/datetime/component/datetime.ts +++ b/src/addons/userprofilefield/datetime/component/datetime.ts @@ -28,7 +28,7 @@ import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-p selector: 'addon-user-profile-field-datetime', templateUrl: 'addon-user-profile-field-datetime.html', }) -export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileFieldBaseComponent { +export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileFieldBaseComponent { ionDateTimePresentation = 'date'; min?: string; @@ -84,13 +84,16 @@ export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileField * * @returns Form control. */ - protected createFormControl(field: AuthEmailSignupProfileField): FormControl { + protected createFormControl(field: AuthEmailSignupProfileField): FormControl { const formData = { value: field.defaultdata != '0' ? field.defaultdata : undefined, disabled: this.disabled, }; - return new FormControl(formData, this.required && !field.locked ? Validators.required : null); + return new FormControl(formData, { + validators: this.required && !field.locked ? Validators.required : null, + nonNullable: true, + }); } } diff --git a/src/core/components/iframe/core-iframe.html b/src/core/components/iframe/core-iframe.html index d15df751c41..22c8fabeb35 100644 --- a/src/core/components/iframe/core-iframe.html +++ b/src/core/components/iframe/core-iframe.html @@ -11,9 +11,17 @@ - + + + + + + + diff --git a/src/core/components/iframe/iframe.ts b/src/core/components/iframe/iframe.ts index 6a21b9b696b..4c49d610ee0 100644 --- a/src/core/components/iframe/iframe.ts +++ b/src/core/components/iframe/iframe.ts @@ -85,12 +85,6 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { this.initialized = true; - this.iframeWidth = (this.iframeWidth && CoreDomUtils.formatPixelsSize(this.iframeWidth)) || '100%'; - this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%'; - this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen); - this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar); - this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate); - if (this.showFullscreenOnToolbar || this.autoFullscreenOnRotate) { // Leave fullscreen when navigating. this.navSubscription = Router.events @@ -157,6 +151,22 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { * Detect changes on input properties. */ async ngOnChanges(changes: {[name: string]: SimpleChange }): Promise { + if (changes.iframeWidth) { + this.iframeWidth = (this.iframeWidth && CoreDomUtils.formatPixelsSize(this.iframeWidth)) || '100%'; + } + if (changes.iframeHeight) { + this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%'; + } + if (changes.allowFullscreen) { + this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen); + } + if (changes.showFullscreenOnToolbar) { + this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar); + } + if (changes.autoFullscreenOnRotate) { + this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate); + } + if (!changes.src) { return; } diff --git a/src/core/components/input-errors/input-errors.ts b/src/core/components/input-errors/input-errors.ts index b43c55cddf0..da973bd0dd0 100644 --- a/src/core/components/input-errors/input-errors.ts +++ b/src/core/components/input-errors/input-errors.ts @@ -40,7 +40,7 @@ import { FormControl } from '@angular/forms'; }) export class CoreInputErrorsComponent implements OnInit, OnChanges { - @Input() control?: FormControl; // Needed to be able to check the validity of the input. + @Input() control?: FormControl; // Needed to be able to check the validity of the input. @Input() errorMessages: Record = {}; // Error messages to show. Keys must be the name of the error. @Input() errorText = ''; // Set other non automatic errors. errorKeys: string[] = []; diff --git a/src/core/components/sites-list/sites-list.ts b/src/core/components/sites-list/sites-list.ts index 812cade37dc..0edb15b4fc7 100644 --- a/src/core/components/sites-list/sites-list.ts +++ b/src/core/components/sites-list/sites-list.ts @@ -47,8 +47,8 @@ export class CoreSitesListComponent { @Input() currentSiteClickable?: boolean; // If set, specify a different clickable value for current site. @Output() onSiteClicked = new EventEmitter(); - @ContentChild('siteItem') siteItemTemplate?: TemplateRef; - @ContentChild('siteLabel') siteLabelTemplate?: TemplateRef; + @ContentChild('siteItem') siteItemTemplate?: TemplateRef<{site: T; isCurrentSite: boolean}>; + @ContentChild('siteLabel') siteLabelTemplate?: TemplateRef<{site: T; isCurrentSite: boolean}>; /** * Check whether a site is clickable. diff --git a/src/core/components/swipe-slides/swipe-slides.ts b/src/core/components/swipe-slides/swipe-slides.ts index 82c856af3a2..fa3864c385b 100644 --- a/src/core/components/swipe-slides/swipe-slides.ts +++ b/src/core/components/swipe-slides/swipe-slides.ts @@ -67,7 +67,7 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe }, 0); } - @ContentChild(TemplateRef) template?: TemplateRef; // Template defined by the content. + @ContentChild(TemplateRef) template?: TemplateRef<{item: Item; active: boolean}>; // Template defined by the content. protected hostElement: HTMLElement; protected unsubscribe?: () => void; diff --git a/src/core/components/tabs/tab.ts b/src/core/components/tabs/tab.ts index d5c0be34ed3..dd276f647a8 100644 --- a/src/core/components/tabs/tab.ts +++ b/src/core/components/tabs/tab.ts @@ -68,7 +68,7 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase { @Input() id = ''; // An ID to identify the tab. @Output() ionSelect: EventEmitter = new EventEmitter(); - @ContentChild(TemplateRef) template?: TemplateRef; // Template defined by the content. + @ContentChild(TemplateRef) template?: TemplateRef; // Template defined by the content. element: HTMLElement; // The core-tab element. loaded = false; diff --git a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts index 48787de7f30..1ef6e64296a 100644 --- a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts +++ b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts @@ -66,7 +66,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, // @todo Implement ControlValueAccessor https://angular.io/api/forms/ControlValueAccessor. @Input() placeholder = ''; // Placeholder to set in textarea. - @Input() control?: FormControl; // Form control. + @Input() control?: FormControl; // Form control. @Input() name = 'core-rich-text-editor'; // Name to set to the textarea. @Input() component?: string; // The component to link the files to. @Input() componentId?: number; // An ID to use in conjunction with the component. @@ -75,7 +75,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, @Input() contextInstanceId?: number; // The instance ID related to the context. @Input() elementId?: string; // An ID to set to the element. @Input() draftExtraParams?: Record; // Extra params to identify the draft. - @Output() contentChanged: EventEmitter; + @Output() contentChanged: EventEmitter; @ViewChild('editor') editor?: ElementRef; // WYSIWYG editor. @ViewChild('textarea') textarea?: IonTextarea; // Textarea editor. @@ -190,8 +190,8 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, // Setup the editor. this.editorElement = this.editor?.nativeElement as HTMLDivElement; this.setContent(this.control?.value); - this.originalContent = this.control?.value; - this.lastDraft = this.control?.value; + this.originalContent = this.control?.value ?? undefined; + this.lastDraft = this.control?.value ?? ''; // Use paragraph on enter. // eslint-disable-next-line deprecation/deprecation @@ -261,19 +261,19 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, // Apply the new content. this.setContent(newValue); - this.originalContent = newValue; + this.originalContent = newValue ?? undefined; this.infoMessage = undefined; // Save a draft so the original content is saved. - this.lastDraft = newValue; + this.lastDraft = newValue ?? ''; CoreEditorOffline.saveDraft( this.contextLevel || '', this.contextInstanceId || 0, this.elementId || '', this.draftExtraParams || {}, this.pageInstance, - newValue, - newValue, + this.lastDraft, + this.originalContent, ); }); @@ -579,7 +579,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, * @param value text * @returns If value is null only a white space. */ - protected isNullOrWhiteSpace(value: string | null): boolean { + protected isNullOrWhiteSpace(value: string | null | undefined): boolean { if (value == null || value === undefined) { return true; } @@ -595,7 +595,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, * * @param value New content. */ - protected setContent(value: string | null): void { + protected setContent(value: string | null | undefined): void { if (!this.editorElement || !this.textarea) { return; } @@ -974,7 +974,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, return; } - const newText = this.control.value; + const newText = this.control.value ?? ''; if (this.lastDraft == newText) { // Text hasn't changed, nothing to save. diff --git a/src/core/features/login/pages/email-signup/email-signup.ts b/src/core/features/login/pages/email-signup/email-signup.ts index 43797e23334..45cb6f9fca6 100644 --- a/src/core/features/login/pages/email-signup/email-signup.ts +++ b/src/core/features/login/pages/email-signup/email-signup.ts @@ -69,8 +69,8 @@ export class CoreLoginEmailSignupPage implements OnInit { // Data for age verification. ageVerificationForm: FormGroup; - countryControl: FormControl; - signUpCountryControl?: FormControl; + countryControl: FormControl; + signUpCountryControl?: FormControl; isMinor = false; // Whether the user is minor age. ageDigitalConsentVerification?: boolean; // Whether the age verification is enabled. supportName?: string; @@ -93,7 +93,7 @@ export class CoreLoginEmailSignupPage implements OnInit { this.ageVerificationForm = this.fb.group({ age: ['', Validators.required], }); - this.countryControl = this.fb.control('', Validators.required); + this.countryControl = this.fb.control('', { validators: Validators.required, nonNullable: true }); this.ageVerificationForm.addControl('country', this.countryControl); // Create the signupForm with the basic controls. More controls will be added later. @@ -141,7 +141,7 @@ export class CoreLoginEmailSignupPage implements OnInit { */ protected completeFormGroup(): void { this.signupForm.addControl('city', this.fb.control(this.settings?.defaultcity || '')); - this.signUpCountryControl = this.fb.control(this.settings?.country || ''); + this.signUpCountryControl = this.fb.control(this.settings?.country || '', { nonNullable: true }); this.signupForm.addControl('country', this.signUpCountryControl); // Add the name fields. diff --git a/src/core/features/user/classes/base-profilefield-component.ts b/src/core/features/user/classes/base-profilefield-component.ts index 78a7fea8c82..9a61cf3270e 100644 --- a/src/core/features/user/classes/base-profilefield-component.ts +++ b/src/core/features/user/classes/base-profilefield-component.ts @@ -24,7 +24,7 @@ import { CoreUserProfileField } from '@features/user/services/user'; @Component({ template: '', }) -export abstract class CoreUserProfileFieldBaseComponent implements OnInit { +export abstract class CoreUserProfileFieldBaseComponent implements OnInit { @Input() field?: AuthEmailSignupProfileField | CoreUserProfileField; // The profile field to be rendered. @Input() signup = false; // True if editing the field in signup. Defaults to false. @@ -36,7 +36,7 @@ export abstract class CoreUserProfileFieldBaseComponent implements OnInit { @Input() contextInstanceId?: number; // The instance ID related to the context. @Input() courseId?: number; // Course ID the field belongs to (if any). It can be used to improve performance with filters. - control?: FormControl; + control?: FormControl; modelName = ''; value?: string; required?: boolean; @@ -91,13 +91,16 @@ export abstract class CoreUserProfileFieldBaseComponent implements OnInit { * * @returns Form control. */ - protected createFormControl(field: AuthEmailSignupProfileField): FormControl { + protected createFormControl(field: AuthEmailSignupProfileField): FormControl { const formData = { - value: field.defaultdata, + value: (field.defaultdata ?? '') as T, disabled: this.disabled, }; - return new FormControl(formData, this.required && !field.locked ? Validators.required : null); + return new FormControl(formData, { + validators: this.required && !field.locked ? Validators.required : null, + nonNullable: true, + }); } } diff --git a/src/core/services/utils/mimetype.ts b/src/core/services/utils/mimetype.ts index 69d6496a957..dc537b7b581 100644 --- a/src/core/services/utils/mimetype.ts +++ b/src/core/services/utils/mimetype.ts @@ -312,17 +312,12 @@ export class CoreMimetypeUtilsProvider { * @returns The lowercased extension without the dot, or undefined. */ guessExtensionFromUrl(fileUrl: string): string | undefined { - const split = CoreUrl.removeUrlAnchor(fileUrl).split('.'); + const parsed = CoreUrl.parse(fileUrl); + const split = parsed?.path?.split('.'); let extension: string | undefined; - if (split.length > 1) { - let candidate = split[split.length - 1].toLowerCase(); - // Remove params if any. - const position = candidate.indexOf('?'); - if (position > -1) { - candidate = candidate.substring(0, position); - } - + if (split && split.length > 1) { + const candidate = split[split.length - 1].toLowerCase(); if (EXTENSION_REGEX.test(candidate)) { extension = candidate; } diff --git a/src/core/utils/rxjs.ts b/src/core/utils/rxjs.ts index 63b95985643..df169319134 100644 --- a/src/core/utils/rxjs.ts +++ b/src/core/utils/rxjs.ts @@ -22,10 +22,10 @@ import { catchError, filter } from 'rxjs/operators'; * @param control Form control. * @returns Form control value observable. */ -export function formControlValue(control: FormControl): Observable { +export function formControlValue(control: FormControl): Observable { return control.valueChanges.pipe( startWithOnSubscribed(() => control.value), - filter(value => value !== null), + filter((value): value is T => value !== null), ); }