Skip to content

Commit 9e9bfc6

Browse files
authored
Merge pull request #265 from jmartinm/244-templating-complex-fields
Add configurable templating for complex fields.
2 parents cd53d8d + 2aa2491 commit 9e9bfc6

File tree

10 files changed

+125
-40
lines changed

10 files changed

+125
-40
lines changed

example/app/app.component.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<ng-template let-item="item" #referenceTemplate>
2+
<div class="reference-template-container" [ngSwitch]="item.hasIn(['record', '$ref'])">
3+
<span *ngSwitchCase="true">
4+
<i class="fa fa-check-circle success" aria-hidden="true"></i>
5+
<a href="{{item.getIn(['record', '$ref'])}}" target="_blank">{{item.getIn(['reference', 'misc', 0])}}</a>
6+
</span>
7+
<span *ngSwitchDefault>
8+
<i class="fa fa-exclamation-triangle warning" aria-hidden="true"></i>
9+
{{item.getIn(['reference', 'misc', 0])}}
10+
</span>
11+
</div>
12+
</ng-template>
13+
<json-editor *ngIf="record && schema"
14+
[config]="config.jsonEditorConfig"
15+
[record]="record"
16+
(onRecordChange)="onRecordChange($event)"
17+
[schema]="schema"
18+
[templates]="{referenceTemplate: referenceTemplate}">
19+
</json-editor>

example/app/app.component.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.fa-check-circle.success {
2+
color: green;
3+
}
4+
5+
.fa-exclamation-triangle.warning {
6+
color: orange;
7+
}
8+
9+
.reference-template-container {
10+
background-color: white;
11+
box-shadow: 0 0.5px 0 0 #ffffff inset, 0 1px 2px 0 #B3B3B3;
12+
padding: 5px;
13+
margin-bottom: 5px;
14+
}

