From d529bf263387e2ded750b90663ecd06521fdf514 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr>
Date: Wed, 28 Jul 2021 16:57:13 +0200
Subject: [PATCH] Refactoring search criterion form

---
 .../search-type/between-date.component.html   |  46 +++----
 .../search-type/between-date.component.ts     |  60 ++++------
 .../search-type/between.component.html        |  66 +++++-----
 .../criteria/search-type/between.component.ts |  68 ++++-------
 .../search-type/checkbox.component.html       |  44 +++----
 ....component.css => checkbox.component.scss} |   0
 .../search-type/checkbox.component.ts         |  87 ++++++--------
 .../search-type/datalist.component.html       |  74 ++++++------
 .../search-type/datalist.component.ts         |  63 ++++------
 .../criteria/search-type/date.component.html  |  64 +++++-----
 .../criteria/search-type/date.component.ts    |  64 ++++------
 .../search-type/datetime.component.html       |  93 +++++++-------
 .../search-type/datetime.component.ts         | 113 ++++++------------
 .../criteria/search-type/field.component.html |  68 ++++++-----
 .../criteria/search-type/field.component.ts   |  61 ++++------
 .../criteria/search-type/json.component.html  |  10 +-
 .../criteria/search-type/json.component.ts    |  65 ++++------
 .../criteria/search-type/list.component.html  |  40 ++++---
 .../criteria/search-type/list.component.ts    |  48 ++++----
 ....component.css => operator.component.scss} |   0
 .../search-type/operator.component.ts         |   2 +-
 .../criteria/search-type/radio.component.html |  38 +++---
 .../criteria/search-type/radio.component.ts   |  61 ++++------
 .../select-multiple.component.html            |  44 +++----
 .../search-type/select-multiple.component.ts  |  66 ++++------
 .../search-type/select.component.html         |  62 +++++-----
 .../criteria/search-type/select.component.ts  |  62 ++++------
 .../criteria/search-type/time.component.html  |  69 +++++------
 .../criteria/search-type/time.component.ts    |  72 ++++-------
 .../cone-search/cone-search.component.ts      |   2 +-
 client/src/assets/app.config.json             |   2 +-
 client/src/styles.scss                        |  11 +-
 32 files changed, 691 insertions(+), 934 deletions(-)
 rename client/src/app/instance/search/components/criteria/search-type/{checkbox.component.css => checkbox.component.scss} (100%)
 rename client/src/app/instance/search/components/criteria/search-type/{operator.component.css => operator.component.scss} (100%)

diff --git a/client/src/app/instance/search/components/criteria/search-type/between-date.component.html b/client/src/app/instance/search/components/criteria/search-type/between-date.component.html
index 62923f54..25d35d5b 100644
--- a/client/src/app/instance/search/components/criteria/search-type/between-date.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/between-date.component.html
@@ -1,25 +1,27 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col px-sm-3">
-                <input type="text" 
-                    placeholder="Pick a date range..." 
-                    class="form-control" 
-                    [formControl]="field"
-                    [bsValue]="field.value" 
-                    [bsConfig]="{ rangeInputFormat: 'YYYY-MM-DD' }" 
-                    autocomplete="off"
-                    bsDaterangepicker>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col px-sm-3">
+                    <input type="text" 
+                        placeholder="Pick a date range..." 
+                        class="form-control" 
+                        formControlName="dateRange"
+                        [bsValue]="form.controls.dateRange.value" 
+                        [bsConfig]="{ rangeInputFormat: 'YYYY-MM-DD', isAnimated: true }" 
+                        autocomplete="off"
+                        bsDaterangepicker>
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end mb-sm-1 pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end mb-sm-1 pb-3">
-        <button class="btn btn-outline-success" *ngIf="!field.disabled" [hidden]="!field.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="field.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
diff --git a/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts b/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts
index 6f4389c8..9e0ab4d1 100644
--- a/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, BetweenCriterion } from 'src/app/instance/store/models';
 
@@ -25,19 +25,26 @@ export class BetweenDateComponent {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Output() addCriterion: EventEmitter<BetweenCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    field = new FormControl('');
+    public form = new FormGroup({
+        dateRange: new FormControl('', [Validators.required])
+    });
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const c = changes.criterion.currentValue as BetweenCriterion;
+            this.form.controls.dateRange.setValue([new Date(c.min), new Date(c.max)]);
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
 
     /**
      * Emits event to add criterion to the criteria list.
@@ -45,37 +52,12 @@ export class BetweenDateComponent {
      * @fires EventEmitter<BetweenCriterion>
      */
     emitAdd(): void {
-        const dateMin = this.getDateString(this.field.value[0]);
-        const dateMax = this.getDateString(this.field.value[1]);
+        const dateMin = this.getDateString(this.form.controls.dateRange.value[0]);
+        const dateMax = this.getDateString(this.form.controls.dateRange.value[1]);
         const fd: BetweenCriterion = {id: this.id, type: 'between', min: dateMin, max: dateMax};
         this.addCriterion.emit(fd);
     }
 
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.field.reset();
-            this.field.enable();
-        } else {
-            const c = criterion as BetweenCriterion;
-            this.field.setValue([new Date(c.min), new Date(c.max)]);
-            this.field.disable();
-        }
-    }
-
     /**
      * Stringifies the given date.
      *
diff --git a/client/src/app/instance/search/components/criteria/search-type/between.component.html b/client/src/app/instance/search/components/criteria/search-type/between.component.html
index 9d499eb3..d2bb5153 100644
--- a/client/src/app/instance/search/components/criteria/search-type/between.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/between.component.html
@@ -1,40 +1,32 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-3 col-lg-auto pr-sm-1 mb-1 mb-lg-0">
-                <div class="readonly">min</div>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1 mb-1 mb-sm-0">
-                <input 
-                type="text" 
-                class="form-control" 
-                [placeholder]="getPlaceholderMin()" 
-                [formControl]="fieldMin"
-                autocomplete="off" />
-            </div>
-            <div class="w-100 d-block d-lg-none"></div>
-            <div class="col col-sm-3 col-lg-auto pr-sm-1 mb-1">
-                <div class="readonly">max</div>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1">
-                <input 
-                type="text" 
-                class="form-control" 
-                [placeholder]="getPlaceholderMax()" 
-                [formControl]="fieldMax"
-                autocomplete="off" />
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-3 col-lg-auto pr-sm-1 mb-1 mb-lg-0">
+                    <div class="readonly">min</div>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1 mb-1 mb-sm-0">
+                    <input type="text" class="form-control" [placeholder]="getPlaceholderMin()" formControlName="min" autocomplete="off" />
+                </div>
+                <div class="w-100 d-block d-lg-none"></div>
+                <div class="col col-sm-3 col-lg-auto pr-sm-1 mb-1">
+                    <div class="readonly">max</div>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1">
+                    <input type="text"  class="form-control" [placeholder]="getPlaceholderMax()" formControlName="max" autocomplete="off" />
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.controls.min.value && !form.controls.max.value" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!fieldMin.disabled" [hidden]="!fieldMin.value && !fieldMax.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="fieldMin.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
diff --git a/client/src/app/instance/search/components/criteria/search-type/between.component.ts b/client/src/app/instance/search/components/criteria/search-type/between.component.ts
index d9c5d872..bc1d7b62 100644
--- a/client/src/app/instance/search/components/criteria/search-type/between.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/between.component.ts
@@ -7,78 +7,54 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, BetweenCriterion } from 'src/app/instance/store/models';
 
 @Component({
     selector: 'app-between',
     templateUrl: 'between.component.html',
-    styleUrls: ['operator.component.css'],
+    styleUrls: ['operator.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 /**
  * @class
  * @classdesc Between search type component.
  */
