[Library | explore-redesign]: Fixes on FoS and SDGs suggest modal (checked existing subjects, added loading) - In FoS fixed scrolling, search and final response of selected fields.

1. composer.ts & resultLanding.component.ts: Removed "and Technology" from "Fields of Science" typename.
2. resultLanding.component.html: Bind (suggestClicked) to <fos> instead of (feedbackClicked).
3. input.component.ts: [Bug fix] Added check in "filter()" method if option.label.
4. sdg-selection.component.ts: [Bug fix] Set this.loading = false; inside subscribe.
5. fos.component.ts: In title, updated FOS to FoS.
6. sdg-fos-suggest.module.ts: Imported LoadingModule.
7. sdg-fos-suggest.component.ts: Show loading when sending email | Do not initialize <sdg-selection> and <fos-selection> before subjects have value (this way inputs of existing fos/sdgs are checked).
8. fos-selection.component.ts:
   a. Added @Input() inModal: boolean = false; to set activeSection, when inModal, instead of navigating with fragment.
   b. [Bug fix] Set this.loading = false; inside subscribe.
   c. Call this.setObserver(); both in ngOnInit and when keyword changes.
   d. Updated threshold of observer from 0.25 to 0.1 (large FoS categories were never triggered on scrolling, because less than 25% of their content was visible).
   e. Updated fosOptions to Map<string, boolean> to track easily checked inputs and updated getSelectedSubjects() accordingly.
9. fos-selection.component.html: In search-input of keyword no options are needed | Updated ngModel of checkbox inputs.
10. cache-interceptor.service.ts: Added check if (!properties.useLongCache) to skip cache.
This commit is contained in:
Konstantinos Triantafyllou 2023-02-20 20:54:26 +02:00 committed by konstantina.galouni
parent f17b25d66f
commit fc9e77b887
12 changed files with 65 additions and 41 deletions

View File

