Browse Source

Form validation improved

pull/1/head
Sebastian 10 years ago
parent
commit
4af38a04a2
  1. 29
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  2. 16
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  3. 16
      src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.html
  4. 6
      src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.ts
  5. 16
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  6. 9
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  7. 8
      src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html
  8. 7
      src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts
  9. 8
      src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html
  10. 11
      src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts
  11. 8
      src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html
  12. 7
      src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts
  13. 24
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html
  14. 5
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts
  15. 8
      src/Squidex/app/features/settings/pages/clients/client.component.html
  16. 15
      src/Squidex/app/features/settings/pages/clients/clients-page.component.html
  17. 9
      src/Squidex/app/features/settings/pages/clients/clients-page.component.ts
  18. 7
      src/Squidex/app/framework/angular/control-errors.component.html
  19. 2
      src/Squidex/app/framework/angular/control-errors.component.scss
  20. 118
      src/Squidex/app/framework/angular/control-errors.component.ts
  21. 4
      src/Squidex/app/framework/angular/validators.spec.ts
  22. 63
      src/Squidex/app/framework/angular/validators.ts
  23. 1
      src/Squidex/app/framework/declarations.ts
  24. 3
      src/Squidex/app/framework/module.ts
  25. 25
      src/Squidex/app/shared/components/app-form.component.html
  26. 9
      src/Squidex/app/shared/components/app-form.component.ts

