Browse Source

Reference fields.

pull/387/head
Sebastian Stehle 7 years ago
parent
commit
29b617f0d2
  1. 2
      src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs
  2. 7
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs
  3. 5
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs
  4. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs
  5. 2
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  6. 5
      src/Squidex/app/features/content/pages/contents/contents-page.component.html
  7. 2
      src/Squidex/app/features/content/shared/content-item.component.html
  8. 5
      src/Squidex/app/features/content/shared/content-item.component.ts
  9. 5
      src/Squidex/app/features/content/shared/contents-selector.component.html
  10. 2
      src/Squidex/app/features/content/shared/references-dropdown.component.ts
  11. 7
      src/Squidex/app/features/content/shared/references-editor.component.html
  12. 18
      src/Squidex/app/features/schemas/pages/schema/forms/field-form-common.component.ts
  13. 24
      src/Squidex/app/shared/services/schemas.service.ts
  14. 1
      src/Squidex/app/shared/services/schemas.types.ts
  15. 9
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs
  16. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs

2
src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs

@ -13,6 +13,8 @@ namespace Squidex.Domain.Apps.Core.Schemas
public bool IsListField { get; set; }
public bool IsReferenceField { get; set; }
public string Placeholder { get; set; }
public string EditorUrl { get; set; }

7
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs

@ -66,5 +66,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
return this;
}
public FieldBuilder ShowInReferences()
{
field.Properties.IsReferenceField = true;
return this;
}
}
}

5
src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs

