mirror of
https://github.com/nasa/openmct.git
synced 2024-12-23 15:02:23 +00:00
[Plots] #638 added onchange handling in order to synchronize forms with domain object model.
Fixed failing test Added tests jslint errors Minor refactoring of layout bundle revert layout/bundle.json
This commit is contained in:
parent
f2903f4030
commit
abf5f22155
@ -295,7 +295,7 @@ define([
|
||||
"provides": "typeService",
|
||||
"type": "decorator",
|
||||
"implementation": TypeRegionDecorator
|
||||
},
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ define(
|
||||
}
|
||||
};
|
||||
this.addPart(metadataPart, 0);
|
||||
}
|
||||
};
|
||||
|
||||
return InspectorRegion;
|
||||
}
|
||||
|
@ -29,16 +29,17 @@ define(
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Adds default screen regions to Type definitions. Screen regions
|
||||
* are sections of the browse and edit view of an object that can be
|
||||
* customized on a per-type basis. Within {@link Region}s are {@link RegionPart}s.
|
||||
* Policies can be used to decide which parts are visible or not based on object state.
|
||||
* Adds default browse screen regions to Type definitions. Screen
|
||||
* regions are sections of the browse and edit view of an object
|
||||
* that can be customized on a per-type basis. Within
|
||||
* {@link Region}s are {@link RegionPart}s. Policies can be used to
|
||||
* decide which parts are visible or not based on object state.
|
||||
* @memberOf platform/commonUI/regions
|
||||
* @see {@link Region}, {@link RegionPart}, {@link EditableRegionPolicy}
|
||||
* @constructor
|
||||
*/
|
||||
function TypeRegionDecorator(typeService) {
|
||||
this.typeService = typeService
|
||||
this.typeService = typeService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,11 +33,11 @@ define(
|
||||
var inspectorRegion;
|
||||
|
||||
beforeEach(function () {
|
||||
inspectorRegion = new InspectorRegion;
|
||||
inspectorRegion = new InspectorRegion();
|
||||
});
|
||||
|
||||
it("creates default region parts", function () {
|
||||
expect(inspectorRegion.parts().length).toBe(2);
|
||||
expect(inspectorRegion.parts.length).toBe(1);
|
||||
});
|
||||
|
||||
});
|
70
platform/commonUI/browse/test/TypeRegionDecoratorSpec.js
Normal file
70
platform/commonUI/browse/test/TypeRegionDecoratorSpec.js
Normal file
@ -0,0 +1,70 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is 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.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
/**
|
||||
* MCTIncudeSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/TypeRegionDecorator"],
|
||||
function (TypeRegionDecorator) {
|
||||
"use strict";
|
||||
|
||||
describe("The type region decorator", function () {
|
||||
var typeRegionDecorator,
|
||||
mockTypeService,
|
||||
mockType,
|
||||
mockTypeDefinition;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTypeDefinition = {};
|
||||
|
||||
mockType = jasmine.createSpyObj('type', [
|
||||
'getDefinition'
|
||||
]);
|
||||
mockType.getDefinition.andReturn(mockTypeDefinition);
|
||||
|
||||
mockTypeService = jasmine.createSpyObj('typeService', [
|
||||
'listTypes',
|
||||
'getType'
|
||||
]);
|
||||
mockTypeService.getType.andReturn(mockType);
|
||||
mockTypeService.listTypes.andReturn([mockType]);
|
||||
|
||||
typeRegionDecorator = new TypeRegionDecorator(mockTypeService);
|
||||
});
|
||||
|
||||
it("decorates individual type definitions with basic inspector" +
|
||||
" region", function () {
|
||||
typeRegionDecorator.getType('someType');
|
||||
expect(mockTypeDefinition.regions).toBeDefined();
|
||||
});
|
||||
|
||||
it("decorates all type definitions with basic inspector" +
|
||||
" region", function () {
|
||||
typeRegionDecorator.listTypes();
|
||||
expect(mockTypeDefinition.regions).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -23,6 +23,7 @@
|
||||
ng-controller="DateTimeFieldController">
|
||||
<input type="text"
|
||||
ng-model="textValue"
|
||||
ng-change="structure.onchange(ngModel[field])"
|
||||
ng-blur="restoreTextValue(); ngBlur()"
|
||||
ng-class="{
|
||||
error: textInvalid ||
|
||||
|
@ -44,11 +44,11 @@ define(
|
||||
if (domainObject.getCapability('status').get('editing')){
|
||||
//If the domain object is in edit mode, only include a part
|
||||
// if it is marked editable
|
||||
return regionPart.modes.indexOf('edit') != -1;
|
||||
return regionPart.modes.indexOf('edit') !== -1;
|
||||
} else {
|
||||
//If the domain object is not in edit mode, return any parts
|
||||
// that are not explicitly marked editable
|
||||
return regionPart.modes.indexOf('browse') != -1;
|
||||
return regionPart.modes.indexOf('browse') !== -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,17 +74,19 @@ define(
|
||||
|
||||
/**
|
||||
* Removes a part from this region.
|
||||
* @param {RegionPart} part the part to add
|
||||
* @param {number} [index] the position to insert the part. By default
|
||||
* will add to the end
|
||||
* @param {RegionPart | number | strnig} part The region part to
|
||||
* remove. If a number, will remove the part at that index. If a
|
||||
* string, will remove the part with the matching name. If an
|
||||
* object, will attempt to remove that object from the Region
|
||||
*/
|
||||
Region.prototype.removePart = function (part){
|
||||
if (typeof part === 'number') {
|
||||
this.parts.splice(part, 1);
|
||||
} else if (typeof part === 'string'){
|
||||
this.parts
|
||||
}
|
||||
else {
|
||||
this.parts = this.parts.filter(function(thisPart) {
|
||||
return thisPart.name !== part;
|
||||
});
|
||||
} else {
|
||||
this.parts.splice(this.parts.indexOf(part), 1);
|
||||
}
|
||||
};
|
||||
|
@ -37,22 +37,19 @@ define(
|
||||
typeCapability = domainObject.getCapability('type');
|
||||
|
||||
/**
|
||||
* TODO: Refactor this out, probably to a directive.
|
||||
* Or, alternatively, could have a regionCapability that returns
|
||||
* regions and parts filtered for applicability to current
|
||||
* object state.
|
||||
* Filters region parts to only those allowed by region policies
|
||||
* @param regions
|
||||
* @returns {*}
|
||||
* @returns {{}}
|
||||
*/
|
||||
function filterParts(regions) {
|
||||
//Dupe so we're not modifying the type definition.
|
||||
var filteredRegions = {};
|
||||
for (var regionName in regions) {
|
||||
Object.keys(regions).forEach(function(regionName) {
|
||||
filteredRegions[regionName] = Object.create(regions[regionName]);
|
||||
filteredRegions[regionName].parts = regions[regionName].parts.filter(function(part){
|
||||
filteredRegions[regionName].parts = (regions[regionName].parts || []).filter(function(part){
|
||||
return policyService.allow('region', part, domainObject);
|
||||
});
|
||||
}
|
||||
});
|
||||
return filteredRegions;
|
||||
}
|
||||
|
||||
|
75
platform/commonUI/regions/test/EditableRegionPolicySpec.js
Normal file
75
platform/commonUI/regions/test/EditableRegionPolicySpec.js
Normal file
@ -0,0 +1,75 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is 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.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
|
||||
|
||||
define(
|
||||
['../src/EditableRegionPolicy'],
|
||||
function (EditableRegionPolicy) {
|
||||
"use strict";
|
||||
|
||||
describe("The editable region policy ", function () {
|
||||
|
||||
var editableRegionPolicy,
|
||||
mockDomainObject,
|
||||
mockStatusCapability,
|
||||
mockBrowseRegionPart = {
|
||||
modes: 'browse'
|
||||
},
|
||||
mockEditRegionPart = {
|
||||
modes: 'edit'
|
||||
},
|
||||
mockAllModesRegionPart = {};
|
||||
|
||||
beforeEach(function(){
|
||||
editableRegionPolicy = new EditableRegionPolicy();
|
||||
|
||||
mockStatusCapability = jasmine.createSpyObj("statusCapability", [
|
||||
"get"
|
||||
]);
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
||||
"getCapability"
|
||||
]);
|
||||
mockDomainObject.getCapability.andReturn(mockStatusCapability);
|
||||
});
|
||||
|
||||
it("includes only browse region parts for object not in edit mode", function() {
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(true);
|
||||
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it("includes only edit region parts for object in edit mode", function() {
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(false);
|
||||
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
it("includes region parts with no mode specification", function() {
|
||||
mockStatusCapability.get.andReturn(false);
|
||||
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
||||
mockStatusCapability.get.andReturn(true);
|
||||
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
82
platform/commonUI/regions/test/RegionControllerSpec.js
Normal file
82
platform/commonUI/regions/test/RegionControllerSpec.js
Normal file
@ -0,0 +1,82 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is 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.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
|
||||
|
||||
define(
|
||||
['../src/RegionController'],
|
||||
function (RegionController) {
|
||||
"use strict";
|
||||
|
||||
describe("The region controller ", function () {
|
||||
var mockScope,
|
||||
mockDomainObject,
|
||||
mockTypeCapability,
|
||||
mockTypeDefinition,
|
||||
mockPolicyService,
|
||||
controller;
|
||||
|
||||
beforeEach(function(){
|
||||
mockTypeDefinition = {
|
||||
regions:
|
||||
{
|
||||
'regionOne': {
|
||||
'parts': [
|
||||
{'name': 'Part One'},
|
||||
{'name': 'Part Two'}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mockTypeCapability = jasmine.createSpyObj('typeCapability', [
|
||||
'getDefinition'
|
||||
]);
|
||||
mockTypeCapability.getDefinition.andReturn(mockTypeDefinition);
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getCapability'
|
||||
]);
|
||||
mockDomainObject.getCapability.andReturn(mockTypeCapability);
|
||||
|
||||
mockPolicyService = jasmine.createSpyObj('policyService', [
|
||||
'allow'
|
||||
]);
|
||||
|
||||
mockScope = {
|
||||
domainObject: mockDomainObject
|
||||
};
|
||||
});
|
||||
|
||||
it("filters out regions disallowed by region policy", function() {
|
||||
mockPolicyService.allow.andReturn(false);
|
||||
controller = new RegionController(mockScope, mockPolicyService);
|
||||
expect(mockScope.regions.regionOne.parts.length).toBe(0);
|
||||
});
|
||||
|
||||
it("does not filter out regions allowed by region policy", function() {
|
||||
mockPolicyService.allow.andReturn(true);
|
||||
controller = new RegionController(mockScope, mockPolicyService);
|
||||
expect(mockScope.regions.regionOne.parts.length).toBe(2);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
107
platform/commonUI/regions/test/RegionSpec.js
Normal file
107
platform/commonUI/regions/test/RegionSpec.js
Normal file
@ -0,0 +1,107 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is 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.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
|
||||
|
||||
define(
|
||||
['../src/Region'],
|
||||
function (Region) {
|
||||
"use strict";
|
||||
|
||||
describe("The region class ", function () {
|
||||
|
||||
var region,
|
||||
part2 = {'name': 'part2'};
|
||||
|
||||
beforeEach(function(){
|
||||
region = new Region();
|
||||
region.parts = [
|
||||
{name: 'part1'},
|
||||
{name: 'part3'},
|
||||
{name: 'part4'}
|
||||
];
|
||||
});
|
||||
|
||||
it("adding a region part at a specified index adds it in that" +
|
||||
" position", function() {
|
||||
|
||||
region.addPart(part2, 1);
|
||||
|
||||
expect(region.parts.length).toBe(4);
|
||||
expect(region.parts[1]).toBe(part2);
|
||||
});
|
||||
|
||||
it("adding a region part without an index adds it at the end", function() {
|
||||
var partN = {'name': 'partN'};
|
||||
|
||||
region.addPart(partN);
|
||||
|
||||
expect(region.parts.length).toBe(4);
|
||||
expect(region.parts[region.parts.length-1]).toBe(partN);
|
||||
});
|
||||
|
||||
describe("removing a region part", function(){
|
||||
var partName = "part2";
|
||||
|
||||
beforeEach(function(){
|
||||
region.parts = [
|
||||
{name: 'part1'},
|
||||
part2,
|
||||
{name: 'part3'},
|
||||
{name: 'part4'}
|
||||
];
|
||||
});
|
||||
|
||||
it("with a string matches on region part" +
|
||||
" name", function() {
|
||||
expect(region.parts.length).toBe(4);
|
||||
expect(region.parts.indexOf(part2)).toBe(1);
|
||||
|
||||
region.removePart(partName);
|
||||
|
||||
expect(region.parts.length).toBe(3);
|
||||
expect(region.parts.indexOf(part2)).toBe(-1);
|
||||
});
|
||||
|
||||
it("with a number removes by index", function() {
|
||||
expect(region.parts.length).toBe(4);
|
||||
expect(region.parts.indexOf(part2)).toBe(1);
|
||||
|
||||
region.removePart(1);
|
||||
|
||||
expect(region.parts.length).toBe(3);
|
||||
expect(region.parts.indexOf(part2)).toBe(-1);
|
||||
});
|
||||
|
||||
|
||||
it("with object matches that object", function() {
|
||||
expect(region.parts.length).toBe(4);
|
||||
expect(region.parts.indexOf(part2)).toBe(1);
|
||||
|
||||
region.removePart(part2);
|
||||
|
||||
expect(region.parts.length).toBe(3);
|
||||
expect(region.parts.indexOf(part2)).toBe(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,3 +0,0 @@
|
||||
[
|
||||
"InspectorRegion"
|
||||
]
|
@ -25,45 +25,15 @@ define([
|
||||
"./src/LayoutController",
|
||||
"./src/FixedController",
|
||||
"./src/LayoutCompositionPolicy",
|
||||
"../../commonUI/browse/src/InspectorRegion",
|
||||
"./src/PlotOptionsController",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
LayoutController,
|
||||
FixedController,
|
||||
LayoutCompositionPolicy,
|
||||
InspectorRegion,
|
||||
PlotOptionsController,
|
||||
legacyRegistry
|
||||
) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Customize and extend the default 'Inspector' region for the panel
|
||||
* type, to add display options for plots. This should be moved to a
|
||||
* dedicated type.
|
||||
* @type {InspectorRegion}
|
||||
*/
|
||||
var plotInspector = new InspectorRegion(),
|
||||
plotOptionsBrowsePart = {
|
||||
name: "plot-options",
|
||||
title: "Plot Options",
|
||||
modes: ['browse'],
|
||||
content: {
|
||||
key: "plot-options-browse"
|
||||
}
|
||||
},
|
||||
plotOptionsEditPart = {
|
||||
name: "plot-options",
|
||||
title: "Plot Options",
|
||||
modes: ['edit'],
|
||||
content: {
|
||||
key: "plot-options-browse"
|
||||
}
|
||||
};
|
||||
plotInspector.addPart(plotOptionsBrowsePart);
|
||||
plotInspector.addPart(plotOptionsEditPart);
|
||||
|
||||
legacyRegistry.register("platform/features/layout", {
|
||||
"name": "Layout components.",
|
||||
"description": "Plug in adding Layout capabilities.",
|
||||
@ -222,10 +192,6 @@ define([
|
||||
{
|
||||
"key": "frame",
|
||||
"templateUrl": "templates/frame.html"
|
||||
},
|
||||
{
|
||||
"key": "plot-options-browse",
|
||||
"templateUrl": "templates/plot-options-browse.html"
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
@ -247,13 +213,6 @@ define([
|
||||
"telemetryFormatter",
|
||||
"throttle"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "PlotOptionsController",
|
||||
"implementation": PlotOptionsController,
|
||||
"depends": [
|
||||
"$scope"
|
||||
]
|
||||
}
|
||||
],
|
||||
"templates": [
|
||||
@ -353,12 +312,7 @@ define([
|
||||
"property": "layoutGrid",
|
||||
"conversion": "number[]"
|
||||
}
|
||||
],
|
||||
"regions": {
|
||||
"inspector": plotInspector
|
||||
}
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -19,8 +19,31 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<style>
|
||||
.l-inspect .l-inspector-part .no-margin .form {
|
||||
margin-left: 0;
|
||||
}
|
||||
.reduced-min-width .form .form-row > .label {
|
||||
min-width: 80px;
|
||||
}
|
||||
</style>
|
||||
<div ng-controller="PlotOptionsController" class="flex-elem grows l-inspector-part">
|
||||
<em class="t-inspector-part-header" title="Display properties for this object">Display</em>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.xAxis"
|
||||
structure="xAxisForm"
|
||||
name="xAxisFormState"
|
||||
class="flex-elem l-flex-row no-validate no-margin reduced-min-width">
|
||||
</mct-form>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.yAxis"
|
||||
structure="yAxisForm"
|
||||
name="yAxisFormState"
|
||||
class="flex-elem l-flex-row no-validate no-margin reduced-min-width">
|
||||
</mct-form>
|
||||
<div class="section-header ng-binding ng-scope">
|
||||
Plot Series
|
||||
</div>
|
||||
<ul class="first flex-elem grows vscroll">
|
||||
<ul class="tree">
|
||||
<li ng-repeat="child in children">
|
||||
@ -41,8 +64,8 @@
|
||||
</span>
|
||||
<mct-form
|
||||
ng-class="{hidden: !toggle.isActive()}"
|
||||
ng-model="plotOptionsModel"
|
||||
structure="plotOptionsStructure"
|
||||
ng-model="configuration.plot.series[child.getId()]"
|
||||
structure="plotSeriesForm"
|
||||
name="plotOptionsState"
|
||||
class="flex-elem l-flex-row l-controls-first no-validate">
|
||||
</mct-form>
|
||||
|
@ -27,8 +27,8 @@
|
||||
* @namespace platform/features/layout
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
['./PlotOptionsForm'],
|
||||
function (PlotOptionsForm) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
@ -50,136 +50,98 @@ define(
|
||||
* @constructor
|
||||
* @param {Scope} $scope the controller's Angular scope
|
||||
*/
|
||||
function PlotOptionsController($scope) {
|
||||
function PlotOptionsController($scope, topic) {
|
||||
|
||||
var self = this,
|
||||
domainObject = $scope.domainObject,
|
||||
xAxisForm = {
|
||||
'name':'x-axis',
|
||||
'sections': [{
|
||||
'name': 'x-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Domain',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
//TODO fetch options from platform or object type configuration
|
||||
'options': [
|
||||
{'name':'scet', 'value': 'scet'},
|
||||
{'name':'sclk', 'value': 'sclk'},
|
||||
{'name':'lst', 'value': 'lst'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]},
|
||||
yAxisForm = {
|
||||
'name':'y-axis',
|
||||
'sections': [{
|
||||
// Will need to be repeated for each y-axis, with a
|
||||
// distinct name each. Ideally the name of the axis itself.
|
||||
'name': 'y-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Autoscale',
|
||||
'control': 'checkbox',
|
||||
'key': 'autoscale',
|
||||
},
|
||||
{
|
||||
'name': 'Min',
|
||||
'control': 'textfield',
|
||||
'key': 'min',
|
||||
'pattern': '[0-9]'
|
||||
},
|
||||
{
|
||||
'name': 'Max',
|
||||
'control': 'textfield',
|
||||
'key': 'min',
|
||||
'pattern': '[0-9]'
|
||||
},
|
||||
{
|
||||
'name': 'Range',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
'options': [
|
||||
{'name':'eu', 'value': 'eu'},
|
||||
{'name':'dn', 'value': 'dn'},
|
||||
{'name':'status', 'value': 'status'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
},
|
||||
plotSeriesForm = {
|
||||
// For correctness of the rendered markup, repeated forms
|
||||
// will probably need to have unique names.
|
||||
'name': 'plotSeries',
|
||||
'sections': [{
|
||||
'name': 'Plot Series',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Markers',
|
||||
'control': 'checkbox',
|
||||
'key': 'markers'
|
||||
},
|
||||
{
|
||||
'name': 'No Line',
|
||||
'control': 'radio',
|
||||
'key': 'noLine'
|
||||
},
|
||||
{
|
||||
'name': 'Step Line',
|
||||
'control': 'radio',
|
||||
'key': 'stepLine'
|
||||
},
|
||||
{
|
||||
'name': 'Linear Line',
|
||||
'control': 'radio',
|
||||
'key': 'linearLine'
|
||||
}
|
||||
]
|
||||
}]
|
||||
},
|
||||
plotOptionsModel = {};
|
||||
composition,
|
||||
mutationListener,
|
||||
formListener,
|
||||
configuration = domainObject.getModel().configuration || {};
|
||||
|
||||
/*domainObject.getModel().configuration.plot.xAxis= {
|
||||
'key': 'scet'
|
||||
};
|
||||
this.plotOptionsForm = new PlotOptionsForm(topic);
|
||||
|
||||
domainObject.getModel().configuration.plot.yAxis = [{
|
||||
'autoscale': true,
|
||||
'min': 0,
|
||||
'max': 15,
|
||||
'key': 'eu'
|
||||
}];
|
||||
/*
|
||||
* Determine whether the changes to the model that triggered a
|
||||
* mutation event were purely compositional.
|
||||
*/
|
||||
function hasCompositionChanged(oldComposition, newComposition){
|
||||
// Framed slightly strangely, but the boolean logic is
|
||||
// easier to follow for the unchanged case.
|
||||
var isUnchanged = oldComposition === newComposition ||
|
||||
(
|
||||
oldComposition.length === newComposition.length &&
|
||||
oldComposition.every( function (currentValue, index) {
|
||||
return newComposition[index] && currentValue === newComposition[index];
|
||||
})
|
||||
);
|
||||
return !isUnchanged;
|
||||
}
|
||||
|
||||
domainObject.getModel().configuration.plot.series = [
|
||||
{
|
||||
'id': '',
|
||||
'lineStyle': '',
|
||||
'color': '#aaddaa',
|
||||
'interpolation': 'none'
|
||||
},
|
||||
//etc
|
||||
];*/
|
||||
|
||||
$scope.plotOptionsStructure = plotSeriesForm;
|
||||
$scope.plotOptionsModel = plotOptionsModel;
|
||||
/*
|
||||
Default the plot options model
|
||||
*/
|
||||
function defaultConfiguration() {
|
||||
configuration.plot = configuration.plot || {};
|
||||
configuration.plot.xAxis = configuration.plot.xAxis || {};
|
||||
configuration.plot.yAxis = configuration.plot.yAxis || {}; // y-axes will be associative array keyed on axis key
|
||||
configuration.plot.series = configuration.plot.series || {}; // series will be associative array keyed on sub-object id
|
||||
$scope.configuration = configuration;
|
||||
}
|
||||
|
||||
/*
|
||||
When a child is added to, or removed from a plot, update the
|
||||
plot options model
|
||||
*/
|
||||
function updateChildren() {
|
||||
domainObject.useCapability('composition').then(function(children){
|
||||
$scope.children = children;
|
||||
composition = domainObject.getModel().composition;
|
||||
children.forEach(function(child){
|
||||
configuration.plot.series[child.getId()] = configuration.plot.series[child.getId()] || {};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
On changes to the form, update the configuration on the domain
|
||||
object
|
||||
*/
|
||||
function updateConfiguration() {
|
||||
domainObject.useCapability('mutation', function(model){
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration.plot = configuration.plot;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Set form structures on scope
|
||||
*/
|
||||
$scope.plotSeriesForm = this.plotOptionsForm.plotSeriesForm;
|
||||
$scope.xAxisForm = this.plotOptionsForm.xAxisForm;
|
||||
$scope.yAxisForm = this.plotOptionsForm.yAxisForm;
|
||||
|
||||
/*
|
||||
Listen for changes to the domain object and update the object's
|
||||
children.
|
||||
*/
|
||||
domainObject.getCapability('mutation').listen(function(model) {
|
||||
updateChildren();
|
||||
mutationListener = domainObject.getCapability('mutation').listen(function(model) {
|
||||
if (hasCompositionChanged(composition, model.composition)) {
|
||||
updateChildren();
|
||||
}
|
||||
});
|
||||
|
||||
formListener = this.plotOptionsForm.listen(updateConfiguration);
|
||||
|
||||
defaultConfiguration();
|
||||
updateChildren();
|
||||
|
||||
$scope.$on("$destroy", function() {
|
||||
//Clean up any listeners on destruction of controller
|
||||
mutationListener();
|
||||
formListener();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return PlotOptionsController;
|
||||
|
166
platform/features/layout/src/PlotOptionsForm.js
Normal file
166
platform/features/layout/src/PlotOptionsForm.js
Normal file
@ -0,0 +1,166 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is 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.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A class for encapsulating structure and behaviour of the plot
|
||||
* options form
|
||||
* @memberOf platform/features/layout
|
||||
* @param topic
|
||||
* @constructor
|
||||
*/
|
||||
function PlotOptionsForm(topic) {
|
||||
var self = this;
|
||||
this.onchangeTopic = topic();
|
||||
|
||||
function onchange(value){
|
||||
self.onchangeTopic.notify(value);
|
||||
}
|
||||
|
||||
/*
|
||||
Defined below are the form structures for the plot options.
|
||||
*/
|
||||
this.xAxisForm = {
|
||||
'name':'x-axis',
|
||||
'sections': [{
|
||||
'name': 'x-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Domain',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
'onchange': onchange,
|
||||
'options': [
|
||||
{'name':'scet', 'value': 'scet'},
|
||||
{'name':'sclk', 'value': 'sclk'},
|
||||
{'name':'lst', 'value': 'lst'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]};
|
||||
|
||||
this.yAxisForm = {
|
||||
'name':'y-axis',
|
||||
'sections': [{
|
||||
// Will need to be repeated for each y-axis, with a
|
||||
// distinct name for each. Ideally the name of the axis
|
||||
// itself.
|
||||
'name': 'y-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Autoscale',
|
||||
'control': 'checkbox',
|
||||
'key': 'autoscale',
|
||||
'onchange': onchange
|
||||
},
|
||||
{
|
||||
'name': 'Min',
|
||||
'control': 'textfield',
|
||||
'key': 'min',
|
||||
'pattern': '[0-9]',
|
||||
'onchange': onchange
|
||||
},
|
||||
{
|
||||
'name': 'Max',
|
||||
'control': 'textfield',
|
||||
'key': 'max',
|
||||
'pattern': '[0-9]',
|
||||
'onchange': onchange
|
||||
},
|
||||
{
|
||||
'name': 'Range',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
'onchange': onchange,
|
||||
'options': [
|
||||
{'name':'eu', 'value': 'eu'},
|
||||
{'name':'dn', 'value': 'dn'},
|
||||
{'name':'status', 'value': 'status'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
this.plotSeriesForm = {
|
||||
'name':'Series Options',
|
||||
'sections': [
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
'name': 'Color',
|
||||
'control': 'color',
|
||||
'key': 'color',
|
||||
'onchange': onchange
|
||||
}]
|
||||
},
|
||||
{
|
||||
'rows':[
|
||||
{
|
||||
'name': 'Markers',
|
||||
'control': 'checkbox',
|
||||
'key': 'markers',
|
||||
'onchange': onchange
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'rows':[
|
||||
{
|
||||
'name': 'No Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'noLine',
|
||||
'onchange': onchange
|
||||
},
|
||||
{
|
||||
'name': 'Step Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'stepLine',
|
||||
'onchange': onchange
|
||||
},
|
||||
{
|
||||
'name': 'Linear Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'linearLine',
|
||||
'onchange': onchange
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
PlotOptionsForm.prototype.listen = function (callback){
|
||||
return this.onchangeTopic.listen(callback);
|
||||
};
|
||||
|
||||
return PlotOptionsForm;
|
||||
}
|
||||
);
|
||||
|
161
platform/features/layout/test/PlotOptionsControllerSpec.js
Normal file
161
platform/features/layout/test/PlotOptionsControllerSpec.js
Normal file
@ -0,0 +1,161 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is 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.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,jasmine,xit*/
|
||||
|
||||
define(
|
||||
['../src/PlotOptionsController'],
|
||||
function (PlotOptionsController) {
|
||||
"use strict";
|
||||
|
||||
describe("The Plot Options controller", function () {
|
||||
var plotOptionsController,
|
||||
mockTopicFunction,
|
||||
mockTopicObject,
|
||||
mockDomainObject,
|
||||
mockMutationCapability,
|
||||
mockUseCapabilities,
|
||||
mockCompositionCapability,
|
||||
mockComposition,
|
||||
mockUnlisten,
|
||||
mockFormUnlisten,
|
||||
mockChildOne,
|
||||
mockChildTwo,
|
||||
model,
|
||||
mockScope;
|
||||
|
||||
beforeEach(function () {
|
||||
model = {
|
||||
composition: ['childOne']
|
||||
};
|
||||
|
||||
mockChildOne = jasmine.createSpyObj('domainObject', [
|
||||
'getId'
|
||||
]);
|
||||
mockChildOne.getId.andReturn('childOne');
|
||||
|
||||
mockChildTwo = jasmine.createSpyObj('childTwo', [
|
||||
'getId'
|
||||
]);
|
||||
mockChildOne.getId.andReturn('childTwo');
|
||||
|
||||
mockCompositionCapability = jasmine.createSpyObj('compositionCapability', [
|
||||
'then'
|
||||
]);
|
||||
mockComposition = [
|
||||
mockChildOne
|
||||
];
|
||||
mockCompositionCapability.then.andCallFake(function (callback){
|
||||
callback(mockComposition);
|
||||
});
|
||||
|
||||
mockUseCapabilities = jasmine.createSpyObj('useCapabilities', [
|
||||
'composition',
|
||||
'mutation'
|
||||
]);
|
||||
mockUseCapabilities.composition.andReturn(mockCompositionCapability);
|
||||
|
||||
mockMutationCapability = jasmine.createSpyObj('mutationCapability', [
|
||||
'listen'
|
||||
]);
|
||||
mockUnlisten = jasmine.createSpy('unlisten');
|
||||
mockMutationCapability.listen.andReturn(mockUnlisten);
|
||||
|
||||
|
||||
mockTopicObject = jasmine.createSpyObj('Topic', [
|
||||
'listen',
|
||||
'notify'
|
||||
]);
|
||||
mockFormUnlisten = jasmine.createSpy('formUnlisten');
|
||||
mockTopicObject.listen.andReturn(mockFormUnlisten);
|
||||
|
||||
mockTopicFunction = function() {
|
||||
return mockTopicObject;
|
||||
};
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getModel',
|
||||
'useCapability',
|
||||
'getCapability'
|
||||
]);
|
||||
mockDomainObject.useCapability.andCallFake(function(capability){
|
||||
return mockUseCapabilities[capability]();
|
||||
});
|
||||
mockDomainObject.getCapability.andReturn(mockMutationCapability);
|
||||
mockDomainObject.getModel.andReturn(model);
|
||||
|
||||
mockScope = jasmine.createSpyObj('scope', [
|
||||
'$on'
|
||||
]);
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
plotOptionsController = new PlotOptionsController(mockScope, mockTopicFunction);
|
||||
});
|
||||
|
||||
it("sets form definitions on scope", function () {
|
||||
expect(mockScope.xAxisForm).toBeDefined();
|
||||
expect(mockScope.yAxisForm).toBeDefined();
|
||||
expect(mockScope.plotSeriesForm).toBeDefined();
|
||||
});
|
||||
|
||||
it("sets object children on scope", function () {
|
||||
expect(mockScope.children).toBe(mockComposition);
|
||||
});
|
||||
|
||||
it("on changes in object composition, updates the form", function () {
|
||||
expect(mockMutationCapability.listen).toHaveBeenCalled();
|
||||
expect(mockScope.children).toBe(mockComposition);
|
||||
expect(mockScope.children.length).toBe(1);
|
||||
mockComposition.push(mockChildTwo);
|
||||
model.composition.push('childTwo');
|
||||
mockMutationCapability.listen.mostRecentCall.args[0](model);
|
||||
expect(mockScope.children).toBe(mockComposition);
|
||||
expect(mockScope.children.length).toBe(2);
|
||||
});
|
||||
|
||||
it("on changes in form values, updates the object model", function () {
|
||||
var scopeConfiguration = mockScope.configuration,
|
||||
model = mockDomainObject.getModel();
|
||||
|
||||
scopeConfiguration.plot.xAxis.key = 'lst';
|
||||
scopeConfiguration.plot.yAxis.autoScale = true;
|
||||
scopeConfiguration.plot.yAxis.key = 'eu';
|
||||
|
||||
expect(mockTopicObject.listen).toHaveBeenCalled();
|
||||
mockTopicObject.listen.mostRecentCall.args[0]();
|
||||
expect(mockDomainObject.useCapability).toHaveBeenCalledWith('mutation', jasmine.any(Function));
|
||||
|
||||
mockDomainObject.useCapability.mostRecentCall.args[1](model);
|
||||
expect(model.configuration.plot.xAxis.key).toBe('lst');
|
||||
expect(model.configuration.plot.yAxis.autoScale).toBe(true);
|
||||
expect(model.configuration.plot.yAxis.key).toBe('eu');
|
||||
|
||||
});
|
||||
|
||||
it("cleans up listeners on destruction of the controller", function () {
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
expect(mockUnlisten).toHaveBeenCalled();
|
||||
expect(mockFormUnlisten).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
73
platform/features/layout/test/PlotOptionsFormSpec.js
Normal file
73
platform/features/layout/test/PlotOptionsFormSpec.js
Normal file
@ -0,0 +1,73 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is 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.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,jasmine,xit*/
|
||||
|
||||
define(
|
||||
['../src/PlotOptionsForm'],
|
||||
function (PlotOptionsForm) {
|
||||
"use strict";
|
||||
|
||||
describe("The Plot Options form", function () {
|
||||
var plotOptionsForm,
|
||||
mockTopicFunction,
|
||||
mockTopicObject,
|
||||
listener;
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
mockTopicObject = jasmine.createSpyObj('Topic', [
|
||||
'listen',
|
||||
'notify'
|
||||
]);
|
||||
mockTopicFunction = function() {
|
||||
return mockTopicObject;
|
||||
};
|
||||
|
||||
plotOptionsForm = new PlotOptionsForm(mockTopicFunction);
|
||||
});
|
||||
|
||||
it("defines form specs for x-axis, y-axis, and series data", function () {
|
||||
expect(plotOptionsForm.xAxisForm).toBeDefined();
|
||||
expect(plotOptionsForm.xAxisForm.sections).toBeDefined();
|
||||
expect(plotOptionsForm.xAxisForm.sections[0].rows).toBeDefined();
|
||||
expect(plotOptionsForm.xAxisForm.sections[0].rows.length).toBeGreaterThan(0);
|
||||
|
||||
expect(plotOptionsForm.yAxisForm).toBeDefined();
|
||||
expect(plotOptionsForm.plotSeriesForm).toBeDefined();
|
||||
});
|
||||
|
||||
it("uses a topic to register listeners and inform them when a" +
|
||||
" form value changes", function () {
|
||||
var changedValue = 'changedValue';
|
||||
|
||||
expect(plotOptionsForm.xAxisForm.sections[0].rows[0].onchange).toBeDefined();
|
||||
|
||||
plotOptionsForm.listen(listener);
|
||||
expect(mockTopicObject.listen).toHaveBeenCalledWith(listener);
|
||||
|
||||
plotOptionsForm.xAxisForm.sections[0].rows[0].onchange(changedValue);
|
||||
expect(mockTopicObject.notify).toHaveBeenCalledWith(changedValue);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -23,6 +23,7 @@
|
||||
<input type="checkbox"
|
||||
name="mctControl"
|
||||
ng-model="ngModel[field]"
|
||||
ng-change="structure.onchange(ngModel[field])"
|
||||
ng-disabled="ngDisabled">
|
||||
<em></em>
|
||||
</label>
|
||||
|
@ -23,6 +23,8 @@
|
||||
<input type="radio"
|
||||
name="mctControl"
|
||||
ng-model="ngModel[field]"
|
||||
ng-disabled="ngDisabled">
|
||||
ng-disabled="ngDisabled"
|
||||
ng-change="structure.onchange(ngModel[field])"
|
||||
ng-value="structure.value">
|
||||
<em></em>
|
||||
</label>
|
||||
|
@ -24,6 +24,7 @@
|
||||
ng-model="ngModel[field]"
|
||||
ng-options="opt.value as opt.name for opt in options"
|
||||
ng-required="ngRequired"
|
||||
ng-change="structure.onchange(ngModel[field])"
|
||||
name="mctControl">
|
||||
<option value="" ng-show="!ngModel[field]">- Select One -</option>
|
||||
</select>
|
||||
|
@ -25,6 +25,7 @@
|
||||
ng-required="ngRequired"
|
||||
ng-model="ngModel[field]"
|
||||
ng-pattern="ngPattern"
|
||||
ng-change="structure.onchange(ngModel[field])"
|
||||
size="{{structure.size}}"
|
||||
name="mctControl">
|
||||
</span>
|
||||
|
@ -41,6 +41,7 @@
|
||||
<mct-control key="row.control"
|
||||
ng-model="ngModel"
|
||||
ng-required="row.required"
|
||||
ng-change="row.onchange"
|
||||
ng-pattern="getRegExp(row.pattern)"
|
||||
options="row.options"
|
||||
structure="row"
|
||||
|
Loading…
Reference in New Issue
Block a user