example/app/app.component.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,7 @@ import { AppConfig } from './app.config';
3333
styleUrls: [
3434
'app.component.scss'
3535
],
36-
template: `
37-
<json-editor *ngIf="record && schema"
38-
[config]="config.jsonEditorConfig"
39-
[record]="record"
40-
(onRecordChange)="onRecordChange($event)"
41-
[schema]="schema">
42-
</json-editor>
43-
`
36+
templateUrl: 'app.component.html'
4437
})
4538
export class AppComponent {
4639
record: Object;

example/app/app.config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ export class AppConfig {
3838
findMultiple: (value, expression) => {
3939
return JSON.stringify(value).search(expression) > -1;
4040
},
41-
itemsPerPage: 5,
41+
itemsPerPage: 20,
4242
maxVisiblePageCount: 5
43+
},
44+
viewTemplateConfig: {
45+
itemTemplateName: 'referenceTemplate',
46+
showEditForm: (value) => {
47+
return !(value.hasIn(['record', '$ref']));
48+
}
4349
}
4450
},
4551
'/arxiv_eprints/items/properties/value': {

src/complex-list-field/complex-list-field.component.html

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
</td>
3030
<td class="navigator-item-right">
3131
<label>
32-
{{paginatedIndices[0] + 1}}-{{paginatedIndices[paginatedIndices.length - 1] + 1}} of {{values.size}} {{path[path.length - 1]}}
32+
{{paginatedItems[0].index + 1}}-{{paginatedItems[paginatedItems.length - 1].index + 1}} of {{values.size}} {{path[path.length - 1]}}
3333
</label>
3434
<br>
3535
<pagination [totalItems]="values.size" [ngModel]="currentPage" [maxSize]="navigator.maxVisiblePageCount" [itemsPerPage]="navigator.itemsPerPage"
@@ -39,11 +39,19 @@
3939
</tr>
4040
</table>
4141
<!-- Elements -->
42-
<div *ngFor="let pIndex of paginatedIndices; let i = index; trackBy:trackByIndex">
42+
<div *ngFor="let item of paginatedItems; trackBy:trackByIndex">
4343
<div class="complex-list-field-wrapper">
44-
<object-field [value]="values.get(pIndex)" [schema]="schema.items" [path]="getElementPath(pIndex)">
45-
<list-action-group (onMove)="moveElement(i, $event)" (onDelete)="deleteElement(i)" [canMove]="schema.sortable"></list-action-group>
46-
</object-field>
44+
<span *ngIf="shouldDisplayTemplate && values.get(item.index).keySeq().size != 0">
45+
<ng-template [ngTemplateOutlet]="customTemplate" [ngTemplateOutletContext]="{item: values.get(item.index)}"></ng-template>
46+
<a href="javascript:void(0)" (click)="item.isEditFormVisible = !item.isEditFormVisible">
47+
{{item.isEditFormVisible ? 'Hide Fields' : 'Show Fields'}}
48+
</a>
49+
</span>
50+
<span *ngIf="item.isEditFormVisible">
51+
<object-field [value]="values.get(item.index)" [schema]="schema.items" [path]="getElementPath(item.index)">
52+
<list-action-group (onMove)="moveElement(item.index, $event)" (onDelete)="deleteElement(item.index)" [canMove]="schema.sortable"></list-action-group>
53+
</object-field>
54+
</span>
4755
</div>
4856
</div>
4957
</div>

src/complex-list-field/complex-list-field.component.ts

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ import {
2727
OnInit,
2828
ChangeDetectionStrategy,
2929
SimpleChanges,
30-
ChangeDetectorRef
30+
ChangeDetectorRef,
31+
TemplateRef
3132
} from '@angular/core';
3233
import { List, Map, Set } from 'immutable';
3334

3435
import { AbstractListFieldComponent } from '../abstract-list-field';
3536
import { AppGlobalsService, JsonStoreService, DomUtilService, PathUtilService } from '../shared/services';
36-
import { LongListNavigatorConfig, JSONSchema } from '../shared/interfaces';
37+
import { LongListNavigatorConfig, JSONSchema, PaginatedItem } from '../shared/interfaces';
3738

3839
@Component({
3940
selector: 'complex-list-field',
@@ -49,7 +50,7 @@ export class ComplexListFieldComponent extends AbstractListFieldComponent implem
4950
@Input() schema: JSONSchema;
5051
@Input() path: Array<any>;
5152

52-
paginatedIndices: Array<number>;
53+
paginatedItems: Array<PaginatedItem>;
5354

5455
foundIndices: Array<number>;
5556
currentFound = 0;
@@ -68,12 +69,7 @@ export class ComplexListFieldComponent extends AbstractListFieldComponent implem
6869

6970
ngOnInit() {
7071
this.navigator = this.schema.longListNavigatorConfig;
71-
if (this.navigator) {
72-
this.paginatedIndices = this.getIndicesForPage(this.currentPage);
73-
} else {
74-
// set all indices as paginated indices if pagination is not enabled.
75-
this.paginatedIndices = this.values.keySeq().toArray();
76-
}
72+
this.paginatedItems = this.getItemsForPage(this.currentPage);
7773
}
7874

7975
ngOnChanges(changes: SimpleChanges) {
@@ -88,14 +84,10 @@ export class ComplexListFieldComponent extends AbstractListFieldComponent implem
8884
// change the page if a new element is added and it's not on the last page
8985
if (curSize > preSize && this.currentPage !== lastPage) {
9086
this.currentPage = lastPage;
91-
} else {
92-
this.paginatedIndices = this.getIndicesForPage(this.currentPage);
9387
}
94-
} else {
95-
this.paginatedIndices = this.values.keySeq().toArray();
9688
}
89+
this.paginatedItems = this.getItemsForPage(this.currentPage);
9790
}
98-
9991
}
10092
}
10193

@@ -150,23 +142,46 @@ export class ComplexListFieldComponent extends AbstractListFieldComponent implem
150142

151143
onPageChange(page: number) {
152144
this.currentPage = page;
153-
this.paginatedIndices = this.getIndicesForPage(page);
145+
this.paginatedItems = this.getItemsForPage(page);
154146
}
155147

