Skip to content

Commit

Permalink
Merge pull request #137 from eneufeld/feat_masterdetail
Browse files Browse the repository at this point in the history
MasterDetail Renderer
  • Loading branch information
edgarmueller committed Nov 12, 2015
2 parents a49f3a4 + a74b65d commit 9f39bd8
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 65 deletions.
1 change: 1 addition & 0 deletions components/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ angular.module('jsonforms', [
'jsonforms.renderers.layouts.categories.categorization',
'jsonforms.renderers.layouts.categories.category',
'jsonforms.renderers.extras.label',
'jsonforms.renderers.layouts.masterdetail',
]);
1 change: 1 addition & 0 deletions components/form/form-directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ angular.module('jsonforms.form').directive('jsonforms', ():ng.IDirective => {
link: (scope, el, attrs, ctrl) => {
scope['element'] = el;
scope.$watch('uiSchema', () => { ctrl.init(); });
scope.$watch('data', () => { ctrl.init(); });
}
}
});
44 changes: 44 additions & 0 deletions components/renderers/layouts/masterdetail/masterdetail-renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
///<reference path="../../../references.ts"/>

class MasterDetailRenderer implements JSONForms.IRenderer {

priority = 1;

constructor(private renderService: JSONForms.IRenderService) { }

render(element: IUISchemaElement, subSchema: SchemaElement, schemaPath: string, services: JSONForms.Services): JSONForms.IContainerRenderDescription {
var control = JSONForms.RenderDescriptionFactory.createControlDescription(schemaPath, services, "");
var template = `
<div class="row">
<!-- Master -->
<div class="col-sm-30">
<masterdetail-collection element="element" collection="element.schema.properties"></masterdetail-collection>
</div>
<!-- Detail -->
<div class="col-sm-70">
<jsonforms schema="element.selectedSchema" data="element.selectedChild" ng-if="element.selectedChild"></jsonforms>
</div>
</div>
`;
control['template'] = template;
control['schema']=subSchema;
control['filter']=(properties) => {
var result = {};
angular.forEach(properties, (value, key) => {
if (value.type=='array' && value.items.type=='object') {
result[key] = value;
}
});
return result;
}
return control;
}

isApplicable(uiElement: IUISchemaElement, jsonSchema: SchemaElement, schemaPath): boolean {
return uiElement.type == "MasterDetailLayout" && jsonSchema !== undefined && jsonSchema.type == 'object';
}
}

angular.module('jsonforms.renderers.layouts.masterdetail').run(['RenderService', (RenderService) => {
RenderService.register(new MasterDetailRenderer(RenderService));
}]);
50 changes: 50 additions & 0 deletions components/renderers/layouts/masterdetail/masterdetail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
///<reference path="../../../references.ts"/>