-export class BetweenComponent {
+export class BetweenComponent implements OnChanges {
     @Input() id: number;
     @Input() label: string;
     @Input() placeholderMin: string;
     @Input() placeholderMax: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Output() addCriterion: EventEmitter<BetweenCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    fieldMin = new FormControl('');
-    fieldMax = new FormControl('');
+    public form = new FormGroup({
+        min: new FormControl('', [Validators.required]),
+        max: new FormControl('', [Validators.required])
+    });
 
-    /**
-     * Emits event to add criterion to the criteria list.
-     *
-     * @fires EventEmitter<BetweenCriterion>
-     */
-    emitAdd(): void {
-        const fd = {id: this.id, type: 'between', min: this.fieldMin.value, max: this.fieldMax.value};
-        this.addCriterion.emit(fd);
-    }
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            this.form.patchValue(this.criterion);
+            this.form.disable();
+        }
 
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
     }
 
     /**
-     * Fills form with the given criterion.
+     * Emits event to add criterion to the criteria list.
      *
-     * @param  {Criterion} criterion - The criterion.
+     * @fires EventEmitter<BetweenCriterion>
      */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.fieldMin.reset();
-            this.fieldMax.reset();
-            this.fieldMin.enable();
-            this.fieldMax.enable();
-        } else {
-            const c = criterion as BetweenCriterion;
-            this.fieldMin.setValue(c.min);
-            this.fieldMax.setValue(c.max);
-            this.fieldMin.disable();
-            this.fieldMax.disable();
-        }
+    emitAdd(): void {
+        this.addCriterion.emit({id: this.id, type: 'between', ...this.form.value});
     }
 
     /**
diff --git a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.html b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.html
index cc269e03..82faebeb 100644
--- a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.html
@@ -1,25 +1,27 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
-                <div class="readonly">in</div>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1" formArrayName="checkboxes">
-                <div *ngFor="let _ of checkboxes.controls; index as i" class="custom-control custom-checkbox form-check form-check-inline form-control-lg">
-                    <input class="custom-control-input" type="checkbox" id="cb_{{options[i].value}}" [formControlName]="i">
-                    <label class="custom-control-label" for="cb_{{options[i].value}}">{{ options[i].label }}</label>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
+                    <div class="readonly">in</div>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1" formArrayName="checkboxes">
+                    <div *ngFor="let _ of getCheckboxes().controls; index as i" class="custom-control custom-checkbox form-check form-check-inline form-control-lg">
+                        <input class="custom-control-input" type="checkbox" id="cb_{{options[i].value}}" [formControlName]="i">
+                        <label class="custom-control-label" for="cb_{{options[i].value}}">{{ options[i].label }}</label>
+                    </div>
                 </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button *ngIf="!form.disabled" class="btn btn-outline-success" [hidden]="!isChecked()" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button *ngIf="form.disabled" class="btn btn-outline-danger" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button *ngIf="!checkboxes.disabled" class="btn btn-outline-success" [hidden]="!isChecked()" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button *ngIf="checkboxes.disabled" class="btn btn-outline-danger" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
diff --git a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.css b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.scss
similarity index 100%
rename from client/src/app/instance/search/components/criteria/search-type/checkbox.component.css
rename to client/src/app/instance/search/components/criteria/search-type/checkbox.component.scss
diff --git a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts
index 3e76f548..67d147fa 100644
--- a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl, FormArray } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormArray, FormControl } from '@angular/forms';
 
 import { Criterion, SelectMultipleCriterion } from 'src/app/instance/store/models';
 import { Option } from 'src/app/metamodel/models';
@@ -16,80 +16,63 @@ import { Option } from 'src/app/metamodel/models';
 @Component({
     selector: 'app-checkbox',
     templateUrl: 'checkbox.component.html',
-    styleUrls: ['checkbox.component.css', 'operator.component.css'],
+    styleUrls: ['checkbox.component.scss', 'operator.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 /**
  * @class
  * @classdesc Checkbox search type component.
  */