156-
getIndicesForPage(page: number): Array<number> {
157-
let start = (page - 1) * this.navigator.itemsPerPage;
158-
let indices: Array<number> = Array.apply(0, Array(this.navigator.itemsPerPage))
159-
.map((el, index) => start + index);
160-
// check if the indices includes some numbers that are out of values index range.
161-
let lastIndexDiff = indices[indices.length - 1] - (this.values.size - 1);
162-
if (lastIndexDiff > 0) {
163-
indices.splice(indices.length - lastIndexDiff);
164-
}
165-
return indices;
166-
}
148+
getItemsForPage(page: number): Array<PaginatedItem> {
149+
let indices = this.getIndicesToDisplay(page);
167150

151+
return indices.map((index) => {
152+
let showEditForm = this.schema.viewTemplateConfig ? this.schema.viewTemplateConfig.showEditForm : undefined;
153+
let isEditFormVisible = !showEditForm || showEditForm(this.values.get(index));
154+
return {index, isEditFormVisible};
155+
});
156+
}
168157

169158
getPageForIndex(index: number): number {
170159
return Math.floor((index / this.navigator.itemsPerPage) + 1);
171160
}
161+
162+
get customTemplate(): TemplateRef<any> {
163+
return this.appGlobalsService.templates[this.schema.viewTemplateConfig.itemTemplateName];
164+
}
165+
166+
get shouldDisplayTemplate(): boolean {
167+
return this.schema.viewTemplateConfig !== undefined;
168+
}
169+
170+
private getIndicesToDisplay(page: number): Array<number> {
171+
let indices: Array<number>;
172+
if (this.navigator) {
173+
let start = (page - 1) * this.navigator.itemsPerPage;
174+
indices = Array.apply(0, Array(this.navigator.itemsPerPage))
175+
.map((el, index) => start + index);
176+
// check if the indices includes some numbers that are out of values index range.
177+
let lastIndexDiff = indices[indices.length - 1] - (this.values.size - 1);
178+
if (lastIndexDiff > 0) {
179+
indices.splice(indices.length - lastIndexDiff);
180+
}
181+
} else {
182+
indices = this.values.keySeq().toArray();
183+
}
184+
185+
return indices;
186+
}
172187
}

src/shared/interfaces/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ export { Tab } from './tab';
1919
export { JSONSchema } from './json-schema';
2020
export { CategorizedValidationErrors } from './categorized-validation-errors';
2121
export { ValidationError } from './validation-error';
22+
export { PaginatedItem } from './paginated-item';
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface PaginatedItem {
2+
index?: number;
3+
isEditFormVisible?: boolean;
4+
}

src/shared/interfaces/schema-option.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { AutocompletionConfig } from './autocompletion-config';
22
import { LongListNavigatorConfig } from './long-list-navigator-config';
3+
import { ViewTemplateConfig } from './view-template-config';
34
import { RefConfig } from './ref-config';
45
import { OnValueChangeFunction } from './on-value-change-function';
56

@@ -89,4 +90,9 @@ export interface SchemaOption {
8990
* - It is `false` by default.
9091
*/
9192
sortable?: boolean;
93+
94+
/**
95+
* Configuration for custom templates in complex list fields.
96+
*/
97+
viewTemplateConfig?: ViewTemplateConfig;
9298
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Map } from 'immutable';
2+
3+
export interface ViewTemplateConfig {
4+
/**
5+
* Used for complex list fields only (list of nested objects).
6+
* The template needs to be defined with <ng-template #myTemplate></ng-template> and passed to the json-editor like so:
7+
* <json-editor [templates]="{myTemplate: myTemplate}"></json-editor>
8+
*
9+
* - template parameter: `item: Immutable.Map<string, any>`
10+
*/
11+
itemTemplateName: string;
12+
/**
13+
* Used for complex list fields only (list of nested objects).
14+
* Function that receives an immutable Map representing the JSON document and returns:
15+
* - true if the fields to edit should be visible by default
16+
* - false if the fields to edit should be hidden by default
17+
*/
18+
showEditForm?: (item: Map<string, any>) => boolean;
19+
};

0 commit comments

Comments
 (0)