angular.module('jsonforms.renderers.layouts.masterdetail', ['jsonforms.renderers.layouts']);
angular.module('jsonforms.renderers.layouts.masterdetail')
.directive('masterdetailCollection', ():ng.IDirective => {
return {
restrict: "E",
replace: true,
scope: {
collection: '=',
element:'='
},
template:
`
<div>
<accordion close-others="false">
<accordion-group is-open="status_attribute.open" ng-repeat="(key, value) in element.filter(collection)">
<accordion-heading>
{{key}} <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status_attribute.open, 'glyphicon-chevron-right': !status_attribute.open}"></i>
</accordion-heading>
<accordion close-others="false">
<accordion-group is-open="status_object.open" ng-repeat="child in element.instance[key]">
<accordion-heading>
<span ng-click="element.selectedChild=child;element.selectedSchema=value.items;">{{child.name}}<!-- label provider needed --></span><i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status_object.open, 'glyphicon-chevron-right': !status_object.open}"></i>
</accordion-heading>
<masterdetail-member element="element" member="value.items"></masterdetail-member>
</accordion-group>
</accordion>
</accordion-group>
</accordion>
</div>
`
}
})
.directive('masterdetailMember', ($compile):ng.IDirective => {
return {
restrict: "E",
replace: true,
scope: {
member: '=',
element:'='
},
link: (scope, element, attrs) => {
$compile('<masterdetail-collection collection="member.properties" element="element"></masterdetail-collection>')
(scope, (cloned, scope) => {
element.replaceWith(cloned);
});
}
}
});
132 changes: 67 additions & 65 deletions examples/js/LocalController.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ angular.module('makeithappen').controller('LocalController', ['$scope', function
},
"occupation": {
"type": "string"
},
"test_wrong": {
"type": "array",
"items": {"type":"string"}
},
"test_correct": {
"type": "array",
"items": {"type":"object","properties": {"name": {"type": "string"}}}
}
},
"required": ["occupation"]
Expand Down Expand Up @@ -181,32 +189,23 @@ angular.module('makeithappen').controller('LocalController', ['$scope', function
};

$scope.usersSchema = {
"type": "array",
"items": $scope.schema
"type": "object",
"properties": {
"users":{
"type": "array",
"items": $scope.schema
},
"enemies":{
"type": "array",
"items": $scope.schema
}
}
};
$scope.usersUiSchema = {
"type": "Control",
"type":"MasterDetailLayout",
"scope": {
"$ref": "#"
},
"columns": [
{
"label": "Name",
"scope": {
"$ref": "#/items/properties/name"
}
},
{
"label": "Age",
"scope": {
"$ref": "#/items/properties/personalData/properties/age"
}
}
],
"options": {
"enableFiltering": true,
"paginationPageSizes": [5, 10, 20]
}
};

$scope.data = {
Expand All @@ -215,51 +214,54 @@ angular.module('makeithappen').controller('LocalController', ['$scope', function
birthDate: "02.06.1985",
nationality: "US"
};
$scope.users = [
$scope.data,
{
name: 'Todd',
age: 33
},
{
name: 'Jimmy',
age: 34
},
{
name: 'Max',
age: 35
},
{
name: 'Jonas',
age: 34
},
{
name: 'Edgar',
age: 30
},
{
name: 'Eugen',
age: 28
},
{
name: 'Johannes',
age: 26
},
{
name: 'Alex',
age: 25
},
{
name: 'Stefan',
age: 27
},
{
name: 'Eva',
age: 30
}
];
$scope.users ={
"users":
[
$scope.data,
{
name: 'Todd',
personalData:{age: 33}
},
{
name: 'Jimmy',
personalData:{age: 32}
},
{
name: 'Max',
personalData:{age: 35}
},
{
name: 'Jonas',
personalData:{age: 34}
},
{
name: 'Edgar',
personalData:{age: 30}
},
{
name: 'Eugen',
personalData:{age: 29}
},
{
name: 'Johannes',
personalData:{age: 26}
},
{
name: 'Alex',
personalData:{age: 25}
},
{
name: 'Stefan',
personalData:{age: 27}
},
{
name: 'Eva',
personalData:{age: 30}
}
]
};

$scope.formattedData = function() {
return JSON.stringify($scope.data, null, 4);
return JSON.stringify($scope.users, null, 4);
};
}]);
80 changes: 80 additions & 0 deletions tests/unit-tests/MasterDetailTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/// <reference path="../references.ts"/>

describe('MasterDetail', () => {

// load all necessary modules and templates
beforeEach(module('jsonforms.form'));
beforeEach(module('jsonforms.renderers.layouts.masterdetail'));
beforeEach(module('jsonforms.renderers.controls.string'));

beforeEach(module('components/form/form.html'));
beforeEach(module('components/renderers/layouts/layout.html'));
beforeEach(module('components/renderers/controls/control.html'));

it("should be rendered", inject(($rootScope, $compile) => {
var scope = $rootScope.$new();
scope.schema = {
"type": "object",
"properties": {
"a":{
"type": "array",
"items": {
"type":"object",
"properties":{
"name": {
"type": "string"
},
}
},
},
"c":{
"type": "array",
"items": {
"type":"object",
"properties":{
"name": {
"type": "string"
},
}
}
},
}
};
scope.uiSchema = {
"type":"MasterDetailLayout",
"scope": {
"$ref": "#"
},
};
scope.data = {
"a":
[
{"name":"x_1"},
{"name":"x_2"}
],
"c":[
{"name":"y_1"},
{"name":"y_2"}
]
};
var el = $compile('<jsonforms schema="schema" ui-schema="uiSchema" data="data"/>')(scope);
scope.$digest();
expect(el.html()).toContain("<!-- Master -->"); //this is not resolved completly
expect(el.html()).toContain("<!-- Detail -->"); //this is not resolved completly
expect(el.html()).toContain('a');
expect(el.html()).toContain('x_1');
expect(el.html()).toContain('x_2');
expect(el.html()).toContain('c');
expect(el.html()).toContain('y_1');
expect(el.html()).toContain('y_2');

var nameInput_empty = el[0].querySelector("#\\#\\/properties\\/name");
expect(nameInput_empty).toBeNull();

var x1 = el[0].querySelector("accordion accordion accordion-heading span");
angular.element(x1).triggerHandler("click");
expect(el.html()).toContain("<label");
var nameInput = el[0].querySelector("#\\#\\/properties\\/name");
expect(nameInput).not.toBeNull();
}));
});

0 comments on commit 9f39bd8

Please sign in to comment.