-export class CheckboxComponent {
+export class CheckboxComponent implements OnInit, OnChanges {
     @Input() id: number;
     @Input() label: string;
     @Input() options: Option[];
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Output() addCriterion: EventEmitter<SelectMultipleCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    checkboxes: FormArray;
-
-    /**
-     * Emits event to add criterion to the criteria list.
-     *
-     * @fires EventEmitter<SelectMultipleCriterion>
-     */
-    emitAdd(): void {
-        // Filter options to keep only the checked checkboxes and emit the new criterion
-        const selected = this.checkboxes.value;
-        const values = [...this.options.filter((option, index) => selected[index])];
-        this.addCriterion.emit({id: this.id, type: 'multiple', options: values});
-    }
-
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        // Delete the criterion into the state to reactivate the checkboxes
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        const multipleCriterion = criterion as SelectMultipleCriterion;
+    public form = new FormGroup({ });
 
+    ngOnInit() {
         // Initialization of checkboxes (1 per option)
         const formControls: FormControl[] = [];
         for (let i = 0; i < this.options.length; i++) {
             formControls.push(new FormControl());
         }
+        this.form.addControl('checkboxes', new FormArray(formControls));
+    }
 
-        // Store checkboxes in an array
-        this.checkboxes = new FormArray(formControls);
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const multipleCriterion = this.criterion as SelectMultipleCriterion;
 
-        // If the criterion is already used
-        // Set to true only the checkboxes present in the criterion
-        if (multipleCriterion) {
             for (let i = 0; i < this.options.length; i++) {
                 if (multipleCriterion.options.find(o => o.label === this.options[i].label)) {
-                    this.checkboxes.controls[i].setValue(true);
+                    this.getCheckboxes().controls[i].setValue(true);
                 }
             }
-            this.checkboxes.disable();
+            this.form.disable();
         }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
+    getCheckboxes() {
+        return this.form.controls.checkboxes as FormArray;
+    }
+
+    /**
+     * Emits event to add criterion to the criteria list.
+     *
+     * @fires EventEmitter<SelectMultipleCriterion>
+     */
+    emitAdd(): void {
+        const selected = this.getCheckboxes().value;
+        const values = [...this.options.filter((option, index) => selected[index])];
+        this.addCriterion.emit({id: this.id, type: 'multiple', options: values});
     }
 
     /**
@@ -99,6 +82,6 @@ export class CheckboxComponent {
      */
     isChecked(): boolean {
         // If one of the checkboxes is checked returns true else returns false
-        return this.checkboxes.controls.filter(formControl => formControl.value).length > 0;
+        return this.getCheckboxes().controls.filter(formControl => formControl.value).length > 0;
     }
 }
diff --git a/client/src/app/instance/search/components/criteria/search-type/datalist.component.html b/client/src/app/instance/search/components/criteria/search-type/datalist.component.html
index 34b72be2..d7a05139 100644
--- a/client/src/app/instance/search/components/criteria/search-type/datalist.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/datalist.component.html
@@ -1,42 +1,44 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <span *ngIf="operator === 'lk'" class="pl-1" [tooltip]="helpLike" placement="right" containerClass="custom-tooltip right-tooltip">
-            <span class="far fa-question-circle fa-sm"></span>
-        </span>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
-                <app-operator
-                    [operator]="operator"
-                    [searchType]="'field'"
-                    [advancedForm]="advancedForm"
-                    [disabled]="disabledOperator"
-                    (changeOperator)="changeOperator($event)">
-                </app-operator>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1">
-                <input [attr.list]="getDatalistId()" 
-                    type="text" 
-                    class="form-control" 
-                    [placeholder]="getPlaceholder()"
-                    [formControl]="field" 
-                    autocomplete="off" />
-                <datalist [id]="getDatalistId()">
-                    <option *ngFor="let option of options" [value]="option.value">
-                </datalist>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <span *ngIf="operator === 'lk'" class="pl-1" [tooltip]="helpLike" placement="right" containerClass="custom-tooltip right-tooltip">
+                <span class="far fa-question-circle fa-sm"></span>
+            </span>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
+                    <app-operator
+                        [operator]="operator"
+                        [searchType]="'field'"
+                        [advancedForm]="advancedForm"
+                        [disabled]="disabledOperator"
+                        (changeOperator)="changeOperator($event)">
+                    </app-operator>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1">
+                    <input [attr.list]="getDatalistId()" 
+                        type="text" 
+                        class="form-control" 
+                        [placeholder]="getPlaceholder()"
+                        formControlName="value"
+                        autocomplete="off" />
+                    <datalist [id]="getDatalistId()">
+                        <option *ngFor="let option of options" [value]="option.value">
+                    </datalist>
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!field.disabled" [hidden]="!field.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="field.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
+</form>
 
 <ng-template #helpLike>
     <app-help-like></app-help-like>
diff --git a/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts b/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts
index 5ef22969..cb31dc15 100644
--- a/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, SimpleChanges, OnChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 import { Option } from 'src/app/metamodel/models';
@@ -22,28 +22,35 @@ import { Option } from 'src/app/metamodel/models';
  * @class
  * @classdesc Datalist search type component.
  */
-export class DatalistComponent {
+export class DatalistComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
     @Input() placeholder: string;
     @Input() options: Option[];
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Input() advancedForm: boolean;
     @Output() addCriterion: EventEmitter<FieldCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    field = new FormControl('');
+    public form = new FormGroup({
+        value: new FormControl('', [Validators.required])
+    });
+
     disabledOperator: boolean;
 
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            this.form.patchValue(this.criterion);
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
     /**
      * Modifies operator with the given one.
      *
@@ -59,35 +66,7 @@ export class DatalistComponent {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const fd = {id: this.id, type: 'field', operator: this.operator, value: this.field.value};
-        this.addCriterion.emit(fd);
-    }
-
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.field.reset();
-            this.field.enable();
-            this.disabledOperator = false;
-        } else {
-            const c = criterion as FieldCriterion;
-            this.field.setValue(c.value);
-            this.field.disable();
-            this.disabledOperator = true;
-        }
+        this.addCriterion.emit({ id: this.id, type: 'field', operator: this.operator, ...this.form.value });
     }
 
     /**
diff --git a/client/src/app/instance/search/components/criteria/search-type/date.component.html b/client/src/app/instance/search/components/criteria/search-type/date.component.html
index 674e2f1e..73c45113 100644
--- a/client/src/app/instance/search/components/criteria/search-type/date.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/date.component.html
@@ -1,34 +1,36 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
-                <app-operator
-                    [operator]="operator"
-                    [searchType]="'field'"
-                    [advancedForm]="advancedForm"
-                    [disabled]="disabledOperator"
-                    (changeOperator)="changeOperator($event)">
-                </app-operator>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1">
-                <input type="text" 
-                    placeholder="Pick a date..." 
-                    class="form-control" 
-                    [formControl]="field"
-                    [bsValue]="field.value" 
-                    [bsConfig]="{ dateInputFormat: 'YYYY-MM-DD' }" 
-                    bsDatepicker>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
+                    <app-operator
+                        [operator]="operator"
+                        [searchType]="'field'"
+                        [advancedForm]="advancedForm"
+                        [disabled]="disabledOperator"
+                        (changeOperator)="changeOperator($event)">
+                    </app-operator>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1">
+                    <input type="text" 
+                        placeholder="Pick a date..." 
+                        class="form-control" 
+                        formControlName="date"
+                        [bsValue]="form.controls.date.value" 
+                        [bsConfig]="{ dateInputFormat: 'YYYY-MM-DD', isAnimated: true }" 
+                        bsDatepicker>
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!field.disabled" [hidden]="!field.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="field.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/date.component.ts b/client/src/app/instance/search/components/criteria/search-type/date.component.ts
index 3b86c996..d1f4f57c 100644
--- a/client/src/app/instance/search/components/criteria/search-type/date.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/date.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 
@@ -21,27 +21,36 @@ import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
  * @class
  * @classdesc Date search type component.
  */
-export class DateComponent {
+export class DateComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
     @Input() placeholder: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Input() advancedForm: boolean;
     @Output() addCriterion: EventEmitter<FieldCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    field = new FormControl('');
+    public form = new FormGroup({
+        date: new FormControl('', [Validators.required])
+    });
+
     disabledOperator: boolean;
 
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const criterion = changes.criterion.currentValue as FieldCriterion;
+
+            this.form.controls.date.setValue(new Date(criterion.value));
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
     /**
      * Modifies operator with the given one.
      *
@@ -57,37 +66,10 @@ export class DateComponent {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const fd = {id: this.id, type: 'field', operator: this.operator, value: this.getDateString(this.field.value)};
+        const fd = {id: this.id, type: 'field', operator: this.operator, value: this.getDateString(this.form.value.date)};
         this.addCriterion.emit(fd);
     }
 
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete():void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.field.reset();
-            this.field.enable();
-            this.disabledOperator = false;
-        } else {
-            const c = criterion as FieldCriterion;
-            this.field.setValue(new Date(c.value));
-            this.field.disable();
-            this.disabledOperator = true;
-        }
-    }
-
     /**
      * Returns placeholder.
      *
diff --git a/client/src/app/instance/search/components/criteria/search-type/datetime.component.html b/client/src/app/instance/search/components/criteria/search-type/datetime.component.html
index ae4a3d96..1e7edf0f 100644
--- a/client/src/app/instance/search/components/criteria/search-type/datetime.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/datetime.component.html
@@ -1,51 +1,52 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
-                <app-operator
-                    [operator]="operator"
-                    [searchType]="'field'"
-                    [advancedForm]="advancedForm"
-                    [disabled]="disabledOperator"
-                    (changeOperator)="changeOperator($event)">
-                </app-operator>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1 mb-1 mb-lg-0 pr-lg-1">
-                <input type="text" 
-                    placeholder="Pick a date..." 
-                    class="form-control" 
-                    [formControl]="date" 
-                    [bsValue]="date.value"
-                    [bsConfig]="{ dateInputFormat: 'YYYY-MM-DD' }" 
-                    (change)="change()" 
-                    bsDatepicker>
-            </div>
-            <div class="w-100 d-block d-lg-none"></div>
-            <div class="col col-lg-auto">
-                <div class="row">
-                    <div class="col-auto pl-lg-1 pr-1">
-                        <ng-select (change)="change()" [formControl]="hh" [multiple]="false" placeholder="HH..." class="ng-select-custom ng-select-time">
-                            <ng-option *ngFor="let hour of hours" [value]="hour">{{ hour }}</ng-option>
-                        </ng-select>
-                    </div>
-                    <div class="col col-lg-auto p-0 text-center">:</div>
-                    <div class="col-auto pl-1">
-                        <ng-select (change)="change()" [formControl]="mm" [multiple]="false" placeholder="MM..." class="ng-select-custom ng-select-time">
-                            <ng-option *ngFor="let min of minutes" [value]="min">{{ min }}</ng-option>
-                        </ng-select>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
+                    <app-operator
+                        [operator]="operator"
+                        [searchType]="'field'"
+                        [advancedForm]="advancedForm"
+                        [disabled]="disabledOperator"
+                        (changeOperator)="changeOperator($event)">
+                    </app-operator>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1 mb-1 mb-lg-0 pr-lg-1">
+                    <input type="text" 
+                        placeholder="Pick a date..." 
+                        class="form-control" 
+                        formControlName="date" 
+                        [bsValue]="form.controls.date.value"
+                        [bsConfig]="{ dateInputFormat: 'YYYY-MM-DD', isAnimated: true }"
+                        bsDatepicker>
+                </div>
+                <div class="w-100 d-block d-lg-none"></div>
+                <div class="col col-lg-auto">
+                    <div class="row">
+                        <div class="col-auto pl-lg-1 pr-1">
+                            <ng-select formControlName="hh" [multiple]="false" placeholder="HH..." class="ng-select-custom ng-select-time">
+                                <ng-option *ngFor="let hour of hours" [value]="hour">{{ hour }}</ng-option>
+                            </ng-select>
+                        </div>
+                        <div class="col col-lg-auto p-0 text-center">:</div>
+                        <div class="col-auto pl-1">
+                            <ng-select formControlName="mm" [multiple]="false" placeholder="MM..." class="ng-select-custom ng-select-time">
+                                <ng-option *ngFor="let min of minutes" [value]="min">{{ min }}</ng-option>
+                            </ng-select>
+                        </div>
                     </div>
                 </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!date.disabled || !hh.disabled || !mm.disabled" [hidden]="!isValidFields" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="date.disabled && hh.disabled && mm.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
diff --git a/client/src/app/instance/search/components/criteria/search-type/datetime.component.ts b/client/src/app/instance/search/components/criteria/search-type/datetime.component.ts
index a4d81d92..0ad31ef7 100644
--- a/client/src/app/instance/search/components/criteria/search-type/datetime.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/datetime.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 
@@ -21,33 +21,43 @@ import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
  * @class
  * @classdesc Datetime search type component.
  */
-export class DatetimeComponent {
+export class DatetimeComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Input() advancedForm: boolean;
     @Output() addCriterion: EventEmitter<FieldCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
     hours: string[] = this.initTime(24);
     minutes: string[] = this.initTime(60);
-    date = new FormControl('');
-    hh = new FormControl();
-    mm = new FormControl();
-    isValidFields = false;
-    datetime: Date = new Date();
+
+    public form = new FormGroup({
+        date: new FormControl('', [Validators.required]),
+        hh: new FormControl('', [Validators.required]),
+        mm: new FormControl('', [Validators.required]) 
+    });
 
     disabledOperator: boolean;
 
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const criterion = changes.criterion.currentValue as FieldCriterion;
+            const [date, time] = criterion.value.split(' ');
+
+            this.form.controls.date.setValue(new Date(date));
+            this.form.controls.hh.setValue(time.slice(0, 2));
+            this.form.controls.mm.setValue(time.slice(3, 5));
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
     /**
      * Modifies operator with the given one.
      *
@@ -63,53 +73,12 @@ export class DatetimeComponent {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const month = ('0' + (this.datetime.getMonth() + 1)).slice(-2);
-        const day = ('0' + (this.datetime.getDate())).slice(-2);
-        const date = this.datetime.getFullYear() + '-' + month + '-' + day;
-        const time = this.hh.value + ':' + this.mm.value;
+        const date = this.getDateString(this.form.value.date);
+        const time = this.form.value.hh + ':' + this.form.value.mm;
         const fd = {id: this.id, type: 'field', operator: this.operator, value: date + ' ' + time};
         this.addCriterion.emit(fd);
     }
 
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.date.reset();
-            this.date.enable();
-            this.hh.reset();
-            this.hh.enable();
-            this.mm.reset();
-            this.mm.enable();
-            this.isValidFields = false;
-            this.disabledOperator = false;
-        } else {
-            const c = criterion as FieldCriterion;
-            const [d, t] = c.value.split(' ');
-            const [h, m] = t.split(':');
-            this.date.setValue(new Date(d));
-            this.date.disable();
-            this.hh.setValue(h);
-            this.hh.disable();
-            this.mm.setValue(m);
-            this.mm.disable();
-            this.isValidFields = true;
-            this.disabledOperator = true;
-        }
-    }
-
     /**
      * Returns string array to represent the given time.
      *
@@ -127,21 +96,15 @@ export class DatetimeComponent {
     }
 
     /**
-     * Reconstructs datetime from form inputs.
+     * Stringifies the given date.
+     *
+     * @param  {Date} date - The date.
+     *
+     * @return string
      */
-    change(): void {
-        if (!this.date.value || !this.hh.value || !this.mm.value) {
-            this.isValidFields = false;
-        } else {
-            this.isValidFields = true;
-            this.datetime.setFullYear(
-                this.date.value.getFullYear(),
-                this.date.value.getMonth(),
-                this.date.value.getDate());
-            this.datetime.setHours(
-                this.hh.value,
-                this.mm.value);
-            this.datetime.setSeconds(0, 0);
-        }
+    getDateString(date: Date): string {
+        const month = ('0' + (date.getMonth() + 1)).slice(-2);
+        const day = ('0' + (date.getDate())).slice(-2);
+        return date.getFullYear() + '-' + month + '-' + day;
     }
 }
diff --git a/client/src/app/instance/search/components/criteria/search-type/field.component.html b/client/src/app/instance/search/components/criteria/search-type/field.component.html
index cbfcb3b2..3e25d2cb 100644
--- a/client/src/app/instance/search/components/criteria/search-type/field.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/field.component.html
@@ -1,39 +1,41 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <span *ngIf="operator === 'lk'" class="pl-1" [tooltip]="helpLike" placement="right" containerClass="custom-tooltip right-tooltip">
-            <span class="far fa-question-circle fa-sm"></span>
-        </span>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
-                <app-operator
-                    [operator]="operator"
-                    [searchType]="'field'"
-                    [advancedForm]="advancedForm"
-                    [disabled]="disabledOperator"
-                    (changeOperator)="changeOperator($event)">
-                </app-operator>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1">
-                <input
-                    [type]="getType()" 
-                    class="form-control" 
-                    [placeholder]="getPlaceholder()" 
-                    [formControl]="field"
-                    autocomplete="off">
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <span *ngIf="operator === 'lk'" class="pl-1" [tooltip]="helpLike" placement="right" containerClass="custom-tooltip right-tooltip">
+                <span class="far fa-question-circle fa-sm"></span>
+            </span>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
+                    <app-operator
+                        [operator]="operator"
+                        [searchType]="'field'"
+                        [advancedForm]="advancedForm"
+                        [disabled]="disabledOperator"
+                        (changeOperator)="changeOperator($event)">
+                    </app-operator>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1">
+                    <input
+                        [type]="getType()" 
+                        class="form-control" 
+                        [placeholder]="getPlaceholder()" 
+                        formControlName="value"
+                        autocomplete="off">
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!field.disabled" [hidden]="!field.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="field.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
+</form>
 
 <ng-template #helpLike>
     <app-help-like></app-help-like>
diff --git a/client/src/app/instance/search/components/criteria/search-type/field.component.ts b/client/src/app/instance/search/components/criteria/search-type/field.component.ts
index a82dd2af..fb942cd2 100644
--- a/client/src/app/instance/search/components/criteria/search-type/field.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/field.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { FieldCriterion, Criterion } from 'src/app/instance/store/models';
 
@@ -27,22 +27,29 @@ export class FieldComponent {
     @Input() label: string;
     @Input() placeholder: string;
     @Input() attributeType: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Input() advancedForm: boolean;
     @Output() addCriterion: EventEmitter<FieldCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    field = new FormControl('');
+    public form = new FormGroup({
+        value: new FormControl('', [Validators.required])
+    });
+    
     disabledOperator: boolean;
 
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            this.form.patchValue(this.criterion);
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
     /**
      * Modifies operator with the given one.
      *
@@ -58,35 +65,7 @@ export class FieldComponent {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const fd = {id: this.id, type: 'field', operator: this.operator, value: this.field.value};
-        this.addCriterion.emit(fd);
-    }
-
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.field.reset();
-            this.field.enable();
-            this.disabledOperator = false;
-        } else {
-            const c = criterion as FieldCriterion;
-            this.field.setValue(c.value);
-            this.field.disable();
-            this.disabledOperator = true;
-        }
+        this.addCriterion.emit({ id: this.id, type: 'field', operator: this.operator, ...this.form.value });
     }
 
     /**
diff --git a/client/src/app/instance/search/components/criteria/search-type/json.component.html b/client/src/app/instance/search/components/criteria/search-type/json.component.html
index e2609133..8d69dac7 100644
--- a/client/src/app/instance/search/components/criteria/search-type/json.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/json.component.html
@@ -1,4 +1,4 @@
-<form [formGroup]="jsonForm">
+<form [formGroup]="form" novalidate>
     <div class="row">
         <div class="col form-group">
             <label>{{ label }}</label>
@@ -19,17 +19,17 @@
                 </div>
                 <div class="w-100 d-block d-sm-none"></div>
                 <div class="col">
-                    <input class="form-control" id="value" name="value" placeholder="Value" autocomplete="off" formControlName="operator">
+                    <input class="form-control" id="value" name="value" placeholder="Value" autocomplete="off" formControlName="value">
                 </div>
             </div>
         </div>
         <div class="col-2 text-center align-self-end pb-3">
-            <button class="btn btn-outline-success" *ngIf="!jsonForm.disabled" [hidden]="!jsonForm.value.path || !jsonForm.value.operator || !jsonForm.value.value" (click)="emitAdd()">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
                 <span class="fas fa-plus fa-fw"></span>
             </button>
-            <button class="btn btn-outline-danger" *ngIf="jsonForm.disabled" (click)="emitDelete()">
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
                 <span class="fa fa-times fa-fw"></span>
             </button>
         </div>
     </div>
-</form>
\ No newline at end of file
+</form>
diff --git a/client/src/app/instance/search/components/criteria/search-type/json.component.ts b/client/src/app/instance/search/components/criteria/search-type/json.component.ts
index 0c33cb0b..32ddefd7 100644
--- a/client/src/app/instance/search/components/criteria/search-type/json.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/json.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl, FormGroup } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { JsonCriterion, Criterion } from 'src/app/instance/store/models';
 
@@ -21,61 +21,38 @@ import { JsonCriterion, Criterion } from 'src/app/instance/store/models';
  * @class
  * @classdesc JSON search type component.
  */
-export class JsonComponent {
+export class JsonComponent implements OnChanges {
     @Input() id: number;
     @Input() label: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Output() addCriterion: EventEmitter<JsonCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    jsonForm = new FormGroup({
-        path: new FormControl(),
-        operator: new FormControl(),
-        value: new FormControl()
+    public form = new FormGroup({
+        path: new FormControl('', [Validators.required]),
+        operator: new FormControl('', [Validators.required]),
+        value: new FormControl('', [Validators.required])
     });
 
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            this.form.patchValue(changes.criterion.currentValue);
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
     /**
      * Emits event to add criterion to the criteria list.
      *
      * @fires EventEmitter<JsonCriterion>
      */
     emitAdd(): void {
-        const js = {id: this.id, type: 'json', path: this.jsonForm.value.path, operator: this.jsonForm.value.operator, value: this.jsonForm.value.value};
+        const js = {id: this.id, type: 'json', ...this.form.value};
         this.addCriterion.emit(js);
     }
-
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.jsonForm.reset();
-            this.jsonForm.enable();
-        } else {
-            const c = criterion as JsonCriterion;
-            this.jsonForm.controls.path.setValue(c.path);
-            this.jsonForm.controls.operator.setValue(c.operator);
-            this.jsonForm.controls.value.setValue(c.value);
-            this.jsonForm.disable();
-        }
-    }
 }
diff --git a/client/src/app/instance/search/components/criteria/search-type/list.component.html b/client/src/app/instance/search/components/criteria/search-type/list.component.html
index 1910184b..f2e467a5 100644
--- a/client/src/app/instance/search/components/criteria/search-type/list.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/list.component.html
@@ -1,22 +1,24 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
-                <div class="readonly">=</div>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1">
-                <textarea class="form-control" rows="3" [placeholder]="placeholder" [formControl]="list" autocomplete="off"></textarea>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
+                    <div class="readonly">=</div>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1">
+                    <textarea class="form-control" rows="3" [placeholder]="getPlaceholder()" formControlName="list" autocomplete="off"></textarea>
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!list.disabled" [hidden]="!list.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="list.disabled" (click)="deleteCriterion.emit(id)">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/list.component.ts b/client/src/app/instance/search/components/criteria/search-type/list.component.ts
index fcbfdb42..9887535b 100644
--- a/client/src/app/instance/search/components/criteria/search-type/list.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/list.component.ts
@@ -7,52 +7,44 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, SimpleChanges, OnChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { ListCriterion, Criterion } from 'src/app/instance/store/models';
 
 @Component({
     selector: 'app-list',
     templateUrl: 'list.component.html',
-    styleUrls: ['operator.component.css'],
+    styleUrls: ['operator.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
 /**
  * @class
  * @classdesc List search type component.
  */
-export class ListComponent {
+export class ListComponent implements OnChanges {
     @Input() id: number;
     @Input() label: string;
     @Input() placeholder: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Output() addCriterion: EventEmitter<ListCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    list = new FormControl('');
+    public form = new FormGroup({
+        list: new FormControl('', [Validators.required])
+    });
 
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.list.reset();
-            this.list.enable();
-        } else {
-            const c = criterion as ListCriterion;
-            this.list.setValue(c.values.join('\n'));
-            this.list.disable();
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const criterion = changes.criterion.currentValue as ListCriterion;
+
+            this.form.controls.list.setValue(criterion.values.join('\n'));
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
         }
     }
 
@@ -75,7 +67,7 @@ export class ListComponent {
      * @fires EventEmitter<ListCriterion>
      */
     emitAdd(): void {
-        const ls = { id: this.id, type: 'list', values: this.list.value.split('\n') };
+        const ls = { id: this.id, type: 'list', values: this.form.value.list.split('\n') };
         this.addCriterion.emit(ls);
     }
 }
diff --git a/client/src/app/instance/search/components/criteria/search-type/operator.component.css b/client/src/app/instance/search/components/criteria/search-type/operator.component.scss
similarity index 100%
rename from client/src/app/instance/search/components/criteria/search-type/operator.component.css
rename to client/src/app/instance/search/components/criteria/search-type/operator.component.scss
diff --git a/client/src/app/instance/search/components/criteria/search-type/operator.component.ts b/client/src/app/instance/search/components/criteria/search-type/operator.component.ts
index b45fb8e3..32b44df3 100644
--- a/client/src/app/instance/search/components/criteria/search-type/operator.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/operator.component.ts
@@ -12,7 +12,7 @@ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from
 @Component({
     selector: 'app-operator',
     templateUrl: 'operator.component.html',
-    styleUrls: ['operator.component.css'],
+    styleUrls: ['operator.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 /**
diff --git a/client/src/app/instance/search/components/criteria/search-type/radio.component.html b/client/src/app/instance/search/components/criteria/search-type/radio.component.html
index 2e0dcb37..3be08e8e 100644
--- a/client/src/app/instance/search/components/criteria/search-type/radio.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/radio.component.html
@@ -1,23 +1,23 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1">
-                <div *ngFor="let option of options" class="form-check form-check-inline">
-                    <input class="form-check-input" type="radio" id="cb_{{option.value}}" [formControl]="radio"
-                        [value]="option.value">
-                    <label class="form-check-label" for="cb_{{option.value}}">{{ option.label }}</label>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1">
+                    <div *ngFor="let option of options" class="form-check form-check-inline">
+                        <input class="form-check-input" type="radio" id="cb_{{option.value}}" formControlName="radio" [value]="option.value">
+                        <label class="form-check-label" for="cb_{{option.value}}">{{ option.label }}</label>
+                    </div>
                 </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!radio.disabled" [hidden]="!radio.value"
-            (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="radio.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
diff --git a/client/src/app/instance/search/components/criteria/search-type/radio.component.ts b/client/src/app/instance/search/components/criteria/search-type/radio.component.ts
index 111afe43..8e43ccce 100644
--- a/client/src/app/instance/search/components/criteria/search-type/radio.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/radio.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 import { Option } from 'src/app/metamodel/models';
@@ -22,24 +22,32 @@ import { Option } from 'src/app/metamodel/models';
  * @class
  * @classdesc Radio search type component.
  */
-export class RadioComponent {
+export class RadioComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
     @Input() options: Option[];
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Output() addCriterion: EventEmitter<FieldCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    radio = new FormControl('');
+    public form = new FormGroup({
+        radio: new FormControl('', [Validators.required])
+    });
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const criterion = this.criterion as FieldCriterion;
+
+            this.form.controls.radio.setValue(criterion.value);
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
 
     /**
      * Emits event to add criterion to the criteria list.
@@ -47,33 +55,8 @@ export class RadioComponent {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const option = this.options.find(o => o.value === this.radio.value);
+        const option = this.options.find(o => o.value === this.form.value.radio);
         const cb = {id: this.id, type: 'field', operator: this.operator, value: option.value};
         this.addCriterion.emit(cb);
     }
-
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.radio.reset();
-            this.radio.enable();
-        } else {
-            const c = criterion as FieldCriterion;
-            this.radio.setValue(c.value);
-            this.radio.disable();
-        }
-    }
 }
diff --git a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.html b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.html
index fed58389..327c45d8 100644
--- a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.html
@@ -1,24 +1,26 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
-                <div class="readonly">in</div>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1">
-                <ng-select [formControl]="ms" [multiple]="true" [hideSelected]="true" class="ng-select-custom">
-                    <ng-option *ngFor="let option of options" [value]="option.value">{{option.label}}</ng-option>
-                </ng-select>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
+                    <div class="readonly">in</div>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1">
+                    <ng-select formControlName="select" [multiple]="true" [hideSelected]="true" class="ng-select-custom">
+                        <ng-option *ngFor="let option of options" [value]="option.value">{{option.label}}</ng-option>
+                    </ng-select>
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!ms.disabled" [hidden]="!ms.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="ms.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.ts b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.ts
index d50a510b..650ac066 100644
--- a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, SelectMultipleCriterion } from 'src/app/instance/store/models';
 import { Option } from 'src/app/metamodel/models';
@@ -16,30 +16,39 @@ import { Option } from 'src/app/metamodel/models';
 @Component({
     selector: 'app-select-multiple',
     templateUrl: 'select-multiple.component.html',
-    styleUrls: ['operator.component.css'],
+    styleUrls: ['operator.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
 /**
  * @class
  * @classdesc Select multiple search type component.
  */
-export class SelectMultipleComponent {
+export class SelectMultipleComponent implements OnChanges {
     @Input() id: number;
     @Input() label: string;
     @Input() options: Option[];
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Output() addCriterion: EventEmitter<SelectMultipleCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    ms = new FormControl();
+    public form = new FormGroup({
+        select: new FormControl('', [Validators.required])
+    });
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const multipleCriterion = this.criterion as SelectMultipleCriterion;
+
+            const values = multipleCriterion.options.map(option => option.value);
+            this.form.controls.select.setValue(values);
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
 
     /**
      * Emits event to add criterion to the criteria list.
@@ -47,33 +56,10 @@ export class SelectMultipleComponent {
      * @fires EventEmitter<SelectMultipleCriterion>
      */
     emitAdd(): void {
-        const options = this.ms.value.map(optionValue => this.options.find(o => o.value === optionValue));
+        const values = this.form.value.select as string[];
+
+        const options = this.options.filter(option => values.includes(option.value));
         const ms = {id: this.id, type: 'multiple', options};
         this.addCriterion.emit(ms);
     }
-
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.ms.reset();
-            this.ms.enable();
-        } else {
-            const c = criterion as SelectMultipleCriterion;
-            this.ms.setValue(c.options.map(o => o.value));
-            this.ms.disable();
-        }
-    }
 }
diff --git a/client/src/app/instance/search/components/criteria/search-type/select.component.html b/client/src/app/instance/search/components/criteria/search-type/select.component.html
index 969d8d0b..8db580ab 100644
--- a/client/src/app/instance/search/components/criteria/search-type/select.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/select.component.html
@@ -1,36 +1,38 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <span *ngIf="operator === 'lk'" class="pl-1" [tooltip]="helpLike" placement="right" containerClass="custom-tooltip right-tooltip">
-            <span class="far fa-question-circle fa-sm"></span>
-        </span>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
-                <app-operator
-                    [operator]="operator"
-                    [searchType]="'field'"
-                    [advancedForm]="advancedForm"
-                    [disabled]="disabledOperator"
-                    (changeOperator)="changeOperator($event)">
-                </app-operator>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col pl-sm-1">
-                <ng-select [formControl]="se" [multiple]="false" [hideSelected]="true" class="ng-select-custom">
-                    <ng-option *ngFor="let option of options" [value]="option.value">{{option.label}}</ng-option>
-                </ng-select>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <span *ngIf="operator === 'lk'" class="pl-1" [tooltip]="helpLike" placement="right" containerClass="custom-tooltip right-tooltip">
+                <span class="far fa-question-circle fa-sm"></span>
+            </span>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-sm-0">
+                    <app-operator
+                        [operator]="operator"
+                        [searchType]="'field'"
+                        [advancedForm]="advancedForm"
+                        [disabled]="disabledOperator"
+                        (changeOperator)="changeOperator($event)">
+                    </app-operator>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col pl-sm-1">
+                    <ng-select formControlName="select" [multiple]="false" [hideSelected]="true" class="ng-select-custom">
+                        <ng-option *ngFor="let option of options" [value]="option.value">{{option.label}}</ng-option>
+                    </ng-select>
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end pb-3">
-        <button class="btn btn-outline-success" *ngIf="!se.disabled" [hidden]="!se.value" (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="se.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
+</form>
 
 <ng-template #helpLike>
     <app-help-like></app-help-like>
diff --git a/client/src/app/instance/search/components/criteria/search-type/select.component.ts b/client/src/app/instance/search/components/criteria/search-type/select.component.ts
index c515a1ec..26dda32e 100644
--- a/client/src/app/instance/search/components/criteria/search-type/select.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/select.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 import { Option } from 'src/app/metamodel/models';
@@ -22,27 +22,34 @@ import { Option } from 'src/app/metamodel/models';
  * @class
  * @classdesc Select search type component.
  */
-export class SelectComponent {
+export class SelectComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
     @Input() options: Option[];
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Input() advancedForm: boolean;
     @Output() addCriterion: EventEmitter<FieldCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
-    se = new FormControl();
+    public form = new FormGroup({
+        select: new FormControl('', [Validators.required])
+    });
+    
     disabledOperator: boolean;
 
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            this.form.controls.select.setValue(changes.criterion.currentValue.value);
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
     /**
      * Modifies operator with the given one.
      *
@@ -58,35 +65,8 @@ export class SelectComponent {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const option = this.options.find(o => o.value === this.se.value);
+        const option = this.options.find(o => o.value === this.form.value.select);
         const se = {id: this.id, type: 'field', operator: this.operator, value: option.value};
         this.addCriterion.emit(se);
     }
-
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.se.reset();
-            this.se.enable();
-            this.disabledOperator = false;
-        } else {
-            const c = criterion as FieldCriterion;
-            this.se.setValue(c.value);
-            this.se.disable();
-            this.disabledOperator = true;
-        }
-    }
 }
diff --git a/client/src/app/instance/search/components/criteria/search-type/time.component.html b/client/src/app/instance/search/components/criteria/search-type/time.component.html
index de4864f7..e2685a83 100644
--- a/client/src/app/instance/search/components/criteria/search-type/time.component.html
+++ b/client/src/app/instance/search/components/criteria/search-type/time.component.html
@@ -1,37 +1,38 @@
-<div class="row">
-    <div class="col form-group">
-        <label>{{ label }}</label>
-        <div class="row">
-            <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
-                <app-operator
-                    [operator]="operator"
-                    [searchType]="'field'"
-                    [advancedForm]="advancedForm"
-                    [disabled]="disabledOperator"
-                    (changeOperator)="changeOperator($event)">
-                </app-operator>
-            </div>
-            <div class="w-100 d-block d-sm-none"></div>
-            <div class="col-auto pl-sm-1 pr-1">
-                <ng-select [formControl]="hh" [multiple]="false" placeholder="HH..." class="ng-select-custom ng-select-time">
-                    <ng-option *ngFor="let hour of hours" [value]="hour">{{ hour }}</ng-option>
-                </ng-select>
-            </div>
-            <div class="col col-sm-auto p-0 text-center">:</div>
-            <div class="col-auto pl-1">
-                <ng-select [formControl]="mm" [multiple]="false" placeholder="MM..." class="ng-select-custom ng-select-time">
-                    <ng-option *ngFor="let min of minutes" [value]="min">{{ min }}</ng-option>
-                </ng-select>
+<form [formGroup]="form" novalidate>
+    <div class="row">
+        <div class="col form-group">
+            <label>{{ label }}</label>
+            <div class="row">
+                <div class="col col-sm-auto pr-sm-1 mb-1 mb-lg-0">
+                    <app-operator
+                        [operator]="operator"
+                        [searchType]="'field'"
+                        [advancedForm]="advancedForm"
+                        [disabled]="disabledOperator"
+                        (changeOperator)="changeOperator($event)">
+                    </app-operator>
+                </div>
+                <div class="w-100 d-block d-sm-none"></div>
+                <div class="col-auto pl-sm-1 pr-1">
+                    <ng-select formControlName="hh" [multiple]="false" placeholder="HH..." class="ng-select-custom ng-select-time">
+                        <ng-option *ngFor="let hour of hours" [value]="hour">{{ hour }}</ng-option>
+                    </ng-select>
+                </div>
+                <div class="col col-sm-auto p-0 text-center">:</div>
+                <div class="col-auto pl-1">
+                    <ng-select formControlName="mm" [multiple]="false" placeholder="MM..." class="ng-select-custom ng-select-time">
+                        <ng-option *ngFor="let min of minutes" [value]="min">{{ min }}</ng-option>
+                    </ng-select>
+                </div>
             </div>
         </div>
+        <div class="col-2 text-center align-self-end mb-0 mb-sm-1 pb-3">
+            <button class="btn btn-outline-success" *ngIf="!form.disabled" [hidden]="!form.valid" (click)="emitAdd()">
+                <span class="fas fa-plus fa-fw"></span>
+            </button>
+            <button class="btn btn-outline-danger" *ngIf="form.disabled" (click)="deleteCriterion.emit(id)">
+                <span class="fa fa-times fa-fw"></span>
+            </button>
+        </div>
     </div>
-    <div class="col-2 text-center align-self-end mb-0 mb-sm-1 pb-3">
-        <button class="btn btn-outline-success" *ngIf="!hh.disabled || !mm.disabled" [hidden]="!hh.value || !mm.value"
-            (click)="emitAdd()">
-            <span class="fas fa-plus fa-fw"></span>
-        </button>
-        <button class="btn btn-outline-danger" *ngIf="hh.disabled && mm.disabled" (click)="emitDelete()">
-            <span class="fa fa-times fa-fw"></span>
-        </button>
-    </div>
-</div>
\ No newline at end of file
+</form>
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/time.component.ts b/client/src/app/instance/search/components/criteria/search-type/time.component.ts
index 0918072c..cc9d32a0 100644
--- a/client/src/app/instance/search/components/criteria/search-type/time.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/time.component.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
+import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 
@@ -21,29 +21,40 @@ import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
  * @class
  * @classdesc Time search type component.
  */
-export class TimeComponent {
+export class TimeComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
-    /**
-     * Calls getDefault function for the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    @Input()
-    set criterion(criterion: Criterion) {
-        this.getDefault(criterion);
-    }
+    @Input() criterion: Criterion;
     @Input() advancedForm: boolean;
     @Output() addCriterion: EventEmitter<FieldCriterion> = new EventEmitter();
     @Output() deleteCriterion: EventEmitter<number> = new EventEmitter();
 
     hours: string[] = this.initTime(24);
     minutes: string[] = this.initTime(60);
-    hh = new FormControl();
-    mm = new FormControl();
+
     disabledOperator: boolean;
 
+    public form = new FormGroup({
+        hh: new FormControl('', [Validators.required]),
+        mm: new FormControl('', [Validators.required])
+    });
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes.criterion && changes.criterion.currentValue) {
+            const criterion = changes.criterion.currentValue as FieldCriterion;
+
+            this.form.controls.hh.setValue(criterion.value.slice(0, 2));
+            this.form.controls.mm.setValue(criterion.value.slice(3, 5));
+            this.form.disable();
+        }
+
+        if (changes.criterion && !changes.criterion.currentValue) {
+            this.form.enable();
+            this.form.reset();
+        }
+    }
+
     /**
      * Modifies operator with the given one.
      *
@@ -59,41 +70,10 @@ export class TimeComponent {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const time = {id: this.id, type: 'field', operator: this.operator, value: this.hh.value + ':' + this.mm.value};
+        const time = {id: this.id, type: 'field', operator: this.operator, value: this.form.value.hh + ':' + this.form.value.mm};
         this.addCriterion.emit(time);
     }
 
-    /**
-     * Emits event to remove criterion ID from the criteria list.
-     *
-     * @fires EventEmitter<number>
-     */
-    emitDelete(): void {
-        this.deleteCriterion.emit(this.id);
-    }
-
-    /**
-     * Fills form with the given criterion.
-     *
-     * @param  {Criterion} criterion - The criterion.
-     */
-    getDefault(criterion: Criterion): void {
-        if (!criterion) {
-            this.hh.reset();
-            this.hh.enable();
-            this.mm.reset();
-            this.mm.enable();
-            this.disabledOperator = false;
-        } else {
-            const c = criterion as FieldCriterion;
-            this.hh.setValue(c.value.slice(0, 2));
-            this.hh.disable();
-            this.mm.setValue(c.value.slice(3, 5));
-            this.mm.disable();
-            this.disabledOperator = true;
-        }
-    }
-
     /**
      * Returns string array to represent the given time.
      *
diff --git a/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts b/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts
index ca589c83..59de49ad 100644
--- a/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts
+++ b/client/src/app/instance/shared-search/components/cone-search/cone-search.component.ts
@@ -7,7 +7,7 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
 import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { ConeSearch, Resolver } from 'src/app/instance/store/models';
diff --git a/client/src/assets/app.config.json b/client/src/assets/app.config.json
index 0270c50c..69f72dd7 100644
--- a/client/src/assets/app.config.json
+++ b/client/src/assets/app.config.json
@@ -2,7 +2,7 @@
     "apiUrl": "http://localhost:8080",
     "servicesUrl": "http://localhost:5000",
     "baseHref": "/",
-    "authenticationEnabled": true,
+    "authenticationEnabled": false,
     "ssoAuthUrl": "http://localhost:8180/auth",
     "ssoRealm": "anis",
     "ssoClientId": "anis-client",
diff --git a/client/src/styles.scss b/client/src/styles.scss
index 93c9c212..388dc72e 100644
--- a/client/src/styles.scss
+++ b/client/src/styles.scss
@@ -31,9 +31,14 @@
 @import "~bootstrap/scss/utilities";
 
 /* Import ngx-toastr bootstrap 4 alert styled design */
-
 @import '~ngx-toastr/toastr-bs4-alert';
 
+/* Import ng-select styled design */
+@import "~@ng-select/ng-select/themes/default.theme.css";
+
+/* Import datepicker ngx bootstrap styled design */
+@import '~ngx-bootstrap/datepicker/bs-datepicker.css';
+
 /* Global styles */
 
 .theme-color {
@@ -71,11 +76,11 @@ main {
 }
 
 /* Angular forms */
-input.ng-valid, select.ng-valid, textarea.ng-valid {
+input.ng-valid, select.ng-valid, ng-select.ng-valid div.ng-select-container, textarea.ng-valid {
     border-left: 5px solid #42A948; /* green */
 }
   
-input.ng-invalid, select.ng-invalid, textarea.ng-invalid {
+input.ng-invalid, select.ng-invalid, ng-select.ng-invalid div.ng-select-container, textarea.ng-invalid {
     border-left: 5px solid #a94442; /* red */
 }
 
-- 
GitLab