Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ontology): bring back the name input field (DEV-157) #559

Merged
merged 7 commits into from Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -144,7 +144,7 @@ export class StringLiteralInputComponent implements OnInit, OnChanges {

const form = this.form;
const control = form.get('text');
this.touched.emit(control && control.dirty);
this.touched.emit(control.dirty || control.touched);

this.updateStringLiterals(this.language, this.form.controls.text.value);

Expand Down
Expand Up @@ -13,6 +13,7 @@ import { CacheService } from 'src/app/main/cache/cache.service';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { existingNamesValidator } from 'src/app/main/directive/existing-name/existing-name.directive';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
import { CustomRegex } from 'src/app/workspace/resource/values/custom-regex';
import { OntologyService } from '../ontology.service';

export interface NewOntology {
Expand Down Expand Up @@ -52,9 +53,6 @@ export class OntologyFormComponent implements OnInit {

lastModificationDate: string;

// regex to check ontology name: shouldn't start with a number or with 'v' followed by a number, spaces or special characters are not allowed
nameRegex = /^(?![vV]+[0-9])+^([a-zA-Z])[a-zA-Z0-9_.-]*$/;

// ontology name must not contain one of the following words
forbiddenNames: string[] = [
'knora',
Expand Down Expand Up @@ -183,7 +181,7 @@ export class OntologyFormComponent implements OnInit {
Validators.minLength(this.nameMinLength),
Validators.maxLength(this.nameMaxLength),
existingNamesValidator(this.existingNames),
Validators.pattern(this.nameRegex)
Validators.pattern(CustomRegex.ID_NAME_REGEX)
]),
label: new FormControl({
value: this.ontologyLabel, disabled: false
Expand Down
13 changes: 12 additions & 1 deletion src/app/project/ontology/ontology.service.ts
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Cardinality } from '@dasch-swiss/dsp-js';
import { Cardinality, Constants } from '@dasch-swiss/dsp-js';

/**
* helper methods for the ontology editor
Expand Down Expand Up @@ -46,6 +46,17 @@ export class OntologyService {
return array[pos].toLowerCase();
}

/**
* get the name from the iri
* @param iri
* @returns name from iri
*/
getNameFromIri(iri: string): string {
const array = iri.split(Constants.HashDelimiter);

return array[1];
}

/**
* convert cardinality values (multiple? & required?) from form to DSP-JS cardinality enum 1-n, 0-n, 1, 0-1
* @param {boolean} multiple
Expand Down
Expand Up @@ -4,15 +4,17 @@
<form [formGroup]="propertyForm" class="form-content">

<p *ngIf="resClassIri && propertyInfo.propDef" class="note warning mat-caption center">
This property already exists. If you want to modify it, go to the "properties" view.
You're adding an already existing property to this class.
The property can't modified here. If you want to modify it,
go to the "properties" view.
<!-- Be careful when editing it, it could have an effect in other resource classes if it is used there. -->
</p>

<div class="center">

<!-- property type -->
<mat-form-field *ngIf="propertyInfo.propType" class="large-field property-type">
<span matPrefix class="property-type-icon">
<mat-form-field *ngIf="propertyInfo.propType" class="large-field ontology-form-field">
<span matPrefix class="ontology-prefix-icon">
<mat-icon>{{propertyForm.controls['propType'].value.icon}}</mat-icon>&nbsp;
</span>
<mat-label>Property type</mat-label>
Expand All @@ -31,20 +33,40 @@
</mat-select>
</mat-form-field>

<!-- name -->
<mat-form-field class="large-field ontology-form-field">
<span matPrefix class="ontology-prefix-icon">
<mat-icon>fingerprint</mat-icon>&nbsp;
</span>
<mat-label>Property name *</mat-label>
<input matInput formControlName="name" >
<mat-hint class="ontology-error-with-prefix" *ngIf="formErrors.name">
{{formErrors.name}}
</mat-hint>
</mat-form-field>

<!-- label -->
<div class="large-field string-literal-container">
<app-string-literal-input [placeholder]="'Property label *'" [value]="labels" [disabled]="(resClassIri && propertyInfo.propDef)"
(dataChanged)="handleData($event, 'labels')">
<app-string-literal-input
[placeholder]="'Property label *'"
[value]="labels"
[disabled]="(resClassIri && propertyInfo.propDef)"
(touched)="labelsTouched = $event"
(dataChanged)="handleData($event, 'label')">
</app-string-literal-input>
<mat-hint class="string-literal-error" *ngIf="!labels.length">
Label is required
<mat-hint class="string-literal-error" *ngIf="formErrors.label">
{{ formErrors.label }}
</mat-hint>
</div>

<!-- comment/description -->
<div class="large-field string-literal-container">
<app-string-literal-input [textarea]="true" [placeholder]="'Comment'" [value]="comments" [disabled]="(resClassIri && propertyInfo.propDef)"
(dataChanged)="handleData($event, 'comments')">
<app-string-literal-input
[placeholder]="'Comment'"
[value]="comments"
[disabled]="(resClassIri && propertyInfo.propDef)"
[textarea]="true"
(dataChanged)="handleData($event, 'comment')">
</app-string-literal-input>
</div>

Expand Down
Expand Up @@ -26,23 +26,6 @@
}
}

.property-type {
.property-type-icon {
width: 36px;
padding: 0 8px;
display: block;
}

mat-label,
mat-select,
input {
margin-left: 12px;
}
mat-select {
width: calc(100% - 12px);
}
}

.cardinality {

.mat-slide-toggle {
Expand Down
Expand Up @@ -243,17 +243,17 @@ describe('PropertyFormComponent', () => {

});

it('should update labels when the value changes', () => {
it('should update labels when the value changes; error message should disapear', () => {

const hostCompDe = simpleTextHostFixture.debugElement;
const submitButton: DebugElement = hostCompDe.query(By.css('button.submit'));
expect(submitButton.nativeElement.innerText).toContain('Update');

simpleTextHostComponent.propertyFormComponent.handleData([], 'labels');
simpleTextHostComponent.propertyFormComponent.handleData([{ language: 'de', value: 'New Label' }], 'label');
simpleTextHostFixture.detectChanges();

const formInvalidMessageDe: DebugElement = hostCompDe.query(By.css('mat-hint'));
expect(formInvalidMessageDe.nativeElement.innerText).toEqual(' Label is required ');
const formInvalidMessageDe: DebugElement = hostCompDe.query(By.css('string-literal-error'));
expect(formInvalidMessageDe).toBeFalsy();

});

Expand Down
58 changes: 47 additions & 11 deletions src/app/project/ontology/property-form/property-form.component.ts
Expand Up @@ -20,7 +20,9 @@ import {
} from '@dasch-swiss/dsp-js';
import { CacheService } from 'src/app/main/cache/cache.service';
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
import { existingNamesValidator } from 'src/app/main/directive/existing-name/existing-name.directive';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
import { CustomRegex } from 'src/app/workspace/resource/values/custom-regex';
import { AutocompleteItem } from 'src/app/workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/operator';
import { DefaultProperties, DefaultProperty, PropertyCategory, PropertyInfoObject } from '../default-data/default-properties';
import { OntologyService } from '../ontology.service';
Expand Down Expand Up @@ -65,11 +67,17 @@ export class PropertyFormComponent implements OnInit {
propertyForm: FormGroup;

formErrors = {
'name': '',
'label': '',
'guiAttr': ''
};

validationMessages = {
'name': {
'required': 'Name is required.',
'existingName': 'This name is already taken. Please choose another one.',
'pattern': 'Name shouldn\'t start with a number or v + number and spaces or special characters (except dash, dot and underscore) are not allowed.'
},
'label': {
'required': 'Label is required.',
},
Expand Down Expand Up @@ -101,17 +109,23 @@ export class PropertyFormComponent implements OnInit {
error = false;

labels: StringLiteral[] = [];
labelsTouched: boolean;
comments: StringLiteral[] = [];
guiAttributes: string[] = [];

// list of existing property names
existingNames: [RegExp] = [
new RegExp('anEmptyRegularExpressionWasntPossible')
];

dspConstants = Constants;

constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
private _cache: CacheService,
private _errorHandler: ErrorHandlerService,
private _fb: FormBuilder,
private _ontologyService: OntologyService
private _os: OntologyService
) { }

ngOnInit() {
Expand All @@ -127,6 +141,16 @@ export class PropertyFormComponent implements OnInit {
// a) in case of link value:
// set list of resource classes from response; needed for linkValue
this.resourceClasses = response.getAllClassDefinitions();

// set list of all existing property names to avoid same name twice
Object.entries(this.ontology.properties).forEach(
([key]) => {
const name = this._os.getNameFromIri(key);
this.existingNames.push(
new RegExp('(?:^|W)' + name.toLowerCase() + '(?:$|W)')
);
}
);
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
Expand Down Expand Up @@ -168,7 +192,7 @@ export class PropertyFormComponent implements OnInit {

// slice array
// this slice value will be kept
// because there was the idea to shorten the array of restrcited elements
// because there was the idea to shorten the array of restricted elements
// in case e.g. richtext can't be changed to simple text, then we shouldn't list the simple text item
const slice = 0;

Expand All @@ -181,6 +205,14 @@ export class PropertyFormComponent implements OnInit {
}

this.propertyForm = this._fb.group({
'name': new FormControl({
value: (this.propertyInfo.propDef ? this._os.getNameFromIri(this.propertyInfo.propDef.id) : ''),
disabled: this.propertyInfo.propDef
}, [
Validators.required,
existingNamesValidator(this.existingNames),
Validators.pattern(CustomRegex.ID_NAME_REGEX)
]),
'propType': new FormControl({
value: this.propertyInfo.propType,
disabled: disablePropType || this.resClassIri
Expand Down Expand Up @@ -217,11 +249,9 @@ export class PropertyFormComponent implements OnInit {
return;
}

const form = this.propertyForm;

Object.keys(this.formErrors).map(field => {
this.formErrors[field] = '';
const control = form.get(field);
const control = this.propertyForm.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
Object.keys(control.errors).map(key => {
Expand All @@ -232,14 +262,20 @@ export class PropertyFormComponent implements OnInit {
});
}

handleData(data: StringLiteral[], type: string) {
handleData(data: StringLiteral[], type: 'label' | 'comment') {

switch (type) {
case 'labels':
case 'label':
this.labels = data;
const messages = this.validationMessages[type];
this.formErrors[type] = '';

if (this.labelsTouched && !this.labels.length) {
this.formErrors[type] = messages['required'];
}
break;

case 'comments':
case 'comment':
this.comments = data;
break;
}
Expand Down Expand Up @@ -420,7 +456,7 @@ export class PropertyFormComponent implements OnInit {
// create mode: new property incl. gui type and attribute
// submit property
// set resource property name / id: randomized string
const uniquePropName: string = this._ontologyService.setUniqueName(this.ontology.id);
// const uniquePropName: string = this._os.setUniqueName(this.ontology.id);

const onto = new UpdateOntology<CreateResourceProperty>();

Expand All @@ -429,7 +465,7 @@ export class PropertyFormComponent implements OnInit {

// prepare payload for property
const newResProp = new CreateResourceProperty();
newResProp.name = uniquePropName;
newResProp.name = this.propertyForm.controls['name'].value;
newResProp.label = this.labels;
newResProp.comment = (this.comments.length ? this.comments : this.labels);
const guiAttr = this.propertyForm.controls['guiAttr'].value;
Expand Down Expand Up @@ -488,7 +524,7 @@ export class PropertyFormComponent implements OnInit {

const propCard: IHasProperty = {
propertyIndex: prop.id,
cardinality: this._ontologyService.translateCardinality(this.propertyForm.value.multiple, this.propertyForm.value.required),
cardinality: this._os.translateCardinality(this.propertyForm.value.multiple, this.propertyForm.value.required),
guiOrder: this.guiOrder // add new property to the end of current list of properties
};

Expand Down
Expand Up @@ -5,11 +5,26 @@
<div class="resource-class-data">
<div class="center more-space-top">

<!-- name -->
<mat-form-field class="large-field ontology-form-field">
<span matPrefix class="ontology-prefix-icon">
<mat-icon>fingerprint</mat-icon>&nbsp;
</span>
<mat-label>Property name *</mat-label>
<input matInput formControlName="name" >
<mat-hint class="ontology-error-with-prefix" *ngIf="formErrors.name">
{{formErrors.name}}
</mat-hint>
</mat-form-field>

<!-- label -->
<div class="large-field string-literal-container">
<app-string-literal-input [placeholder]="'Label *'" [value]="resourceClassLabels"
<div class="large-field string-literal-container more-space-top">
<app-string-literal-input
[placeholder]="'Label *'"
[value]="resourceClassLabels"
(enter)="submitData()"
(dataChanged)="handleData($event, 'labels')">
(touched)="resourceClassLabelsTouched = $event"
(dataChanged)="handleData($event, 'label')">
</app-string-literal-input>
<mat-hint class="string-literal-error" *ngIf="formErrors.label">
{{ formErrors.label }}
Expand All @@ -18,8 +33,16 @@

<!-- description -->
<div class="large-field string-literal-container more-space-top">
<app-string-literal-input [placeholder]="'Comment *'" [value]="resourceClassComments" [textarea]="true"
(dataChanged)="handleData($event, 'comments')"></app-string-literal-input>
<app-string-literal-input
[placeholder]="'Comment *'"
[value]="resourceClassComments"
[textarea]="true"
(touched)="resourceClassCommentsTouched = $event"
(dataChanged)="handleData($event, 'comment')">
</app-string-literal-input>
<mat-hint class="string-literal-error" *ngIf="formErrors.comment">
{{ formErrors.comment }}
</mat-hint>
</div>
</div>

Expand Down