@ -49,6 +49,11 @@ export class CacheInterceptorService implements HttpInterceptor {
} }
public checkForCachedRequests(url){ public checkForCachedRequests(url){
// Do not call cache when it is not enabled at all.
// This property is not correctly used here. A new property should be introduced. For now it is ok.
if (!properties.useLongCache) {
return false;
}
if(url.indexOf("refine=true") !== -1 || url.indexOf("/count?format=json") !== -1 || url.indexOf("relresulttype%3Dpublication") !== -1) { if(url.indexOf("refine=true") !== -1 || url.indexOf("/count?format=json") !== -1 || url.indexOf("relresulttype%3Dpublication") !== -1) {
return this.cachingRequests.some(partUrl => (url.indexOf(partUrl) !== -1)); return this.cachingRequests.some(partUrl => (url.indexOf(partUrl) !== -1));
} }

View File

@ -4,7 +4,7 @@
<div *ngIf="!loading"> <div *ngIf="!loading">
<div class="uk-visible@m"> <div class="uk-visible@m">
<div #searchElement class="uk-flex uk-flex-right uk-margin-small-bottom"> <div #searchElement class="uk-flex uk-flex-right uk-margin-small-bottom">
<div search-input [searchControl]="keywordControl" [options]="fosOptions" iconPosition="left" placeholder="Write a key word to filter the content" <div search-input [searchControl]="keywordControl" iconPosition="left" placeholder="Write a key word to filter the content"
searchInputClass="border-bottom" class="uk-width-large"></div> searchInputClass="border-bottom" class="uk-width-large"></div>
</div> </div>
</div> </div>
@ -30,7 +30,7 @@
<div class="uk-width-1-1 uk-hidden@m"> <div class="uk-width-1-1 uk-hidden@m">
<div class="uk-sticky uk-blur-background" uk-sticky> <div class="uk-sticky uk-blur-background" uk-sticky>
<div class="uk-flex uk-flex-center uk-margin-small-bottom"> <div class="uk-flex uk-flex-center uk-margin-small-bottom">
<div search-input [searchControl]="keywordControl" [options]="fosOptions" iconPosition="left" placeholder="Write a key word to filter the content" <div search-input [searchControl]="keywordControl" iconPosition="left" placeholder="Write a key word to filter the content"
searchInputClass="border-bottom" class="uk-width-large"></div> searchInputClass="border-bottom" class="uk-width-large"></div>
</div> </div>
<div #tabs class="uk-slider uk-position-relative" uk-slider="finite: true"> <div #tabs class="uk-slider uk-position-relative" uk-slider="finite: true">
@ -77,7 +77,7 @@
</h3> </h3>
<div *ngFor="let subChild of child.children" class="uk-margin-xsmall-bottom uk-text-truncate"> <div *ngFor="let subChild of child.children" class="uk-margin-xsmall-bottom uk-text-truncate">
<label [class.uk-text-bolder]="subjects?.includes(subChild.id)"> <label [class.uk-text-bolder]="subjects?.includes(subChild.id)">
<input [(ngModel)]="subChild.checked" <input [ngModel]="fosOptions.get(subChild.id)" (ngModelChange)="fosOptions.set(subChild.id, $event)"
type="checkbox" class="uk-checkbox uk-margin-small-right"> type="checkbox" class="uk-checkbox uk-margin-small-right">
<span [title]="subChild.id">{{subChild.id}}</span> <span [title]="subChild.id">{{subChild.id}}</span>
</label> </label>
@ -110,10 +110,10 @@
</a> --> </a> -->
</h3> </h3>
<div *ngFor="let subSubItem of subItem.children" class="uk-margin-xsmall-bottom uk-text-truncate"> <div *ngFor="let subSubItem of subItem.children" class="uk-margin-xsmall-bottom uk-text-truncate">
<label> <label [class.uk-text-bolder]="subjects?.includes(subSubItem.id)">
<input [checked]="subjects.includes(subSubItem.id)" <input [ngModel]="fosOptions.get(subSubItem.id)" (ngModelChange)="fosOptions.set(subSubItem.id, $event)"
type="checkbox" class="uk-checkbox uk-margin-small-right"> type="checkbox" class="uk-checkbox uk-margin-small-right">
<span [innerHTML]="highlightKeyword(subSubItem.id)" [title]="subChild.id"></span> <span [innerHTML]="highlightKeyword(subSubItem.id)" [title]="subSubItem.id"></span>
</label> </label>
<!-- <a [routerLink]="properties.searchLinkToResults" [queryParams]="{'fos': urlEncodeAndQuote(subSubItem.id)}" <!-- <a [routerLink]="properties.searchLinkToResults" [queryParams]="{'fos': urlEncodeAndQuote(subSubItem.id)}"
class="uk-link-text" [innerHTML]="highlightKeyword(subSubItem.id)"> class="uk-link-text" [innerHTML]="highlightKeyword(subSubItem.id)">

View File

@ -20,12 +20,13 @@ declare var UIkit;
export class FosSelectionComponent { export class FosSelectionComponent {
public properties: EnvProperties = properties; public properties: EnvProperties = properties;
@Input() subjects: string[]; @Input() subjects: string[];
@Input() inModal: boolean = false;
@Input() contentHeight: number = 0; @Input() contentHeight: number = 0;
@ViewChild("searchElement") searchElement: ElementRef; @ViewChild("searchElement") searchElement: ElementRef;
public loading: boolean; public loading: boolean;
public fos: any[] = []; public fos: any[] = [];
public fosOptions: any = []; public fosOptions: Map<string, boolean>;
public activeSection: string; public activeSection: string;
public keywordControl: FormControl; public keywordControl: FormControl;
@ -54,6 +55,7 @@ export class FosSelectionComponent {
this.httpClient.get(this.properties.domain+'/assets/common-assets/vocabulary/fos.json').subscribe(data => { this.httpClient.get(this.properties.domain+'/assets/common-assets/vocabulary/fos.json').subscribe(data => {
this.fos = data['fos']; this.fos = data['fos'];
this.convertFosToOptions(); this.convertFosToOptions();
this.convertFosToOptions();
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
setTimeout(()=> { setTimeout(()=> {
let slider = UIkit.slider(this.tabs.nativeElement); let slider = UIkit.slider(this.tabs.nativeElement);
@ -70,7 +72,9 @@ export class FosSelectionComponent {
} else { } else {
this.activeSection = this.fos[0].id; this.activeSection = this.fos[0].id;
} }
this.cdr.detectChanges();
this.setObserver();
this.cdr.detectChanges();
}); });
} }
this.subscriptions.push(this.route.fragment.subscribe(fragment => { this.subscriptions.push(this.route.fragment.subscribe(fragment => {
@ -95,8 +99,8 @@ export class FosSelectionComponent {
}); });
} }
})); }));
}); this.loading = false;
this.loading = false; });
} }
ngOnDestroy() { ngOnDestroy() {
@ -119,11 +123,19 @@ export class FosSelectionComponent {
clearTimeout(this.timeout); clearTimeout(this.timeout);
} }
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
this._router.navigate(['./'], {fragment: entry.target.id, relativeTo: this.route, state: {disableScroll: true}}); if(!this.inModal) {
this._router.navigate(['./'], {
fragment: entry.target.id,
relativeTo: this.route,
state: {disableScroll: true}
});
} else {
this.activeSection = entry.target.id;
}
}, 200); }, 200);
} }
}); });
}, {threshold: 0.25, rootMargin: '-100px'}); }, {threshold: 0.1, rootMargin: '-100px'});
this.fos.forEach(fos => { this.fos.forEach(fos => {
let element = document.getElementById(fos.id); let element = document.getElementById(fos.id);
if(element) { if(element) {
@ -133,15 +145,15 @@ export class FosSelectionComponent {
} }
convertFosToOptions() { convertFosToOptions() {
this.fosOptions = []; this.fosOptions = new Map<string, boolean>();
this.fos.forEach(fos => { this.fos.forEach(fos => {
this.fosOptions.push({id: fos.id, checked: false}); this.fosOptions.set(fos.id, false);
if(fos.children) { if(fos.children) {
fos.children.forEach(child => { fos.children.forEach(child => {
this.fosOptions.push({id: child.id, checked: false}); this.fosOptions.set(child.id, false);
if(child.children) { if(child.children) {
child.children.forEach(child2 => { child.children.forEach(child2 => {
this.fosOptions.push({id: child2.id, checked: this.subjects?.includes(child2.id)}); this.fosOptions.set(child2.id, this.subjects?.includes(child2.id));
}); });
} }
}); });
@ -191,7 +203,10 @@ export class FosSelectionComponent {
} }
public getSelectedSubjects() { public getSelectedSubjects() {
return this.fosOptions.filter(sub => sub.checked == true); let checked = Array.from(this.fosOptions, function (entry) {
return {id: entry[0], checked: entry[1]};
});
return checked.filter(sub => sub.checked == true);
} }
get calculatedHeight(): number { get calculatedHeight(): number {

View File

@ -172,7 +172,7 @@ import {Organization, Project} from "../../utils/result-preview/result-preview";
<!-- Projects --> <!-- Projects -->
<ng-container *ngIf="projects && projects.length > 0"> <ng-container *ngIf="projects && projects.length > 0">
<span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span> <span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span>
<span uk-tooltip="Project" *ngFor="let project of projects.slice(0,10) let i=index"> <span uk-tooltip="Project" *ngFor="let project of projects.slice(0,3) let i=index">
<span class="space"> <span class="space">
{{project.funderShortname ? project.funderShortname : project.funderName}} {{project.funderShortname ? project.funderShortname : project.funderName}}
</span> </span>
@ -183,25 +183,25 @@ import {Organization, Project} from "../../utils/result-preview/result-preview";
<span *ngIf="project.code"> ({{project.code}})</span> <span *ngIf="project.code"> ({{project.code}})</span>
<span *ngIf="i < projects.length-1">, </span> <span *ngIf="i < projects.length-1">, </span>
</span> </span>
<span *ngIf="projects.length > 10">...</span> <span *ngIf="projects.length > 3">...</span>
</ng-container> </ng-container>
<!-- Organizations --> <!-- Organizations -->
<ng-container *ngIf="organizations && organizations.length > 0"> <ng-container *ngIf="organizations && organizations.length > 0">
<span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span> <span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span>
<span uk-tooltip="Partner" *ngFor="let organization of organizations.slice(0,10) let i=index"> <span uk-tooltip="Partner" *ngFor="let organization of organizations.slice(0,3) let i=index">
<span class="space">{{organization.name}}</span> <span class="space">{{organization.name}}</span>
<span *ngIf="(i < organizations.length-1) && (i < 9)">,</span> <span *ngIf="i < organizations.length-1">, </span>
</span> </span>
<span *ngIf="organizations.length > 10">...</span> <span *ngIf="organizations.length > 3">...</span>
</ng-container> </ng-container>
<!-- Subjects --> <!-- Subjects -->
<ng-container *ngIf="subjects && subjects.length > 0"> <ng-container *ngIf="subjects && subjects.length > 0">
<span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span> <span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span>
<span uk-tooltip="Subject" *ngFor="let subject of subjects.slice(0,10) let i = index"> <span uk-tooltip="Subject" *ngFor="let subject of subjects.slice(0,3) let i = index">
<span class="space">{{subject}}</span> <span class="space">{{subject}}</span>
<span>{{(i < (subjects.slice(0, 10).length - 1)) ? "," : ""}}</span> <span *ngIf="i < subjects.length - 1">, </span>
</span> </span>
<span *ngIf="subjects.length > 10">...</span> <span *ngIf="subjects.length > 3">...</span>
</ng-container> </ng-container>
<ng-container *ngIf="provenanceAction"> <ng-container *ngIf="provenanceAction">
<span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span> <span class="uk-margin-xsmall-left uk-margin-xsmall-right bullet"></span>

View File

@ -38,7 +38,7 @@ import {StringUtils} from "../../utils/string-utils.class";
<div class="uk-grid uk-grid-small uk-grid-divider" uk-grid> <div class="uk-grid uk-grid-small uk-grid-divider" uk-grid>
<div class="uk-width-1-4 uk-text-meta"> <div class="uk-width-1-4 uk-text-meta">
<div class="uk-text-xsmall" style="color: #EEB204">Beta</div> <div class="uk-text-xsmall" style="color: #EEB204">Beta</div>
Fields of Science (FOS) Fields of Science (FoS)
</div> </div>
<div class="uk-width-expand"> <div class="uk-width-expand">
<div *ngFor="let subject of subjects.slice(0, viewAll?subjects.length:threshold); let i=index" class="uk-text-truncate"> <div *ngFor="let subject of subjects.slice(0, viewAll?subjects.length:threshold); let i=index" class="uk-text-truncate">

View File

@ -15,10 +15,10 @@ import {StringUtils} from "../../../utils/string-utils.class";
template: ` template: `
<modal-alert #selectionModal [large]="true" (alertOutput)="modalOutput()" (cancelOutput)="modalCancel()" <modal-alert #selectionModal [large]="true" (alertOutput)="modalOutput()" (cancelOutput)="modalCancel()"
[okDisabled]="!sent && !selectionStep1 && (form.invalid || sending)"> [okDisabled]="!sent && !selectionStep1 && (form.invalid || sending)">
<sdg-selection *ngIf="subjectType == 'sdg'" #selection [class.uk-hidden]="!selectionStep1" <sdg-selection *ngIf="subjects && subjectType == 'sdg'" #selection [class.uk-hidden]="!selectionStep1"
[subjects]="subjects" [entityType]="entityType"></sdg-selection> [subjects]="subjects" [entityType]="entityType"></sdg-selection>
<fos-selection *ngIf="subjectType == 'fos'" #selection [class.uk-hidden]="!selectionStep1" <fos-selection *ngIf="subjects && subjectType == 'fos'" #selection [class.uk-hidden]="!selectionStep1"
[subjects]="subjects" [contentHeight]="selectionModal.bodyHeight"></fos-selection> [subjects]="subjects" [contentHeight]="selectionModal.bodyHeight" [inModal]="true"></fos-selection>
<div [class.uk-hidden]="selectionStep1"> <div [class.uk-hidden]="selectionStep1">
<div class="uk-flex uk-flex-column uk-flex-middle"> <div class="uk-flex uk-flex-column uk-flex-middle">
<ng-container *ngIf="!sent && !error"> <ng-container *ngIf="!sent && !error">
@ -29,8 +29,9 @@ import {StringUtils} from "../../../utils/string-utils.class";
<span note>(Optional)</span> <span note>(Optional)</span>
</div> </div>
<div> <div>
<re-captcha (resolved)="handleRecaptcha($event)" [siteKey]="properties.reCaptchaSiteKey"> <re-captcha (resolved)="handleRecaptcha($event)" [siteKey]="properties.reCaptchaSiteKey"
</re-captcha> [ngClass]="sending ? 'uk-hidden':''"></re-captcha>
<loading [ngClass]="sending ? '':'uk-hidden'"></loading>
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="sent"> <ng-container *ngIf="sent">
@ -120,6 +121,7 @@ export class SdgFosSuggestComponent {
this.selectionModal.alertTitle = "Please send your feedback on most relevant Fields of Science for this "+this.getEntityName(this.entityType)+"."; this.selectionModal.alertTitle = "Please send your feedback on most relevant Fields of Science for this "+this.getEntityName(this.entityType)+".";
} }
} else { } else {
this.sending = true;
// email functionality // email functionality
this.form.get("subjects").setValue(this.selection.getSelectedSubjects().map(subject => subject.id)); this.form.get("subjects").setValue(this.selection.getSelectedSubjects().map(subject => subject.id));
this.subscriptions.push(this.emailService.contact(this.properties, this.subscriptions.push(this.emailService.contact(this.properties,
@ -142,7 +144,7 @@ export class SdgFosSuggestComponent {
this.selectionModal.previousButton = false; this.selectionModal.previousButton = false;
this.selectionModal.stayOpen = false; this.selectionModal.stayOpen = false;
} }
// this.sending = false; this.sending = false;
}, error => { }, error => {
console.log(error); console.log(error);
this.error = true; this.error = true;

View File

@ -8,10 +8,11 @@ import {InputModule} from "../../../sharedComponents/input/input.module";
import {SdgFosSuggestComponent} from "./sdg-fos-suggest.component"; import {SdgFosSuggestComponent} from "./sdg-fos-suggest.component";
import {AlertModalModule} from "../../../utils/modal/alertModal.module"; import {AlertModalModule} from "../../../utils/modal/alertModal.module";
import {IconsModule} from "../../../utils/icons/icons.module"; import {IconsModule} from "../../../utils/icons/icons.module";
import {LoadingModule} from "../../../utils/loading/loading.module";
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, FormsModule, InputModule, SdgSelectionModule, FosSelectionModule, RecaptchaModule, AlertModalModule, IconsModule CommonModule, FormsModule, InputModule, SdgSelectionModule, FosSelectionModule, RecaptchaModule, AlertModalModule, IconsModule, LoadingModule
], ],
declarations: [ declarations: [
SdgFosSuggestComponent SdgFosSuggestComponent

View File

@ -719,7 +719,7 @@
</div> </div>
<!-- FOS --> <!-- FOS -->
<div *ngIf="resultLandingInfo.fos && resultLandingInfo.fos.length > 0 && !viewAllMobile" class="uk-margin-small-top"> <div *ngIf="resultLandingInfo.fos && resultLandingInfo.fos.length > 0 && !viewAllMobile" class="uk-margin-small-top">
<fos [subjects]="resultLandingInfo.fos" (feedbackClicked)="feedbackClicked('Fields of Science and Technology (FOS)')" <fos [subjects]="resultLandingInfo.fos" (suggestClicked)="suggestMobileClicked($event)"
(viewAllClicked)="viewAllMobile=$event; openFsModal(fosFsModal, 'Fields of Science (FoS)')"></fos> (viewAllClicked)="viewAllMobile=$event; openFsModal(fosFsModal, 'Fields of Science (FoS)')"></fos>
</div> </div>
</div> </div>
@ -871,7 +871,7 @@
<fs-modal #fosFsModal *ngIf="isMobile" [classTitle]="'uk-background-primary-opacity'" (cancelEmitter)="fosFsModalCancelled()"> <fs-modal #fosFsModal *ngIf="isMobile" [classTitle]="'uk-background-primary-opacity'" (cancelEmitter)="fosFsModalCancelled()">
<ng-container *ngIf="resultLandingInfo.fos && resultLandingInfo.fos.length > 0"> <ng-container *ngIf="resultLandingInfo.fos && resultLandingInfo.fos.length > 0">
<fos *ngIf="viewAllMobile=='fos'" [subjects]="resultLandingInfo.fos" [viewAll]="true"(viewAllClicked)="viewAllMobile=$event"></fos> <fos *ngIf="viewAllMobile=='fos'" [subjects]="resultLandingInfo.fos" [viewAll]="true" (suggestClicked)="suggestMobileClicked($event)"></fos>
<sdg-fos-suggest *ngIf="viewAllMobile=='fosSuggest'" #sdgFosSuggest [title]="resultLandingInfo.title" [entityType]="resultLandingInfo.resultType"></sdg-fos-suggest> <sdg-fos-suggest *ngIf="viewAllMobile=='fosSuggest'" #sdgFosSuggest [title]="resultLandingInfo.title" [entityType]="resultLandingInfo.resultType"></sdg-fos-suggest>
</ng-container> </ng-container>
</fs-modal> </fs-modal>

View File

@ -116,7 +116,7 @@ export class ResultLandingComponent {
'Title', 'Authors', 'Access rights', 'Title', 'Authors', 'Access rights',
'Publisher information', 'Funding Information', 'Publisher information', 'Funding Information',
'Persistent identifiers', 'Sustainable Development Goals (SDGs)', 'Persistent identifiers', 'Sustainable Development Goals (SDGs)',
'Fields of Science and Technology (FOS)', 'Other']; 'Fields of Science (FoS)', 'Other'];
public pidsArrayString: string = ""; public pidsArrayString: string = "";
public identifier: Identifier; public identifier: Identifier;
@ -993,6 +993,7 @@ export class ResultLandingComponent {
this.sdgFosSuggest.subjects=this.resultLandingInfo.fos; this.sdgFosSuggest.subjects=this.resultLandingInfo.fos;
this.sdgFosSuggest.subjectType="fos"; this.sdgFosSuggest.subjectType="fos";
} }
this.cdr.detectChanges();
this.sdgFosSuggest.openSelectionModal(); this.sdgFosSuggest.openSelectionModal();
} }

View File

@ -28,8 +28,8 @@ export class SdgSelectionComponent {
this.sdgs.push({code: element.code, id: element.id, label: element.label, html: element.html, checked: this.subjects?.includes(element.id)}); this.sdgs.push({code: element.code, id: element.id, label: element.label, html: element.html, checked: this.subjects?.includes(element.id)});
}); });
this.sdgs.push({code: '18', id: 'No SDGs are relevant for this ' + this.getEntityName(this.entityType), label: 'Not relevant', html: 'Not relevant', checked: false}); this.sdgs.push({code: '18', id: 'No SDGs are relevant for this ' + this.getEntityName(this.entityType), label: 'Not relevant', html: 'Not relevant', checked: false});
}); this.loading = false;
this.loading = false; });
} }
public get firstColumn() { public get firstColumn() {

View File

@ -476,7 +476,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
return (this.showOptionsOnEmpty) ? options : []; return (this.showOptionsOnEmpty) ? options : [];
} }
const filterValue = value.toString().toLowerCase(); const filterValue = value.toString().toLowerCase();
options = options.filter(option => option.label.toLowerCase().indexOf(filterValue) != -1); options = options.filter(option => (option.label && option.label.toLowerCase().indexOf(filterValue) != -1));
this.selectedIndex = options.findIndex(option => option.value === this.formControl.value); this.selectedIndex = options.findIndex(option => option.value === this.formControl.value);
if (this.selectedIndex === -1) { if (this.selectedIndex === -1) {
this.selectedIndex = 0; this.selectedIndex = 0;

View File

@ -142,7 +142,7 @@ export class Composer {
if(subjectType == "sdg") { if(subjectType == "sdg") {
typeName = "Sustainable Development Goals (SDGs)"; typeName = "Sustainable Development Goals (SDGs)";
} else { } else {
typeName = "Fields of Science and Technology (FoS)"; typeName = "Fields of Science (FoS)";
} }
let email: Email = new Email(); let email: Email = new Email();