29
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -6,15 +6,18 @@
*/
import { Component } from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { AbstractControl, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import {
AppComponentBase,
AppsStoreService,
NotificationService,
NumberFieldPropertiesDto,
SchemaDetailsDto,
UsersProviderService
StringFieldPropertiesDto,
UsersProviderService,
ValidatorsEx
} from 'shared';
@Component({
@ -51,9 +54,27 @@ export class ContentPageComponent extends AppComponentBase {
const controls: { [key: string]: AbstractControl } = {};
for (const field of schema.fields) {
const formControl = new FormControl();
const validators: ValidatorFn[] = [];
controls[field.name] = formControl;
if (field.properties.isRequired) {
validators.push(Validators.required);
}
if (field.properties instanceof NumberFieldPropertiesDto) {
validators.push(ValidatorsEx.between(field.properties.minValue, field.properties.maxValue));
}
if (field.properties instanceof StringFieldPropertiesDto) {
if (field.properties.minLength) {
validators.push(Validators.minLength(field.properties.minLength));
}
if (field.properties.maxLength) {
validators.push(Validators.maxLength(field.properties.maxLength));
}
if (field.properties.pattern) {
validators.push(ValidatorsEx.pattern(field.properties.pattern, field.properties.patternMessage));
}
}
controls[field.name] = new FormControl(validators);
}
this.contentForm = new FormGroup(controls);

16
src/Squidex/app/features/schemas/pages/schema/field.component.html

@ -86,13 +86,7 @@
<label for="field-label" class="col-xs-3 col-form-label">Label</label>
<div class="col-xs-6">
<div class="errors-box" *ngIf="editForm.controls.label.invalid && editForm.controls.label.touched" [@fade]>
<div class="errors">
<span *ngIf="editForm.controls.label.hasError('maxlength')">
Label can not have more than 100 characters.
</span>
</div>
</div>
<sqx-control-errors for="label" [submitted]="editForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="field-label" maxlength="100" formControlName="label" />
@ -106,13 +100,7 @@
<label for="field-hints" class="col-xs-3 col-form-label">Hints</label>
<div class="col-xs-6">
<div class="errors-box" *ngIf="editForm.controls.hints.invalid && editForm.controls.hints.touched" [@fade]>
<div class="errors">
<span *ngIf="editForm.controls.hints.hasError('maxlength')">
Hints can not have more than 100 characters.
</span>
</div>
</div>
<sqx-control-errors for="hints" [submitted]="editForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="field-hints" maxlength="100" formControlName="hints" />

16
src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.html

@ -8,13 +8,7 @@
<div class="form-group">
<label for="schema-label">Label</label>
<div class="errors-box" *ngIf="editForm.controls.label.invalid && editForm.controls.label.touched" [@fade]>
<div class="errors">
<span *ngIf="editForm.controls.label.hasError('maxlength')">
Label can not have more than 100 characters.
</span>
</div>
</div>
<sqx-control-errors for="label" [submitted]="editForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="schema-label" formControlName="label" />
</div>
@ -22,13 +16,7 @@
<div class="form-group">
<label for="schema-hints">Hints</label>
<div class="errors-box" *ngIf="editForm.controls.hints.invalid && editForm.controls.hints.touched" [@fade]>
<div class="errors">
<span *ngIf="editForm.controls.hints.hasError('maxlength')">
Hints can not have more than 1000 characters.
</span>
</div>
</div>
<sqx-control-errors for="hints" [submitted]="editForm.touched"></sqx-control-errors>
<textarea type="text" class="form-control" id="schema-hints" formControlName="hints" rows="4"></textarea>
</div>

6
src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.ts

@ -9,7 +9,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {
fadeAnimation,
Notification,
NotificationService,
SchemasService
@ -20,10 +19,7 @@ import { SchemaPropertiesDto } from './schema-properties';
@Component({
selector: 'sqx-schema-edit-form',
styleUrls: ['./schema-edit-form.component.scss'],
templateUrl: './schema-edit-form.component.html',
animations: [
fadeAnimation
]
templateUrl: './schema-edit-form.component.html'
})
export class SchemaEditFormComponent implements OnInit {
@Output()

16
src/Squidex/app/features/schemas/pages/schema/schema-page.component.html

@ -43,25 +43,15 @@
<option *ngFor="let type of fieldTypes">{{type}}</option>
</select>
</div>
<div class="form-group">
<div class="errors-box" *ngIf="addFieldForm.controls.name.invalid && addFieldForm.controls.name.dirty">
<div class="errors">
<span *ngIf="addFieldForm.controls.name.hasError('required')">
Name is required.
</span>
<span *ngIf="addFieldForm.controls.name.hasError('maxlength')">
Name can not have more than 40 characters.
</span>
<span *ngIf="addFieldForm.controls.name.hasError('pattern')">
Name can contain lower case letters (a-z), numbers and dashes (not at the end).
</span>
</div>
</div>
<sqx-control-errors for="name" [submitted]="addFieldForm.touched"></sqx-control-errors>
<input type="text" class="form-control" formControlName="name" maxlength="40" placeholder="Enter field name" />
</div>
<button type="submit" class="btn btn-success" [disabled]="addFieldForm.invalid">Add Field</button>
<button type="reset" class="btn btn-link" (click)="resetFieldForm()">Cancel</button>
</form>
</div>
</div>

9
src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts

@ -24,7 +24,8 @@ import {
SchemaDetailsDto,
SchemasService,
UpdateFieldDto,
UsersProviderService
UsersProviderService,
ValidatorsEx
} from 'shared';
import { SchemaPropertiesDto } from './schema-properties';
@ -63,7 +64,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
[
Validators.required,
Validators.maxLength(40),
Validators.pattern('[a-z0-9]+(\-[a-z0-9]+)*')
ValidatorsEx.pattern('[a-z0-9]+(\-[a-z0-9]+)*', 'Name can contain lower case letters (a-z), numbers and dashes (not at the end).')
]]
});
@ -203,6 +204,10 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
}
}
public resetFieldForm() {
this.addFieldForm.reset();
}
public onSchemaSaved(properties: SchemaPropertiesDto) {
this.updateProperties(properties);

8
src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.html

@ -3,13 +3,7 @@
<label for="field-placeholder" class="col-xs-3 col-form-label">Placeholder</label>
<div class="col-xs-6">
<div class="errors-box" *ngIf="editForm.controls.placeholder.invalid && editForm.controls.placeholder.touched" [@fade]>
<div class="errors">
<span *ngIf="editForm.controls.placeholder.hasError('maxlength')">
Placeholder can not have more than 100 characters.
</span>
</div>
</div>
<sqx-control-errors for="placeholder" [submitted]="editForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="field-placeholder" maxlength="100" formControlName="placeholder" />

7
src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts

@ -8,15 +8,12 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { fadeAnimation, BooleanFieldPropertiesDto } from 'shared';
import { BooleanFieldPropertiesDto } from 'shared';
@Component({
selector: 'sqx-boolean-ui',
styleUrls: ['boolean-ui.component.scss'],
templateUrl: 'boolean-ui.component.html',
animations: [
fadeAnimation
]
templateUrl: 'boolean-ui.component.html'
})
export class BooleanUIComponent implements OnInit {
@Input()

8
src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.html

@ -3,13 +3,7 @@
<label for="field-placeholder" class="col-xs-3 col-form-label">Placeholder</label>
<div class="col-xs-6">
<div class="errors-box" *ngIf="editForm.controls.placeholder.invalid && editForm.controls.placeholder.touched" [@fade]>
<div class="errors">
<span *ngIf="editForm.controls.placeholder.hasError('maxlength')">
Placeholder can not have more than 100 characters.
</span>
</div>
</div>
<sqx-control-errors for="placeholder" [submitted]="editForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="field-placeholder" maxlength="100" formControlName="placeholder" />

11
src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts

@ -9,19 +9,12 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Observable, Subscription } from 'rxjs';
import {
fadeAnimation,
FloatConverter,
NumberFieldPropertiesDto
} from 'shared';
import { FloatConverter, NumberFieldPropertiesDto } from 'shared';
@Component({
selector: 'sqx-number-ui',
styleUrls: ['number-ui.component.scss'],
templateUrl: 'number-ui.component.html',
animations: [
fadeAnimation
]
templateUrl: 'number-ui.component.html'
})
export class NumberUIComponent implements OnDestroy, OnInit {
private editorSubscription: Subscription;

8
src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.html

@ -3,13 +3,7 @@
<label for="field-input-placeholder" class="col-xs-3 col-form-label">Placeholder</label>
<div class="col-xs-6">
<div class="errors-box" *ngIf="editForm.controls.placeholder.invalid && editForm.controls.placeholder.touched" [@fade]>
<div class="errors">
<span *ngIf="editForm.controls.placeholder.hasError('maxlength')">
Placeholder can not have more than 100 characters.
</span>
</div>
</div>
<sqx-control-errors for="placeholder" [submitted]="editForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="field-input-placeholder" maxlength="100" formControlName="placeholder" />

7
src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts

@ -9,15 +9,12 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Observable, Subscription } from 'rxjs';
import { fadeAnimation, StringFieldPropertiesDto } from 'shared';
import { StringFieldPropertiesDto } from 'shared';
@Component({
selector: 'sqx-string-ui',
styleUrls: ['string-ui.component.scss'],
templateUrl: 'string-ui.component.html',
animations: [
fadeAnimation
]
templateUrl: 'string-ui.component.html'
})
export class StringUIComponent implements OnDestroy, OnInit {
private editorSubscription: Subscription;

24
src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html

@ -8,29 +8,15 @@
<div class="form-group">
<label for="schema-name">Name</label>
<div class="errors-box" *ngIf="createForm.controls.name.invalid && createForm.controls.name.touched" [@fade]>
<div class="errors">
<span *ngIf="createForm.controls.name.hasError('required')">
Name is required.
</span>
<span *ngIf="createForm.controls.name.hasError('maxlength')">
Name can not have more than 40 characters.
</span>
<span *ngIf="createForm.controls.name.hasError('pattern')">
Name can contain lower case letters (a-z), numbers and dashes only (not at the end).
</span>
</div>
</div>
<sqx-control-errors for="name" [submitted]="createForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="schema-name" formControlName="name" />
<span class="form-hint">
<p>
The schema name becomes part of the api url,<br /> e.g https://{{appName}}.squidex.io/<b>{{schemaName | async}}</b>/.
</p>
<p>
It must contain lower case letters (a-z), numbers and dashes only, and cannot be longer than 40 characters. The name cannot be changed later.
</p>
The schema name becomes part of the api url,<br /> e.g https://{{appName}}.squidex.io/<b>{{schemaName | async}}</b>/.
</span>
<span class="form-hint">
It must contain lower case letters (a-z), numbers and dashes only, and cannot be longer than 40 characters. The name cannot be changed later.
</span>
</div>

5
src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts

@ -15,7 +15,8 @@ import {
DateTime,
fadeAnimation,
SchemaDto,
SchemasService
SchemasService,
ValidatorsEx
} from 'shared';
const FALLBACK_NAME = 'my-schema';
@ -45,7 +46,7 @@ export class SchemaFormComponent {
[
Validators.required,
Validators.maxLength(40),
Validators.pattern('[a-z0-9]+(\-[a-z0-9]+)*')
ValidatorsEx.pattern('[a-z0-9]+(\-[a-z0-9]+)*', 'Name can contain lower case letters (a-z), numbers and dashes only (not at the end).')
]]
});

8
src/Squidex/app/features/settings/pages/clients/client.component.html

@ -15,13 +15,7 @@
<div class="client-header">
<form *ngIf="isRenaming" class="form-inline" [formGroup]="renameForm" (ngSubmit)="rename()">
<div class="form-group">
<div class="errors-box" *ngIf="renameForm.controls.name.invalid" [@fade]>
<div class="errors">
<span *ngIf="renameForm.controls.name.hasError('required')">
Name is required.
</span>
</div>
</div>
<sqx-control-errors for="name" [submitted]="renameForm.touched"></sqx-control-errors>
<input type="text" class="form-control client-name enabled" formControlName="name" maxlength="20" sqxFocusOnInit (keydown)="onKeyDown($event.keyCode)" />
</div>

15
src/Squidex/app/features/settings/pages/clients/clients-page.component.html

@ -22,24 +22,13 @@
<div class="table-items-footer">
<form class="form-inline" [formGroup]="addClientForm" (ngSubmit)="attachClient()">
<div class="form-group">
<div class="errors-box" *ngIf="addClientForm.controls.name.invalid && addClientForm.controls.name.dirty">
<div class="errors">
<span *ngIf="addClientForm.controls.name.hasError('required')">
Name is required.
</span>
<span *ngIf="addClientForm.controls.name.hasError('maxlength')">
Name can not have more than 40 characters.
</span>
<span *ngIf="addClientForm.controls.name.hasError('pattern')">
Name can contain lower case letters (a-z), numbers and dashes (not at the end).
</span>
</div>
</div>
<sqx-control-errors for="name" [submitted]="addClientForm.touched"></sqx-control-errors>
<input type="text" class="form-control" formControlName="name" maxlength="40" placeholder="Enter client name" />
</div>
<button type="submit" class="btn btn-success" [disabled]="addClientForm.invalid">Add Client</button>
<button type="reset" class="btn btn-link" (click)="resetClientForm()">Cancel</button>
</form>
</div>
</div>

9
src/Squidex/app/features/settings/pages/clients/clients-page.component.ts

@ -19,7 +19,8 @@ import {
MessageBus,
NotificationService,
UpdateAppClientDto,
UsersProviderService
UsersProviderService,
ValidatorsEx
} from 'shared';
function rename(client: AppClientDto, name: string): AppClientDto {
@ -40,7 +41,7 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
[
Validators.required,
Validators.maxLength(40),
Validators.pattern('[a-z0-9]+(\-[a-z0-9]+)*')
ValidatorsEx.pattern('[a-z0-9]+(\-[a-z0-9]+)*', 'Name can contain lower case letters (a-z), numbers and dashes (not at the end).')
]]
});
@ -88,6 +89,10 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
});
}
public resetClientForm() {
this.addClientForm.reset();
}
public attachClient() {
this.addClientForm.markAsDirty();

7
src/Squidex/app/framework/angular/control-errors.component.html

@ -0,0 +1,7 @@
<div class="errors-box" *ngIf="errorMessages" [@fade]>
<div class="errors">
<span *ngFor="let message of errorMessages">
{{message}}
</span>
</div>
</div>

2
src/Squidex/app/framework/angular/control-errors.component.scss

@ -0,0 +1,2 @@
@import '_mixins';
@import '_vars';

118
src/Squidex/app/framework/angular/control-errors.component.ts

@ -0,0 +1,118 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { ChangeDetectorRef, ChangeDetectionStrategy, Component, Host, Input, OnChanges, OnInit, OnDestroy, Optional } from '@angular/core';
import { AbstractControl, FormGroupDirective } from '@angular/forms';
import { Subscription } from 'rxjs';
import { fadeAnimation } from './animations';
const DEFAULT_ERRORS: { [key: string]: string } = {
required: '{field} is required.',
pattern: '{field} does not follow the pattern.',
patternMessage: '{message}',
minValue: '{field} must be larget than {minValue}.',
maxValue: '{field} must be larget than {maxValue}.',
minLength: '{field} must have more than {minLength} characters.',
maxLength: '{field} cannot have more than {maxLength} characters.',
validNumber: '{field} is not a valid number.',
validValues: '{field} is not a valid value.'
};
@Component({
selector: 'sqx-control-errors',
styleUrls: ['./control-errors.component.scss'],
templateUrl: './control-errors.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
fadeAnimation
]
})
export class ControlErrorsComponent implements OnChanges, OnInit, OnDestroy {
private formSubscription: Subscription;
private displayFieldName: string;
private control: AbstractControl;
@Input()
public for: string;
@Input()
public fieldName: string;
@Input()
public errors: string;
@Input()
public submitted: boolean;
public errorMessages: string[];
constructor(@Optional() @Host() private readonly form: FormGroupDirective,
private readonly changeDetector: ChangeDetectorRef
) {
if (!this.form) {
throw new Error('control-errors must be used with a parent formGroup directive');
}
}
public ngOnChanges() {
if (this.fieldName) {
this.displayFieldName = this.fieldName;
} else if (this.for) {
this.displayFieldName = this.for.substr(0, 1).toUpperCase() + this.for.substr(1);
}
this.update();
}
public ngOnDestroy() {
this.formSubscription.unsubscribe();
}
public ngOnInit() {
this.control = this.form.form.controls[this.for];
this.formSubscription =
this.form.form.statusChanges.merge(this.control.statusChanges)
.subscribe(_ => {
this.update();
});
}
private update() {
if (!this.control) {
return;
}
if (this.control.invalid && (this.control.touched || this.form.form.touched)) {
const errors: string[] = [];
for (let key in <any>this.control.errors) {
if (this.control.errors.hasOwnProperty(key)) {
let message: string = (this.errors ? this.errors[key] : null) || DEFAULT_ERRORS[key];
let properties = this.control.errors[key];
for (let property in properties) {
if (properties.hasOwnProperty(property)) {
message = message.replace('{' + property + '}', properties[property]);
}
}
message = message.replace('{field}', this.displayFieldName);
errors.push(message);
}
}
this.errorMessages = errors.length > 0 ? errors : null;
} else {
this.errorMessages = null;
}
this.changeDetector.markForCheck();
}
}

