[Library | Trunk]: Fix server errors. Input add validators for chips. Subscriber-invite: Change To field to chips, add short view

git-svn-id: https://svn.driver.research-infrastructures.eu/driver/dnet40/modules/uoa-services-library/trunk/ng-openaire-library/src/app@60968 d315682c-612b-4755-9ff5-7f18f6832af3
This commit is contained in:
k.triantafyllou 2021-04-23 16:25:52 +00:00
parent 1bd9eb8ec2
commit 336f85aa97
5 changed files with 79 additions and 89 deletions

View File

@ -11,7 +11,7 @@ import {
SimpleChanges,
ViewChild
} from "@angular/core";
import {AbstractControl, FormArray, FormControl} from "@angular/forms";
import {AbstractControl, FormArray, FormControl, ValidatorFn} from "@angular/forms";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {Observable, of, Subscription} from "rxjs";
import {MatSelect} from "@angular/material/select";
@ -100,7 +100,7 @@ export interface Option {
</span>
</mat-chip>
<div [class.uk-hidden]="formControl.value" class="uk-width-expand uk-position-relative chip-input">
<input #searchInput class="uk-width-1-1" [formControl]="searchControl" [matAutocomplete]="auto"
<input #searchInput [formControl]="searchControl" [matAutocomplete]="auto"
[matChipInputFor]="chipList" [matAutocompleteConnectedTo]="origin">
<div *ngIf="placeholder && !searchInput.value" class="placeholder uk-width-1-1"
(click)="searchInput.focus()">{{placeholder}}</div>
@ -119,7 +119,7 @@ export interface Option {
<div [ngClass]="inputClass"
[attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':null"
[class.clickable]="formControl.enabled"
[class.uk-form-danger]="formControl.invalid && formControl.touched" (click)="openSelect()">
[class.uk-form-danger]="formControl.invalid && searchControl.invalid && searchControl.touched" (click)="openSelect()">
<mat-form-field class="uk-width-1-1">
<mat-chip-list #chipList>
<mat-chip *ngFor="let chip of formAsArray.controls; let i=index" [selectable]="false"
@ -130,7 +130,7 @@ export interface Option {
</span>
</mat-chip>
<div class="uk-width-expand uk-position-relative chip-input">
<input #searchInput class="uk-width-1-1" [formControl]="searchControl" [matAutocomplete]="auto"
<input #searchInput style="width: calc(100% - 8px) !important;" [formControl]="searchControl" [matAutocomplete]="auto"
[matChipInputFor]="chipList" [matAutocompleteConnectedTo]="origin"
[matChipInputAddOnBlur]="addExtraChips && searchControl.value"
(matChipInputTokenEnd)="add($event)">
@ -181,7 +181,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
/** Textarea options */
@Input('rows') rows: number = 3;
/** Select | chips available options */
@Input('options') options: Option[];
@Input('options') options: Option[] = [];
@Input('hint') hint = null;
@Input('placeholder') placeholder = '';
@Input() inputClass: string = 'input-box';
@ -200,6 +200,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
@Input() panelWidth: number = 300;
@Input() panelClass: string = null;
@Input() showOptionsOnEmpty: boolean = true;
@Input() validators: ValidatorFn[];
@Output() focusEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
/** LogoUrl information */
public secure: boolean = true;
@ -248,11 +249,9 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
this.secure = (!this.initValue || this.initValue.includes('https://'));
}
if (this.type === 'chips' || this.type === 'autocomplete') {
if(!this.options) {
console.error('Please provide options to continue');
} else {
if(this.options) {
this.filteredOptions = of(this.options);
this.searchControl = new FormControl('');
this.searchControl = new FormControl('', this.validators);
this.subscriptions.push(this.searchControl.valueChanges.subscribe(value => {
setTimeout(() => {
this.searchInput.nativeElement.focus();
@ -333,9 +332,9 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
}
add(event: MatChipInputEvent) {
if (this.addExtraChips && event.value) {
if (this.addExtraChips && event.value && this.searchControl.valid) {
this.stopPropagation();
this.formAsArray.push(new FormControl(event.value));
this.formAsArray.push(new FormControl(event.value, this.validators));
this.formAsArray.markAsDirty();
this.searchControl.setValue('');
this.searchInput.nativeElement.value = '';

View File

@ -1,5 +1,5 @@
import {Component, Input, OnDestroy, OnInit} from "@angular/core";
import {AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators} from "@angular/forms";
import {AbstractControl, FormBuilder, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms";
import {Subscriber} from "rxjs";
import {StringUtils} from "../../utils/string-utils.class";
import {Email} from "../../utils/email/email";
@ -16,17 +16,16 @@ declare var UIkit;
@Component({
selector: 'subscriber-invite',
template: `
<div class="uk-grid uk-child-width-1-1" uk-grid [formGroup]="inviteForm">
<div *ngIf="longView" class="uk-grid uk-child-width-1-1" uk-grid [formGroup]="inviteForm">
<div dashboard-input [formInput]="inviteForm.get('name')" [gridSmall]="true">
<div class="uk-text-bold field-label">
From:
</div>
</div>
<div dashboard-input [formInput]="inviteForm.get('recipients')" [gridSmall]="true">
<div class="uk-text-bold field-label uk-margin-bottom">
<div dashboard-input [formInput]="inviteForm.get('recipients')" type="chips" placeholder="Write email(s)" [addExtraChips]="true" [validators]="validators" [gridSmall]="true">
<div class="uk-text-bold field-label">
To *:
</div>
<span note>Separate multiple emails with a comma</span>
</div>
<div class="uk-grid uk-grid-small" uk-grid>
<div class="uk-text-bold field-label">
@ -34,7 +33,7 @@ declare var UIkit;
</div>
<div class="uk-width-expand">
<ckeditor *ngIf="isManager" class="form-control" formControlName="message" id="message"
debounce="400"
debounce="400" (ready)="loading = false"
[config]="{ extraAllowedContent: '* [uk-*](*) ; span', disallowedContent: 'script; *[on*]', removeButtons: 'Save,NewPage,DocProps,Preview,Print',
extraPlugins: 'divarea'}"></ckeditor>
<div *ngIf="!isManager" [innerHTML]="body.paragraphs"></div>
@ -52,17 +51,23 @@ declare var UIkit;
</div>
</div>
</div>
<div *ngIf="!longView">
<div dashboard-input [formInput]="inviteForm.get('recipients')" type="chips" placeholder="Write email(s)" [addExtraChips]="true" [validators]="validators" [gridSmall]="true"></div>
</div>
`,
styleUrls: ['subscriber-invite.component.css']
})
export class SubscriberInviteComponent implements OnInit, OnDestroy {
@Input()
public user: User;
public community: CommunityInfo
@Input()
public longView: boolean = true;
public community: CommunityInfo;
public inviteForm: FormGroup;
public email: Email;
public body: Body;
public loading: boolean = false;
public validators: ValidatorFn[] = [Validators.email, Validators.required];
public loading: boolean = true;
private subscriptions: any[] = [];
constructor(private fb: FormBuilder,
@ -71,6 +76,7 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.loading = this.longView;
this.reset();
}
@ -90,7 +96,7 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
this.unsubscribe();
this.inviteForm = this.fb.group({
name: this.fb.control('', Validators.required),
recipients: this.fb.control('', [Validators.required, this.emailsValidator]),
recipients: this.fb.array([], Validators.required),
message: this.fb.control('', Validators.required)
});
this.subscriptions.push(this.communityService.getCommunityAsObservable().subscribe(community => {
@ -102,20 +108,16 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
this.email = Composer.initializeInvitationsEmail(community.title);
this.inviteForm.get('message').setValue(this.body.paragraphs);
}));
}
emailsValidator(control: AbstractControl): ValidationErrors | null {
if (control.value === '' || !control.value || StringUtils.validateEmails(control.value)) {
return null;
if(!this.isManager) {
this.loading = false;
}
return { emails: true };
}
invite() {
this.loading = true;
this.parseRecipients();
this.body.paragraphs = this.inviteForm.value.message;
this.email.body = Composer.formatEmailBodyForInvitation(this.body);
this.email.recipients = this.inviteForm.get('recipients').value;
this.subscriptions.push(this.emailService.sendEmail(properties, this.email).subscribe(res => {
if(res['success']) {
UIkit.notification('Invitation to subscribe has been <b>sent</b>', {
@ -130,6 +132,7 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
pos: 'bottom-right'
});
}
this.reset();
this.loading = false;
},error => {
UIkit.notification('An error has occurred. Please try again later', {
@ -137,29 +140,16 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
timeout: 6000,
pos: 'bottom-right'
});
this.reset();
this.loading = false;
}));
}
public parseRecipients() {
// remove spaces
let emails = this.inviteForm.get('recipients').value.replace(/\s/g, '');
// remove commas
emails = emails.split(",");
// remove empty fields
for (let i = 0; i < emails.length; i++) {
if (!(emails[i] == "")) {
this.email.recipients.push(emails[i]);
}
}
}
get isManager() {
return Session.isPortalAdministrator(this.user) || Session.isCurator('community', this.user) || Session.isManager('community', this.community.communityId, this.user);
}
get valid() {
return this.inviteForm && this.inviteForm.valid;
return !this.loading && this.inviteForm && this.inviteForm.valid;
}
}

View File

@ -118,18 +118,12 @@ export class ConfigurationService{
filtering(page_route: string) {
let community: Portal = this.communityInformation.getValue();
let pages: Page[] = <Page[]>community.pages;
pages = pages.filter(function (page: Page) {
return page.route == page_route;
});
if (pages) {
let result = false;
if (pages['length'] > 0 && pages[0].route == page_route) {
result = pages[0].isEnabled;
}
return result;
let page = pages.find((page: Page) => page.route == page_route);
return page && page.isEnabled;
} else {
return false;
}
}

View File

@ -138,3 +138,8 @@ export const reset = {
name: 'reset',
data: '<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><polyline fill="#000" points="1 2 2 2 2 6 6 6 6 7 1 7 1 2"></polyline><path fill="none" stroke="#000" stroke-width="1.1" d="M2.1,6.548 C3.391,3.29 6.746,1 10.5,1 C15.5,1 19.5,5 19.5,10 C19.5,15 15.5,19 10.5,19 C5.5,19 1.5,15 1.5,10"></path></svg>'
}
export const send = {
name: 'send',
data: '<svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 0 24 24" width="20" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>'
}

View File

@ -11,46 +11,48 @@ export class SmoothScroll {
private lastRoute;
constructor(private router: Router) {
this.sub = router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
if (this.interval) {
clearInterval(this.interval);
}
const fragment = router.parseUrl(router.url).fragment;
if (this.lastRoute !== this.getUrl(event.url)) {
window.scrollTo({top: 0});
}
if (fragment) {
let i = 0;
this.interval = setInterval(() => {
i++;
const element = document.getElementById(fragment);
if (element) {
if (this.interval) {
if(typeof window !== "undefined") {
this.sub = router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
if (this.interval) {
clearInterval(this.interval);
}
const fragment = router.parseUrl(router.url).fragment;
if (this.lastRoute !== this.getUrl(event.url)) {
window.scrollTo({top: 0});
}
if (fragment) {
let i = 0;
this.interval = setInterval(() => {
i++;
const element = document.getElementById(fragment);
if (element) {
if (this.interval) {
clearInterval(this.interval);
}
const yOffset = -100;
let position = 0;
let interval = setInterval(() => {
if (position !== element.getBoundingClientRect().top) {
position = element.getBoundingClientRect().top;
} else {
clearInterval(interval);
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({top: y, behavior: 'smooth'});
}
}, 50);
}
if (i > 4 && this.interval) {
clearInterval(this.interval);
}
const yOffset = -100;
let position = 0;
let interval = setInterval(() => {
if (position !== element.getBoundingClientRect().top) {
position = element.getBoundingClientRect().top;
} else {
clearInterval(interval);
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({top: y, behavior: 'smooth'});
}
}, 50);
}
if (i > 4 && this.interval) {
clearInterval(this.interval);
}
}, 100);
} else {
window.scrollTo({top: 0, behavior: 'smooth'});
}, 100);
} else {
window.scrollTo({top: 0, behavior: 'smooth'});
}
this.lastRoute = this.getUrl(event.url);
}
this.lastRoute = this.getUrl(event.url);
}
});
});
}
}
private getUrl(url: string) {