@ -28,6 +28,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
yield return new ValidationError("UI field cannot be a list field.", nameof(properties.IsListField));
}
if (!properties.IsForApi() && properties.IsReferenceField)
{
yield return new ValidationError("UI field cannot be a reference field.", nameof(properties.IsReferenceField));
}
foreach (var error in properties.Accept(Instance))
{
yield return error;

5
src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs

@ -47,6 +47,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// </summary>
public bool IsListField { get; set; }
/// <summary>
/// Determines if the field should be displayed in reference lists.
/// </summary>
public bool IsReferenceField { get; set; }
/// <summary>
/// Optional url to the editor.
/// </summary>

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

@ -115,7 +115,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
}
public canDeactivate(): Observable<boolean> {
if (!this.contentForm.form.dirty || !this.content) {
if (!this.contentForm.form.dirty) {
return of(true);
} else {
return this.dialogs.confirm('Unsaved changes', 'You have unsaved changes, do you want to close the current content view and discard your changes?');

5
src/Squidex/app/features/content/pages/contents/contents-page.component.html

@ -90,11 +90,12 @@
<table class="table table-items table-fixed">
<tbody *ngFor="let content of contentsState.contents | async; trackBy: trackByContent">
<tr [sqxContent]="content" [routerLink]="[content.id]" routerLinkActive="active"
[language]="language"
[schema]="schema"
[selected]="isItemSelected(content)"
(selectedChange)="selectItem(content, $event)"
(statusChange)="changeStatus(content, $event)"
[language]="language"
[schema]="schema"
[schemaFields]="schema.listFields"
(delete)="delete(content)"
(clone)="clone(content)">
</tr>

2
src/Squidex/app/features/content/shared/content-item.component.html

@ -11,7 +11,7 @@
</ng-template>
</td>
<td class="cell-auto" *ngFor="let field of schema.listFields; let i = index; trackBy: trackByField.bind(this)" [sqxStopClick]="isDirty || (field.isInlineEditable && patchAllowed)">
<td class="cell-auto" *ngFor="let field of schemaFields; let i = index; trackBy: trackByField.bind(this)" [sqxStopClick]="isDirty || (field.isInlineEditable && patchAllowed)">
<ng-container *ngIf="field.isInlineEditable && patchAllowed; else displayTemplate">
<sqx-content-value-editor [form]="patchForm.form" [field]="field"></sqx-content-value-editor>
</ng-container>

5
src/Squidex/app/features/content/shared/content-item.component.ts

@ -58,6 +58,9 @@ export class ContentItemComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
@Input()
public schemaFields: RootFieldDto[];
@Input()
public canClone: boolean;
@ -148,7 +151,7 @@ export class ContentItemComponent implements OnChanges {
private updateValues() {
this.values = [];
for (let field of this.schema.listFields) {
for (let field of this.schemaFields) {
const value = this.getRawValue(field);
if (Types.isUndefined(value)) {

5
src/Squidex/app/features/content/shared/contents-selector.component.html

@ -33,7 +33,7 @@
<th class="cell-select">
<input type="checkbox" class="form-check" [ngModel]="isAllSelected" (ngModelChange)="selectAll($event)" />
</th>
<th class="cell-auto" *ngFor="let field of schema.listFields">
<th class="cell-auto" *ngFor="let field of schema.referenceFields">
<sqx-table-header [text]="field.displayName"
[sortable]="field.properties.isSortable"
[sorting]="filter.sortMode(field) | async"
@ -64,7 +64,8 @@
[selected]="isItemSelected(content)"
(selectedChange)="selectContent(content)"
[language]="language"
[schema]="schema"
[schema]="schema"
[schemaFields]="schema.referenceFields"
isReadOnly="true"></tr>
<tr class="spacer"></tr>
</tbody>

2
src/Squidex/app/features/content/shared/references-dropdown.component.ts

@ -142,7 +142,7 @@ export class ReferencesDropdownComponent extends StatefulControlComponent<State,
return contents.map(content => {
const values: any[] = [];
for (let field of schema.listFields) {
for (let field of schema.referenceFields) {
const value = getRawValue(field, content.data, this.languageField);
if (!Types.isUndefined(value)) {

7
src/Squidex/app/features/content/shared/references-editor.component.html

@ -1,12 +1,12 @@
<div class="references-container" [class.disabled]="snapshot.isDisabled">
<ng-container *ngIf="snapshot.schema">
<ng-container *ngIf="snapshot.schema; let schema">
<div class="drop-area-container">
<div class="drop-area" (click)="selectorDialog.show()">
Click here to link content items.
</div>
</div>
<table class="table table-items table-fixed" [class.disabled]="snapshot.isDisabled" *ngIf="snapshot.schema && snapshot.contentItems && snapshot.contentItems.length > 0"
<table class="table table-items table-fixed" [class.disabled]="snapshot.isDisabled" *ngIf="schema && snapshot.contentItems && snapshot.contentItems.length > 0"
[sqxSortModel]="snapshot.contentItems.mutableValues"
(sqxSort)="sort($event)">
<tbody *ngFor="let content of snapshot.contentItems; trackBy: trackByContent">
@ -15,7 +15,8 @@
[isReadOnly]="true"
[isReference]="true"
[isCompact]="isCompact"
[schema]="snapshot.schema"
[schema]="schema"
[schemaFields]="schema.referenceFields"
(delete)="remove(content)"></tr>
<tr class="spacer"></tr>
</tbody>

18
src/Squidex/app/features/schemas/pages/schema/forms/field-form-common.component.ts

@ -68,6 +68,21 @@ import { FieldDto } from '@app/shared';
</sqx-form-hint>
</div>
</div>
<div class="form-group row" *ngIf="field.properties.isContentField">
<div class="col-6 offset-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="{{field.fieldId}}_fieldReferencefield" formControlName="isReferenceField" />
<label class="form-check-label" for="{{field.fieldId}}_fieldReferencefield">
Reference Field
</label>
</div>
<sqx-form-hint>
Reference fields are shown as a column in the content list when referenced by another content.<br />When no reference field is defined, the first field is used.
</sqx-form-hint>
</div>
</div>
</div>
`
})
@ -88,6 +103,9 @@ export class FieldFormCommonComponent implements OnInit {
this.editForm.setControl('isListField',
new FormControl(this.field.properties.isListField));
this.editForm.setControl('isReferenceField',
new FormControl(this.field.properties.isReferenceField));
this.editForm.setControl('editorUrl',
new FormControl(this.field.properties.editorUrl));

24
src/Squidex/app/shared/services/schemas.service.ts

@ -82,6 +82,7 @@ export class SchemaDto {
export class SchemaDetailsDto extends SchemaDto {
public listFields: RootFieldDto[];
public listFieldsEditable: RootFieldDto[];
public referenceFields: RootFieldDto[];
constructor(links: ResourceLinks, id: string, name: string, category: string,
properties: SchemaPropertiesDto,
@ -99,19 +100,24 @@ export class SchemaDetailsDto extends SchemaDto {
super(links, id, name, category, properties, isSingleton, isPublished, created, createdBy, lastModified, lastModifiedBy, version);
if (fields) {
let listFields = this.fields.filter(x => x.properties.isListField && x.properties.isContentField);
this.listFields = this.getField(x => x.properties.isListField);
this.listFieldsEditable = this.listFields.filter(x => x.isInlineEditable);
if (listFields.length === 0 && this.fields.length > 0) {
listFields = [this.fields[0]];
}
this.referenceFields = this.getField(x => x.properties.isReferenceField);
}
}
if (listFields.length === 0) {
listFields = NONE_FIELDS;
}
private getField(predicate: (field: RootFieldDto) => boolean) {
let fields = this.fields.filter(x => predicate(x) && x.properties.isContentField);
this.listFields = listFields;
this.listFieldsEditable = listFields.filter(x => x.isInlineEditable);
if (fields.length === 0 && this.fields.length > 0) {
fields = [this.fields[0]];
}
if (fields.length === 0) {
fields = NONE_FIELDS;
}
return fields;
}
public export(): any {

1
src/Squidex/app/shared/services/schemas.types.ts

@ -141,6 +141,7 @@ export abstract class FieldPropertiesDto {
public readonly placeholder?: string;
public readonly isRequired: boolean = false;
public readonly isListField: boolean = false;
public readonly isReferenceField: boolean = false;
constructor(public readonly editor: string,
props?: Partial<FieldPropertiesDto>

9
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs

@ -261,6 +261,15 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
new ValidationError("UI field cannot be a list field.", "Properties.IsListField"));
}
[Fact]
public void CanUpdate_should_throw_exception_if_marking_a_ui_field_as_reference_field()
{
var command = new UpdateField { FieldId = 4, Properties = new UIFieldProperties { IsReferenceField = true } };
ValidationAssert.Throws(() => GuardSchemaField.CanUpdate(schema_0, command),
new ValidationError("UI field cannot be a reference field.", "Properties.IsReferenceField"));
}
[Fact]
public void CanUpdate_should_throw_exception_if_properties_null()
{

5
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs

@ -354,7 +354,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
Name = "field1",
Properties = new UIFieldProperties
{
IsListField = true
IsListField = true,
IsReferenceField = true,
},
IsHidden = true,
IsDisabled = true,
@ -367,6 +368,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
await ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider),
new ValidationError("UI field cannot be a list field.",
"Fields[1].Properties.IsListField"),
new ValidationError("UI field cannot be a reference field.",
"Fields[1].Properties.IsReferenceField"),
new ValidationError("UI field cannot be hidden.",
"Fields[1].IsHidden"),
new ValidationError("UI field cannot be disabled.",

Loading…
Cancel
Save