Decouple @enum and drop-down widget
Provide a convenience fields.linkedcollection model to handle common use-case of using @ref in a Mistral WB. Cover it with unit-tests as well all scenarios of using fields.linkedcollection in MIstral WB. Change-Id: I97a61262db4cc521b5c230667a49b99701318f3f Closes-Bug: #1467514
This commit is contained in:
parent
7ccf4f0dd3
commit
a5c1c308cf
@ -110,10 +110,10 @@
|
||||
base.on('change', function(operation) {
|
||||
var argsEntry, pos, entry;
|
||||
if ( operation != 'id' ) {
|
||||
pos = base._stdActions.getPosByID(base.get());
|
||||
pos = base._collection.getPosByID(base.get());
|
||||
if ( pos > -1 ) {
|
||||
entry = self.get('base-input');
|
||||
argsEntry = base._stdActions.get(pos);
|
||||
argsEntry = base._collection.get(pos);
|
||||
entry.resetKeys(argsEntry.toJSON());
|
||||
}
|
||||
}
|
||||
@ -122,44 +122,15 @@
|
||||
}
|
||||
}, {
|
||||
'base': {
|
||||
'@class': fields.string.extend({
|
||||
'@class': fields.linkedcollection.extend({
|
||||
create: function(json, parameters) {
|
||||
var self = fields.string.create.call(this, json, parameters),
|
||||
stdActionsCls = Barricade.create({
|
||||
'@type': String,
|
||||
'@ref': {
|
||||
to: function() {
|
||||
return fields.StandardActions;
|
||||
},
|
||||
needs: function() {
|
||||
return models.Root;
|
||||
},
|
||||
getter: function(data) {
|
||||
return data.needed.get('standardActions');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self._stdActions = stdActionsCls.create().on(
|
||||
'replace', function(newValue) {
|
||||
self._stdActions = newValue;
|
||||
self._stdActions.on('change', function() {
|
||||
self._choices = self._stdActions.getIDs();
|
||||
self.resetValues();
|
||||
});
|
||||
self._stdActions.emit('change');
|
||||
});
|
||||
|
||||
return self;
|
||||
},
|
||||
_choices: []
|
||||
parameters = Object.create(parameters);
|
||||
parameters.toCls = models.StandardActions;
|
||||
parameters.neededCls = models.Root;
|
||||
parameters.substitutedEntryID = 'standardActions';
|
||||
return fields.linkedcollection.create.call(this, json, parameters);
|
||||
}
|
||||
}, {
|
||||
'@enum': function() {
|
||||
if ( this._stdActions.isPlaceholder() ) {
|
||||
this.emit('_resolveUp', this._stdActions);
|
||||
}
|
||||
return this._choices;
|
||||
},
|
||||
'@meta': {
|
||||
'index': 1,
|
||||
'row': 0
|
||||
@ -402,43 +373,15 @@
|
||||
models.ActionTaskMixin = Barricade.Blueprint.create(function() {
|
||||
return this.extend({}, {
|
||||
'action': {
|
||||
'@class': fields.string.extend({
|
||||
'@class': fields.linkedcollection.extend({
|
||||
create: function(json, parameters) {
|
||||
var self = fields.string.create.call(this, json, parameters),
|
||||
actionsCls = Barricade.create({
|
||||
'@type': String,
|
||||
'@ref': {
|
||||
to: function() {
|
||||
return models.Actions;
|
||||
},
|
||||
needs: function() {
|
||||
return models.Workbook;
|
||||
},
|
||||
getter: function(data) {
|
||||
return data.needed.get('actions');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self._actions = actionsCls.create().on(
|
||||
'replace', function(newValue) {
|
||||
self._actions = newValue;
|
||||
self._actions.on('change', function() {
|
||||
self._choices = self._actions.getIDs();
|
||||
self.resetValues();
|
||||
});
|
||||
self._actions.emit('change');
|
||||
});
|
||||
return self;
|
||||
},
|
||||
_choices: []
|
||||
parameters = Object.create(parameters);
|
||||
parameters.toCls = models.Actions;
|
||||
parameters.neededCls = models.Workbook;
|
||||
parameters.substitutedEntryID = 'actions';
|
||||
return fields.linkedcollection.create.call(this, json, parameters);
|
||||
}
|
||||
}, {
|
||||
'@enum': function() {
|
||||
if ( this._actions.isPlaceholder() ) {
|
||||
this.emit('_resolveUp', this._actions);
|
||||
}
|
||||
return this._choices;
|
||||
},
|
||||
'@meta': {
|
||||
'row': 0,
|
||||
'index': 1
|
||||
@ -451,43 +394,15 @@
|
||||
models.WorkflowTaskMixin = Barricade.Blueprint.create(function() {
|
||||
return this.extend({}, {
|
||||
'workflow': {
|
||||
'@class': fields.string.extend({
|
||||
'@class': fields.linkedcollection.extend({
|
||||
create: function(json, parameters) {
|
||||
var self = fields.string.create.call(this, json, parameters),
|
||||
workflowsCls = Barricade.create({
|
||||
'@type': String,
|
||||
'@ref': {
|
||||
to: function() {
|
||||
return models.Workflows;
|
||||
},
|
||||
needs: function() {
|
||||
return models.Workbook;
|
||||
},
|
||||
getter: function(data) {
|
||||
return data.needed.get('workflows');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self._workflows = workflowsCls.create().on(
|
||||
'replace', function(newValue) {
|
||||
self._workflows = newValue;
|
||||
self._workflows.on('change', function() {
|
||||
self._choices = self._workflows.getIDs();
|
||||
self.resetValues();
|
||||
});
|
||||
self._workflows.emit('change');
|
||||
});
|
||||
return self;
|
||||
},
|
||||
_choices: []
|
||||
parameters = Object.create(parameters);
|
||||
parameters.toCls = models.Workflows;
|
||||
parameters.neededCls = models.Workbook;
|
||||
parameters.substitutedEntryID = 'workflows';
|
||||
return fields.linkedcollection.create.call(this, json, parameters);
|
||||
}
|
||||
}, {
|
||||
'@enum': function() {
|
||||
if ( this._workflows.isPlaceholder() ) {
|
||||
this.emit('_resolveUp', this._workflows);
|
||||
}
|
||||
return this._choices;
|
||||
},
|
||||
'@meta': {
|
||||
'row': 0,
|
||||
'index': 1
|
||||
|
143
extensions/mistral/test/js/workbook.controller.spec.js
Normal file
143
extensions/mistral/test/js/workbook.controller.spec.js
Normal file
@ -0,0 +1,143 @@
|
||||
|
||||
/* Copyright (c) 2015 Mirantis, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
describe('together workbook model and controller', function() {
|
||||
var models, utils, workbook;
|
||||
|
||||
beforeEach(function () {
|
||||
module('mistral');
|
||||
inject(function ($injector) {
|
||||
models = $injector.get('mistral.workbook.models');
|
||||
utils = $injector.get('merlin.utils');
|
||||
});
|
||||
workbook = models.Workbook.create();
|
||||
});
|
||||
|
||||
|
||||
describe('define top-level actions available to user:', function () {
|
||||
var $scope;
|
||||
|
||||
beforeEach(inject(function (_$controller_) {
|
||||
var $controller = _$controller_;
|
||||
$scope = {};
|
||||
$controller('workbookCtrl', {$scope: $scope});
|
||||
$scope.workbook = workbook;
|
||||
}));
|
||||
|
||||
describe("'Add Action' action", function () {
|
||||
it('adds a new Action', function () {
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.get('actions').get(0)).toBeDefined();
|
||||
});
|
||||
|
||||
it('creates action with predefined name', function () {
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.get('actions').get(0).getID()).toBeGreaterThan('');
|
||||
});
|
||||
|
||||
describe('', function () {
|
||||
var actionID;
|
||||
beforeEach(inject(function (baseActionID) {
|
||||
actionID = baseActionID + '1';
|
||||
}));
|
||||
|
||||
it("corresponding JSON has the right key for the Action", function () {
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeDefined();
|
||||
});
|
||||
|
||||
it("once the Action ID is changed, it's reflected in JSON", function () {
|
||||
var newID = 'action10';
|
||||
|
||||
$scope.addAction();
|
||||
workbook.get('actions').getByID(actionID).setID(newID);
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeUndefined();
|
||||
expect(workbook.toJSON({pretty: true}).actions[newID]).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('creates actions with different names on 2 successive calls', function () {
|
||||
$scope.addAction();
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.get('actions').get(0).getID()).not.toEqual(
|
||||
workbook.get('actions').get(1).getID())
|
||||
});
|
||||
});
|
||||
|
||||
describe("'Add Workflow' action", function () {
|
||||
it('adds a new Workflow', function () {
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.get('workflows').get(0)).toBeDefined();
|
||||
});
|
||||
|
||||
describe('', function () {
|
||||
var workflowID;
|
||||
beforeEach(inject(function (baseWorkflowID) {
|
||||
workflowID = baseWorkflowID + '1';
|
||||
}));
|
||||
|
||||
it("corresponding JSON has the right key for the Workflow", function () {
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeDefined();
|
||||
});
|
||||
|
||||
it("once the workflow ID is changed, it's reflected in JSON", function () {
|
||||
var newID = 'workflow10';
|
||||
|
||||
$scope.addWorkflow();
|
||||
workbook.get('workflows').getByID(workflowID).setID(newID);
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeUndefined();
|
||||
expect(workbook.toJSON({pretty: true}).workflows[newID]).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('creates workflow with predefined name', function () {
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.get('workflows').get(0).getID()).toBeGreaterThan('');
|
||||
});
|
||||
|
||||
it('creates workflows with different names on 2 successive calls', function () {
|
||||
$scope.addWorkflow();
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.get('workflows').get(0).getID()).not.toEqual(
|
||||
workbook.get('workflows').get(1).getID())
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("'Create'/'Modify'/'Cancel' actions", function () {
|
||||
it('edit causes a request to an api and a return to main page', function () {
|
||||
|
||||
});
|
||||
|
||||
it('cancel causes just a return to main page', function () {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
});
|
@ -31,6 +31,37 @@ describe('workbook model logic', function() {
|
||||
return workbook.get('workflows').getByID(workflowID);
|
||||
}
|
||||
|
||||
describe('defines the standard actions getter for Action->Base field:', function() {
|
||||
var root, action1;
|
||||
|
||||
beforeEach(function() {
|
||||
root = models.Root.create();
|
||||
root.set('workbook', workbook);
|
||||
root.set('standardActions', {
|
||||
'nova.create_server': ['image', 'flavor', 'network_id'],
|
||||
'neutron.create_network': ['name', 'create_subnet'],
|
||||
'glance.create_image': ['image_url']
|
||||
});
|
||||
workbook.get('actions').add('action1');
|
||||
action1 = workbook.get('actions').getByID('action1');
|
||||
});
|
||||
|
||||
it('all actions are present as choices for the Base field', function() {
|
||||
var availableActions = action1.get('base').getValues();
|
||||
|
||||
expect(availableActions).toEqual([
|
||||
'nova.create_server', 'neutron.create_network', 'glance.create_image']);
|
||||
});
|
||||
|
||||
it("'Base Input' field is set to have keys corresponding to 'Base' field value", function() {
|
||||
action1.get('base').set('nova.create_server');
|
||||
expect(action1.get('base-input').getIDs()).toEqual(['image', 'flavor', 'network_id']);
|
||||
|
||||
action1.get('base').set('neutron.create_network');
|
||||
expect(action1.get('base-input').getIDs()).toEqual(['name', 'create_subnet']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('defines workflow structure transformations:', function() {
|
||||
var workflowID = 'workflow1';
|
||||
|
||||
@ -71,7 +102,7 @@ describe('workbook model logic', function() {
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
workbook.get('workflows').push({name: 'Workflow 1'}, {id: workflowID});
|
||||
workbook.get('workflows').add(workflowID);
|
||||
});
|
||||
|
||||
describe('', function() {
|
||||
@ -120,11 +151,30 @@ describe('workbook model logic', function() {
|
||||
expect(getTask(taskID).instanceof(models.DirectWFTask)).toBe(true);
|
||||
});
|
||||
|
||||
it("changing task type from 'action' to 'workflow' causes proper structure changes", function() {
|
||||
getTask(taskID).get('type').set('workflow');
|
||||
it("'action'-based task offers available custom actions for its Action field", function() {
|
||||
workbook.get('actions').add('action1');
|
||||
expect(getTask(taskID).get('action').getValues()).toEqual(['action1']);
|
||||
|
||||
expect(getTask(taskID).instanceof(models.WorkflowTaskMixin)).toBe(true);
|
||||
expect(getTask(taskID).instanceof(models.DirectWFTask)).toBe(true);
|
||||
workbook.get('actions').add('action2');
|
||||
expect(getTask(taskID).get('action').getValues()).toEqual(['action1', 'action2']);
|
||||
});
|
||||
|
||||
describe("changing task type from 'action' to 'workflow' causes", function() {
|
||||
beforeEach(function() {
|
||||
getTask(taskID).get('type').set('workflow');
|
||||
});
|
||||
|
||||
it('proper structure changes', function() {
|
||||
expect(getTask(taskID).instanceof(models.WorkflowTaskMixin)).toBe(true);
|
||||
expect(getTask(taskID).instanceof(models.DirectWFTask)).toBe(true);
|
||||
});
|
||||
|
||||
it('and causes the Workflow field to suggest available workflows as choices', function() {
|
||||
expect(getTask(taskID).get('workflow').getValues()).toEqual(['workflow1']);
|
||||
|
||||
workbook.get('workflows').add('workflow2');
|
||||
expect(getTask(taskID).get('workflow').getValues()).toEqual([workflowID, 'workflow2']);
|
||||
});
|
||||
});
|
||||
|
||||
it("changing workflow type to 'reverse' causes the proper changes to its tasks", function() {
|
||||
@ -168,11 +218,30 @@ describe('workbook model logic', function() {
|
||||
expect(getTask(taskID).instanceof(models.ReverseWFTask)).toBe(true);
|
||||
});
|
||||
|
||||
it("changing task type from 'action' to 'workflow' causes proper structure changes", function() {
|
||||
getTask(taskID).get('type').set('workflow');
|
||||
it("'action'-based task offers available custom actions for its Action field", function() {
|
||||
workbook.get('actions').add('action1');
|
||||
expect(getTask(taskID).get('action').getValues()).toEqual(['action1']);
|
||||
|
||||
expect(getTask(taskID).instanceof(models.WorkflowTaskMixin)).toBe(true);
|
||||
expect(getTask(taskID).instanceof(models.ReverseWFTask)).toBe(true);
|
||||
workbook.get('actions').add('action2');
|
||||
expect(getTask(taskID).get('action').getValues()).toEqual(['action1', 'action2']);
|
||||
});
|
||||
|
||||
describe("changing task type from 'action' to 'workflow' causes", function() {
|
||||
beforeEach(function() {
|
||||
getTask(taskID).get('type').set('workflow');
|
||||
});
|
||||
|
||||
it('proper structure changes', function() {
|
||||
expect(getTask(taskID).instanceof(models.WorkflowTaskMixin)).toBe(true);
|
||||
expect(getTask(taskID).instanceof(models.ReverseWFTask)).toBe(true);
|
||||
});
|
||||
|
||||
it('and causes the Workflow field to suggest available workflows as choices', function() {
|
||||
expect(getTask(taskID).get('workflow').getValues()).toEqual(['workflow1']);
|
||||
|
||||
workbook.get('workflows').add('workflow2');
|
||||
expect(getTask(taskID).get('workflow').getValues()).toEqual([workflowID, 'workflow2']);
|
||||
});
|
||||
});
|
||||
|
||||
it("changing workflow type to 'direct' causes the proper changes to its tasks", function() {
|
||||
@ -202,118 +271,4 @@ describe('workbook model logic', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('defines top-level actions available to user:', function() {
|
||||
var $scope;
|
||||
|
||||
beforeEach(inject(function(_$controller_) {
|
||||
var $controller = _$controller_;
|
||||
$scope = {};
|
||||
$controller('workbookCtrl', {$scope: $scope});
|
||||
$scope.workbook = workbook;
|
||||
}));
|
||||
|
||||
describe("'Add Action' action", function() {
|
||||
it('adds a new Action', function() {
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.get('actions').get(0)).toBeDefined();
|
||||
});
|
||||
|
||||
it('creates action with predefined name', function() {
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.get('actions').get(0).getID()).toBeGreaterThan('');
|
||||
});
|
||||
|
||||
describe('', function() {
|
||||
var actionID;
|
||||
beforeEach(inject(function(baseActionID) {
|
||||
actionID = baseActionID + '1';
|
||||
}));
|
||||
|
||||
it("corresponding JSON has the right key for the Action", function() {
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeDefined();
|
||||
});
|
||||
|
||||
it("once the Action ID is changed, it's reflected in JSON", function() {
|
||||
var newID = 'action10';
|
||||
|
||||
$scope.addAction();
|
||||
workbook.get('actions').getByID(actionID).setID(newID);
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).actions[actionID]).toBeUndefined();
|
||||
expect(workbook.toJSON({pretty: true}).actions[newID]).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('creates actions with different names on 2 successive calls', function() {
|
||||
$scope.addAction();
|
||||
$scope.addAction();
|
||||
|
||||
expect(workbook.get('actions').get(0).getID()).not.toEqual(
|
||||
workbook.get('actions').get(1).getID())
|
||||
});
|
||||
});
|
||||
|
||||
describe("'Add Workflow' action", function() {
|
||||
it('adds a new Workflow', function() {
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.get('workflows').get(0)).toBeDefined();
|
||||
});
|
||||
|
||||
describe('', function() {
|
||||
var workflowID;
|
||||
beforeEach(inject(function(baseWorkflowID) {
|
||||
workflowID = baseWorkflowID + '1';
|
||||
}));
|
||||
|
||||
it("corresponding JSON has the right key for the Workflow", function() {
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeDefined();
|
||||
});
|
||||
|
||||
it("once the workflow ID is changed, it's reflected in JSON", function() {
|
||||
var newID = 'workflow10';
|
||||
|
||||
$scope.addWorkflow();
|
||||
workbook.get('workflows').getByID(workflowID).setID(newID);
|
||||
|
||||
expect(workbook.toJSON({pretty: true}).workflows[workflowID]).toBeUndefined();
|
||||
expect(workbook.toJSON({pretty: true}).workflows[newID]).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('creates workflow with predefined name', function() {
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.get('workflows').get(0).getID()).toBeGreaterThan('');
|
||||
});
|
||||
|
||||
it('creates workflows with different names on 2 successive calls', function() {
|
||||
$scope.addWorkflow();
|
||||
$scope.addWorkflow();
|
||||
|
||||
expect(workbook.get('workflows').get(0).getID()).not.toEqual(
|
||||
workbook.get('workflows').get(1).getID())
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("'Create'/'Modify'/'Cancel' actions", function() {
|
||||
it('edit causes a request to an api and a return to main page', function() {
|
||||
|
||||
});
|
||||
|
||||
it('cancel causes just a return to main page', function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
|
@ -52,7 +52,8 @@ module.exports = function (config) {
|
||||
// explicitly require first module definition file to avoid errors
|
||||
'extensions/mistral/static/mistral/js/mistral.init.js',
|
||||
'extensions/mistral/static/mistral/js/mistral.*.js',
|
||||
'extensions/mistral/test/js/*Spec.js'
|
||||
'extensions/mistral/test/js/*Spec.js',
|
||||
'extensions/mistral/test/js/*spec.js'
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
|
@ -134,18 +134,6 @@
|
||||
})
|
||||
.directive('typedField', ['$compile', 'merlin.templates',
|
||||
function($compile, templates) {
|
||||
function updateAutoCompletionDirective(template) {
|
||||
template.find('input').each(function(index, elem) {
|
||||
elem = angular.element(elem);
|
||||
if ( elem.attr('autocompletable') ) {
|
||||
// process 'autocompletable' attribute only once
|
||||
elem.removeAttr('autocompletable');
|
||||
elem.attr('typeahead-editable', true);
|
||||
elem.attr('typeahead',
|
||||
"option for option in value.getSuggestions() | filter:$viewValue");
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
@ -154,10 +142,6 @@
|
||||
},
|
||||
link: function(scope, element) {
|
||||
templates.templateReady(scope.type).then(function(template) {
|
||||
template = angular.element(template);
|
||||
if ( scope.value.getSuggestions ) {
|
||||
updateAutoCompletionDirective(template);
|
||||
}
|
||||
element.replaceWith($compile(template)(scope));
|
||||
})
|
||||
}
|
||||
|
@ -10,9 +10,10 @@
|
||||
return this;
|
||||
});
|
||||
|
||||
var restrictedChoicesMixin = Barricade.Blueprint.create(function() {
|
||||
var viewChoicesMixin = Barricade.Blueprint.create(function() {
|
||||
var self = this,
|
||||
values, labels, items;
|
||||
dropDownLimit = this._dropDownLimit || 5,
|
||||
values, labels, items, isDropDown;
|
||||
|
||||
function fillItems() {
|
||||
values = self.getEnumValues();
|
||||
@ -42,6 +43,15 @@
|
||||
values = undefined;
|
||||
};
|
||||
|
||||
this.isDropDown = function() {
|
||||
// what starts its life as being dropdown / not being dropdown
|
||||
// should remain so forever
|
||||
if ( angular.isUndefined(isDropDown) ) {
|
||||
isDropDown = !this.isEmpty() && this.getValues().length < dropDownLimit;
|
||||
}
|
||||
return isDropDown;
|
||||
};
|
||||
|
||||
this.setType('choices');
|
||||
return this;
|
||||
});
|
||||
@ -92,11 +102,7 @@
|
||||
};
|
||||
wildcardMixin.call(this);
|
||||
if ( this.getEnumValues ) {
|
||||
restrictedChoicesMixin.call(this);
|
||||
}
|
||||
var autocompletionUrl = utils.getMeta(this, 'autocompletionUrl');
|
||||
if ( autocompletionUrl ) {
|
||||
autoCompletionMixin.call(this, autocompletionUrl);
|
||||
viewChoicesMixin.call(this);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
@ -114,19 +120,6 @@
|
||||
}
|
||||
}, {'@type': String});
|
||||
|
||||
var autoCompletionMixin = Barricade.Blueprint.create(function(url) {
|
||||
var self = this;
|
||||
|
||||
this.getSuggestions = function() { return []; };
|
||||
$http.get(url).success(function(data) {
|
||||
self.getSuggestions = function() {
|
||||
return data;
|
||||
};
|
||||
});
|
||||
|
||||
return this;
|
||||
});
|
||||
|
||||
var textModel = Barricade.Primitive.extend({
|
||||
create: function(json, parameters) {
|
||||
var self = Barricade.Primitive.create.call(this, json, parameters);
|
||||
@ -256,14 +249,55 @@
|
||||
}
|
||||
}, {'@type': Object});
|
||||
|
||||
var linkedCollectionModel = stringModel.extend({
|
||||
create: function(json, parameters) {
|
||||
var self = stringModel.create.call(this, json, parameters),
|
||||
collectionCls = Barricade.create({
|
||||
'@type': String,
|
||||
'@ref': {
|
||||
to: function() {
|
||||
return parameters.toCls;
|
||||
},
|
||||
needs: function() {
|
||||
return parameters.neededCls;
|
||||
},
|
||||
getter: function(data) {
|
||||
return data.needed.get(parameters.substitutedEntryID);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self._collection = collectionCls.create().on(
|
||||
'replace', function(newValue) {
|
||||
self._collection = newValue;
|
||||
self._collection.on('change', function() {
|
||||
self._choices = self._collection.getIDs();
|
||||
self.resetValues();
|
||||
});
|
||||
self._collection.emit('change');
|
||||
});
|
||||
|
||||
return self;
|
||||
},
|
||||
_choices: []
|
||||
}, {
|
||||
'@enum': function() {
|
||||
if ( this._collection.isPlaceholder() ) {
|
||||
this.emit('_resolveUp', this._collection);
|
||||
}
|
||||
return this._choices;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
string: stringModel,
|
||||
text: textModel,
|
||||
number: numberModel,
|
||||
list: listModel,
|
||||
linkedcollection: linkedCollectionModel,
|
||||
dictionary: dictionaryModel,
|
||||
frozendict: frozendictModel,
|
||||
autocompletionmixin: autoCompletionMixin,
|
||||
wildcard: wildcardMixin // use for most general type-checks
|
||||
};
|
||||
}])
|
||||
|
@ -1,9 +1,16 @@
|
||||
<div class="form-group">
|
||||
<label for="elem-{$ $id $}.$index">{$ value.title() $}</label>
|
||||
<select id="elem-{$ $id $}.$index" class="form-control"
|
||||
<label for="elem-{$ $id $}">{$ value.title() $}</label>
|
||||
<select ng-if="value.isDropDown()"
|
||||
id="elem-{$ $id $}" class="form-control"
|
||||
ng-model="value.value" ng-model-options="{getterSetter: true}">
|
||||
<option ng-repeat="option in value.getValues()"
|
||||
value="{$ option $}"
|
||||
ng-selected="value.get() == option">{$ value.getLabel(option) $}</option>
|
||||
</select>
|
||||
<input ng-if="!value.isDropDown()"
|
||||
type="text" class="form-control" id="elem-{$ $id $}"
|
||||
ng-model="value.value" ng-model-options="{ getterSetter: true }"
|
||||
validatable-with="value" typeahead-editable="true"
|
||||
typeahead="option for option in value.getValues() | filter:$viewValue">
|
||||
<div ng-show="error" class="alert alert-danger">{$ error $}</div>
|
||||
</div>
|
||||
|
@ -3,6 +3,6 @@
|
||||
<label for="elem-{$ $id $}">{$ value.title() $}</label>
|
||||
<input type="number" class="form-control" id="elem-{$ $id $}"
|
||||
ng-model="value.value" ng-model-options="{ getterSetter: true }"
|
||||
autocompletable="true" validatable-with="value">
|
||||
validatable-with="value">
|
||||
<div ng-show="error" class="alert alert-danger">{$ error $}</div>
|
||||
</div>
|
||||
|
@ -2,6 +2,6 @@
|
||||
<label for="elem-{$ $id $}">{$ value.title() $}</label>
|
||||
<input type="text" class="form-control" id="elem-{$ $id $}"
|
||||
ng-model="value.value" ng-model-options="{ getterSetter: true }"
|
||||
autocompletable="true" validatable-with="value">
|
||||
validatable-with="value">
|
||||
<div ng-show="error" class="alert alert-danger">{$ error $}</div>
|
||||
</div>
|
||||
|
@ -2,6 +2,6 @@
|
||||
<label for="elem-{$ $id $}">{$ value.title() $}</label>
|
||||
<textarea class="form-control" id="elem-{$ $id $}"
|
||||
ng-model="value.value" ng-model-options="{ getterSetter: true }"
|
||||
autocompletable="true" validatable-with="value"></textarea>
|
||||
validatable-with="value"></textarea>
|
||||
<div ng-show="error" class="alert alert-danger">{$ error $}</div>
|
||||
</div>
|
||||
|
@ -141,4 +141,65 @@ describe('merlin models:', function() {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('linkedCollection field', function() {
|
||||
var collectionCls, linkedObjCls, linkedObj, lnkField;
|
||||
|
||||
beforeEach(function() {
|
||||
collectionCls = fields.dictionary.extend({}, {
|
||||
'?': {
|
||||
'@class': fields.string
|
||||
}
|
||||
});
|
||||
linkedObjCls = fields.frozendict.extend({}, {
|
||||
'realCollection': {
|
||||
'@class': collectionCls
|
||||
},
|
||||
'linkedField': {
|
||||
'@class': fields.linkedcollection.extend({
|
||||
create: function(json, parameters) {
|
||||
parameters = Object.create(parameters);
|
||||
parameters.toCls = collectionCls;
|
||||
parameters.neededCls = linkedObjCls;
|
||||
parameters.substitutedEntryID = 'realCollection';
|
||||
return fields.linkedcollection.create.call(this, json, parameters);
|
||||
},
|
||||
_dropDownLimit: 4
|
||||
})
|
||||
}
|
||||
});
|
||||
linkedObj = linkedObjCls.create({'realCollection': {'a': '', 'b': ''}});
|
||||
lnkField = linkedObj.get('linkedField');
|
||||
});
|
||||
|
||||
it('provides access from @enum values of one field to IDs of another one', function() {
|
||||
expect(lnkField.getValues()).toEqual(['a', 'b']);
|
||||
|
||||
linkedObj.get('realCollection').add('c');
|
||||
expect(lnkField.getValues()).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
describe('and exposes _collection attribute', function() {
|
||||
it('in case more complex things need to be done', function() {
|
||||
expect(lnkField._collection).toBeDefined();
|
||||
});
|
||||
|
||||
it("which is truly initialized after first @enum's .getValues() call", function() {
|
||||
expect(lnkField._collection.isPlaceholder()).toBe(true);
|
||||
|
||||
lnkField.getValues();
|
||||
expect(lnkField._collection.isPlaceholder()).toBe(false);
|
||||
expect(lnkField._collection).toBe(linkedObj.get('realCollection'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('exposes .isDropDown() call due to @enum presense', function() {
|
||||
it('which always returns false due to deferred nature of linkedField', function() {
|
||||
expect(lnkField.isDropDown()).toBe(false);
|
||||
|
||||
lnkField.getValues();
|
||||
expect(lnkField.isDropDown()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user