Skip to content

Commit 51e539d

Browse files
authored
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
1 parent bc210ef commit 51e539d

13 files changed

Lines changed: 209 additions & 80 deletions

File tree

‎src/app/main/action/string-literal-input/string-literal-input.component.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export class StringLiteralInputComponent implements OnInit, OnChanges {
144144

145145
const form = this.form;
146146
const control = form.get('text');
147-
this.touched.emit(control && control.dirty);
147+
this.touched.emit(control.dirty || control.touched);
148148

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

‎src/app/project/ontology/ontology-form/ontology-form.component.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { CacheService } from 'src/app/main/cache/cache.service';
1313
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
1414
import { existingNamesValidator } from 'src/app/main/directive/existing-name/existing-name.directive';
1515
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
16+
import { CustomRegex } from 'src/app/workspace/resource/values/custom-regex';
1617
import { OntologyService } from '../ontology.service';
1718

1819
export interface NewOntology {
@@ -52,9 +53,6 @@ export class OntologyFormComponent implements OnInit {
5253

5354
lastModificationDate: string;
5455

55-
// 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
56-
nameRegex = /^(?![vV]+[0-9])+^([a-zA-Z])[a-zA-Z0-9_.-]*$/;
57-
5856
// ontology name must not contain one of the following words
5957
forbiddenNames: string[] = [
6058
'knora',
@@ -183,7 +181,7 @@ export class OntologyFormComponent implements OnInit {
183181
Validators.minLength(this.nameMinLength),
184182
Validators.maxLength(this.nameMaxLength),
185183
existingNamesValidator(this.existingNames),
186-
Validators.pattern(this.nameRegex)
184+
Validators.pattern(CustomRegex.ID_NAME_REGEX)
187185
]),
188186
label: new FormControl({
189187
value: this.ontologyLabel, disabled: false

‎src/app/project/ontology/ontology.service.ts‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@angular/core';
2-
import { Cardinality } from '@dasch-swiss/dsp-js';
2+
import { Cardinality, Constants } from '@dasch-swiss/dsp-js';
33

44
/**
55
* helper methods for the ontology editor
@@ -46,6 +46,17 @@ export class OntologyService {
4646
return array[pos].toLowerCase();
4747
}
4848

49+
/**
50+
* get the name from the iri
51+
* @param iri
52+
* @returns name from iri
53+
*/
54+
getNameFromIri(iri: string): string {
55+
const array = iri.split(Constants.HashDelimiter);
56+
57+
return array[1];
58+
}
59+
4960
/**
5061
* convert cardinality values (multiple? & required?) from form to DSP-JS cardinality enum 1-n, 0-n, 1, 0-1
5162
* @param {boolean} multiple

‎src/app/project/ontology/property-form/property-form.component.html‎

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44
<form [formGroup]="propertyForm" class="form-content">
55

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

1113
<div class="center">
1214

1315
<!-- property type -->
14-
<mat-form-field *ngIf="propertyInfo.propType" class="large-field property-type">
15-
<span matPrefix class="property-type-icon">
16+
<mat-form-field *ngIf="propertyInfo.propType" class="large-field ontology-form-field">
17+
<span matPrefix class="ontology-prefix-icon">
1618
<mat-icon>{{propertyForm.controls['propType'].value.icon}}</mat-icon>&nbsp;
1719
</span>
1820
<mat-label>Property type</mat-label>
@@ -31,20 +33,40 @@
3133
</mat-select>
3234
</mat-form-field>
3335

36+
<!-- name -->
37+
<mat-form-field class="large-field ontology-form-field">
38+
<span matPrefix class="ontology-prefix-icon">
39+
<mat-icon>fingerprint</mat-icon>&nbsp;
40+
</span>
41+
<mat-label>Property name *</mat-label>
42+
<input matInput formControlName="name" >
43+
<mat-hint class="ontology-error-with-prefix" *ngIf="formErrors.name">
44+
{{formErrors.name}}
45+
</mat-hint>
46+
</mat-form-field>
47+
3448
<!-- label -->
3549
<div class="large-field string-literal-container">
36-
<app-string-literal-input [placeholder]="'Property label *'" [value]="labels" [disabled]="(resClassIri && propertyInfo.propDef)"
37-
(dataChanged)="handleData($event, 'labels')">
50+
<app-string-literal-input
51+
[placeholder]="'Property label *'"
52+
[value]="labels"
53+
[disabled]="(resClassIri && propertyInfo.propDef)"
54+
(touched)="labelsTouched = $event"
55+
(dataChanged)="handleData($event, 'label')">
3856
</app-string-literal-input>
39-
<mat-hint class="string-literal-error" *ngIf="!labels.length">
40-
Label is required
57+
<mat-hint class="string-literal-error" *ngIf="formErrors.label">
58+
{{ formErrors.label }}
4159
</mat-hint>
4260
</div>
4361

4462
<!-- comment/description -->
4563
<div class="large-field string-literal-container">
46-
<app-string-literal-input [textarea]="true" [placeholder]="'Comment'" [value]="comments" [disabled]="(resClassIri && propertyInfo.propDef)"
47-
(dataChanged)="handleData($event, 'comments')">
64+
<app-string-literal-input
65+
[placeholder]="'Comment'"
66+
[value]="comments"
67+
[disabled]="(resClassIri && propertyInfo.propDef)"
68+
[textarea]="true"
69+
(dataChanged)="handleData($event, 'comment')">
4870
</app-string-literal-input>
4971
</div>
5072

‎src/app/project/ontology/property-form/property-form.component.scss‎

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,6 @@
2626
}
2727
}
2828

29-
.property-type {
30-
.property-type-icon {
31-
width: 36px;
32-
padding: 0 8px;
33-
display: block;
34-
}
35-
36-
mat-label,
37-
mat-select,
38-
input {
39-
margin-left: 12px;
40-
}
41-
mat-select {
42-
width: calc(100% - 12px);
43-
}
44-
}
45-
4629
.cardinality {
4730

4831
.mat-slide-toggle {

‎src/app/project/ontology/property-form/property-form.component.spec.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,17 +243,17 @@ describe('PropertyFormComponent', () => {
243243

244244
});
245245

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

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

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

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

258258
});
259259

‎src/app/project/ontology/property-form/property-form.component.ts‎

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import {
2020
} from '@dasch-swiss/dsp-js';
2121
import { CacheService } from 'src/app/main/cache/cache.service';
2222
import { DspApiConnectionToken } from 'src/app/main/declarations/dsp-api-tokens';
23+
import { existingNamesValidator } from 'src/app/main/directive/existing-name/existing-name.directive';
2324
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
25+
import { CustomRegex } from 'src/app/workspace/resource/values/custom-regex';
2426
import { AutocompleteItem } from 'src/app/workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/operator';
2527
import { DefaultProperties, DefaultProperty, PropertyCategory, PropertyInfoObject } from '../default-data/default-properties';
2628
import { OntologyService } from '../ontology.service';
@@ -65,11 +67,17 @@ export class PropertyFormComponent implements OnInit {
6567
propertyForm: FormGroup;
6668

6769
formErrors = {
70+
'name': '',
6871
'label': '',
6972
'guiAttr': ''
7073
};
7174

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

103111
labels: StringLiteral[] = [];
112+
labelsTouched: boolean;
104113
comments: StringLiteral[] = [];
105114
guiAttributes: string[] = [];
106115

116+
// list of existing property names
117+
existingNames: [RegExp] = [
118+
new RegExp('anEmptyRegularExpressionWasntPossible')
119+
];
120+
107121
dspConstants = Constants;
108122

109123
constructor(
110124
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
111125
private _cache: CacheService,
112126
private _errorHandler: ErrorHandlerService,
113127
private _fb: FormBuilder,
114-
private _ontologyService: OntologyService
128+
private _os: OntologyService
115129
) { }
116130

117131
ngOnInit() {
@@ -127,6 +141,16 @@ export class PropertyFormComponent implements OnInit {
127141
// a) in case of link value:
128142
// set list of resource classes from response; needed for linkValue
129143
this.resourceClasses = response.getAllClassDefinitions();
144+
145+
// set list of all existing property names to avoid same name twice
146+
Object.entries(this.ontology.properties).forEach(
147+
([key]) => {
148+
const name = this._os.getNameFromIri(key);
149+
this.existingNames.push(
150+
new RegExp('(?:^|W)' + name.toLowerCase() + '(?:$|W)')
151+
);
152+
}
153+
);
130154
},
131155
(error: ApiResponseError) => {
132156
this._errorHandler.showMessage(error);
@@ -168,7 +192,7 @@ export class PropertyFormComponent implements OnInit {
168192

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

@@ -181,6 +205,14 @@ export class PropertyFormComponent implements OnInit {
181205
}
182206

183207
this.propertyForm = this._fb.group({
208+
'name': new FormControl({
209+
value: (this.propertyInfo.propDef ? this._os.getNameFromIri(this.propertyInfo.propDef.id) : ''),
210+
disabled: this.propertyInfo.propDef
211+
}, [
212+
Validators.required,
213+
existingNamesValidator(this.existingNames),
214+
Validators.pattern(CustomRegex.ID_NAME_REGEX)
215+
]),
184216
'propType': new FormControl({
185217
value: this.propertyInfo.propType,
186218
disabled: disablePropType || this.resClassIri
@@ -217,11 +249,9 @@ export class PropertyFormComponent implements OnInit {
217249
return;
218250
}
219251

220-
const form = this.propertyForm;
221-
222252
Object.keys(this.formErrors).map(field => {
223253
this.formErrors[field] = '';
224-
const control = form.get(field);
254+
const control = this.propertyForm.get(field);
225255
if (control && control.dirty && !control.valid) {
226256
const messages = this.validationMessages[field];
227257
Object.keys(control.errors).map(key => {
@@ -232,14 +262,20 @@ export class PropertyFormComponent implements OnInit {
232262
});
233263
}
234264

235-
handleData(data: StringLiteral[], type: string) {
265+
handleData(data: StringLiteral[], type: 'label' | 'comment') {
236266

237267
switch (type) {
238-
case 'labels':
268+
case 'label':
239269
this.labels = data;
270+
const messages = this.validationMessages[type];
271+
this.formErrors[type] = '';
272+
273+
if (this.labelsTouched && !this.labels.length) {
274+
this.formErrors[type] = messages['required'];
275+
}
240276
break;
241277

242-
case 'comments':
278+
case 'comment':
243279
this.comments = data;
244280
break;
245281
}
@@ -420,7 +456,7 @@ export class PropertyFormComponent implements OnInit {
420456
// create mode: new property incl. gui type and attribute
421457
// submit property
422458
// set resource property name / id: randomized string
423-
const uniquePropName: string = this._ontologyService.setUniqueName(this.ontology.id);
459+
// const uniquePropName: string = this._os.setUniqueName(this.ontology.id);
424460

425461
const onto = new UpdateOntology<CreateResourceProperty>();
426462

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

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

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

‎src/app/project/ontology/resource-class-form/resource-class-form.component.html‎

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,26 @@
55
<div class="resource-class-data">
66
<div class="center more-space-top">
77

8+
<!-- name -->
9+
<mat-form-field class="large-field ontology-form-field">
10+
<span matPrefix class="ontology-prefix-icon">
11+
<mat-icon>fingerprint</mat-icon>&nbsp;
12+
</span>
13+
<mat-label>Property name *</mat-label>
14+
<input matInput formControlName="name" >
15+
<mat-hint class="ontology-error-with-prefix" *ngIf="formErrors.name">
16+
{{formErrors.name}}
17+
</mat-hint>
18+
</mat-form-field>
19+
820
<!-- label -->
9-
<div class="large-field string-literal-container">
10-
<app-string-literal-input [placeholder]="'Label *'" [value]="resourceClassLabels"
21+
<div class="large-field string-literal-container more-space-top">
22+
<app-string-literal-input
23+
[placeholder]="'Label *'"
24+
[value]="resourceClassLabels"
1125
(enter)="submitData()"
12-
(dataChanged)="handleData($event, 'labels')">
26+
(touched)="resourceClassLabelsTouched = $event"
27+
(dataChanged)="handleData($event, 'label')">
1328
</app-string-literal-input>
1429
<mat-hint class="string-literal-error" *ngIf="formErrors.label">
1530
{{ formErrors.label }}
@@ -18,8 +33,16 @@
1833

1934
<!-- description -->
2035
<div class="large-field string-literal-container more-space-top">
21-
<app-string-literal-input [placeholder]="'Comment *'" [value]="resourceClassComments" [textarea]="true"
22-
(dataChanged)="handleData($event, 'comments')"></app-string-literal-input>
36+
<app-string-literal-input
37+
[placeholder]="'Comment *'"
38+
[value]="resourceClassComments"
39+
[textarea]="true"
40+
(touched)="resourceClassCommentsTouched = $event"
41+
(dataChanged)="handleData($event, 'comment')">
42+
</app-string-literal-input>
43+
<mat-hint class="string-literal-error" *ngIf="formErrors.comment">
44+
{{ formErrors.comment }}
45+
</mat-hint>
2346
</div>
2447
</div>
2548

0 commit comments

Comments
 (0)