4
src/Squidex/app/framework/angular/validators.spec.ts

@ -7,13 +7,13 @@
import { FormControl } from '@angular/forms';
import { Validators } from './../';
import { ValidatorsEx } from './../';
describe('Validators', () => {
let validateBetween: any;
beforeEach(() => {
validateBetween = Validators.between(10, 200);
validateBetween = ValidatorsEx.between(10, 200);
});
it('should return error when not a number', () => {

63
src/Squidex/app/framework/angular/validators.ts

@ -5,19 +5,66 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { AbstractControl } from '@angular/forms';
import { AbstractControl, ValidatorFn, Validators } from '@angular/forms';
export class Validators {
public static between(minValue: number, maxValue: number) {
export class ValidatorsEx {
public static pattern(pattern: string | RegExp, message: string | undefined = undefined): ValidatorFn {
if (!pattern) {
return Validators.nullValidator;
}
let regex: RegExp;
let regexStr: string;
if (typeof pattern === 'string') {
regexStr = `^${pattern}$`;
regex = new RegExp(regexStr);
} else {
regexStr = pattern.toString();
regex = pattern;
}
return (control: AbstractControl): { [key: string]: any } => {
const n: string = control.value;
if (n == null || n.length === 0) {
return null;
}
if (!regex.test(n)) {
if (message) {
return { patternMessage: { requiredPattern: regexStr, actualValue: n, message } };
} else {
return { pattern: { requiredPattern: regexStr, actualValue: n } };
}
}
return null;
};
}
public static between(minValue: number | undefined, maxValue: number | undefined) {
return (control: AbstractControl): { [key: string]: any } => {
const n: number = control.value;
if (typeof n !== 'number') {
return { 'validNumber': false };
} else if (n < minValue) {
return { 'minValue': { 'requiredValue': minValue, 'actualValue': n } };
} else if (n > maxValue) {
return { 'maxValue': { 'requiredValue': maxValue, 'actualValue': n } };
return { validNumber: false };
} else if (minValue && n < minValue) {
return { minValue: { minValue, actualValue: n } };
} else if (maxValue && n > maxValue) {
return { maxValue: { maxValue, actualValue: n } };
}
return {};
};
}
public static validValues<T>(values: T[]) {
return (control: AbstractControl): { [key: string]: any } => {
const n: T = control.value;
if (values.indexOf(n) < 0) {
return { validValues: false };
}
return {};

1
src/Squidex/app/framework/declarations.ts

@ -9,6 +9,7 @@ export * from './angular/animations';
export * from './angular/autocomplete.component';
export * from './angular/validators';
export * from './angular/cloak.directive';
export * from './angular/control-errors.component';
export * from './angular/copy.directive';
export * from './angular/date-time.pipes';
export * from './angular/focus-on-change.directive';

3
src/Squidex/app/framework/module.ts

@ -15,6 +15,7 @@ import {
AutocompleteComponent,
ClipboardService,
CloakDirective,
ControlErrorsComponent,
CopyDirective,
DayOfWeekPipe,
DayPipe,
@ -55,6 +56,7 @@ import {
declarations: [
AutocompleteComponent,
CloakDirective,
ControlErrorsComponent,
CopyDirective,
DayOfWeekPipe,
DayPipe,
@ -80,6 +82,7 @@ import {
exports: [
AutocompleteComponent,
CloakDirective,
ControlErrorsComponent,
CopyDirective,
DayOfWeekPipe,
DayPipe,

25
src/Squidex/app/shared/components/app-form.component.html

@ -8,29 +8,16 @@
<div class="form-group">
<label for="app-name">Name</label>
<div class="errors-box" *ngIf="createForm.controls.name.invalid && createForm.controls.name.touched" [@fade]>
<div class="errors">
<span *ngIf="createForm.controls.name.hasError('required')">
Name is required.
</span>
<span *ngIf="createForm.controls.name.hasError('maxlength')">
Name can not have more than 40 characters.
</span>
<span *ngIf="createForm.controls.name.hasError('pattern')">
Name can contain lower case letters (a-z), numbers and dashes only (not at the end).
</span>
</div>
</div>
<sqx-control-errors for="name" [submitted]="createForm.touched"></sqx-control-errors>
<input type="text" class="form-control" id="app-name" formControlName="name" />
<span class="form-hint">
<p>
The app name becomes part of the api url,<br /> e.g https://<b>{{appName | async}}</b>.squidex.io/.
</p>
<p>
It must contain lower case letters (a-z), numbers and dashes only, and cannot be longer than 40 characters. The name cannot be changed later.
</p>
The app name becomes part of the api url,<br /> e.g https://<b>{{appName | async}}</b>.squidex.io/.
</span>
<span class="form-hint">
It must contain lower case letters (a-z), numbers and dashes only, and cannot be longer than 40 characters. The name cannot be changed later.
</span>
</div>

9
src/Squidex/app/shared/components/app-form.component.ts

@ -9,7 +9,7 @@ import { Component, EventEmitter, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { fadeAnimation } from 'framework';
import { ValidatorsEx } from 'framework';
import { AppsStoreService } from './../services/apps-store.service';
import { AppDto, CreateAppDto } from './../services/apps.service';
@ -19,10 +19,7 @@ const FALLBACK_NAME = 'my-app';
@Component({
selector: 'sqx-app-form',
styleUrls: ['./app-form.component.scss'],
templateUrl: './app-form.component.html',
animations: [
fadeAnimation
]
templateUrl: './app-form.component.html'
})
export class AppFormComponent {
@Output()
@ -38,7 +35,7 @@ export class AppFormComponent {
[
Validators.required,
Validators.maxLength(40),
Validators.pattern('[a-z0-9]+(\-[a-z0-9]+)*')
ValidatorsEx.pattern('[a-z0-9]+(\-[a-z0-9]+)*', 'Name can contain lower case letters (a-z), numbers and dashes (not at the end).')
]]
});

Loading…
Cancel
Save