Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(ontology): bring back the name input field (DEV-157) (#559)
* feat(ontology): bring back the name input field (DEV-157)

* refactor(ontology): clean up prop form

* fix(string-literal): correct string literal touched handler

* style(ontology): fix field error in case of string literal

* refactor(ontology): clean up template

* test(ontology): fix tests
  • Loading branch information
kilchenmann committed Oct 21, 2021
1 parent bc210ef commit 51e539d
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 80 deletions.
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

0 comments on commit 51e539d

Please sign in to comment.