mirror of
https://github.com/nasa/openmct.git
synced 2025-01-31 00:23:54 +00:00
Merge pull request #347 from nasa/open259b
[Time Conductor] Don't update model while typing (v2)
This commit is contained in:
commit
096da0cb94
@ -941,6 +941,12 @@ look at field (see below) to determine which field in the model should be
|
||||
modified.
|
||||
* `ngRequired`: True if input is required.
|
||||
* `ngPattern`: The pattern to match against (for text entry)
|
||||
* `ngBlur`: A function that may be invoked to evaluate the expression
|
||||
associated with the `ng-blur` attribute associated with the control.
|
||||
* This should be called when the control has lost focus; for controls
|
||||
which simply wrap or augment `input` elements, this should be fired
|
||||
on `blur` events associated with those elements, while more complex
|
||||
custom controls may fire this at the end of more specific interactions.
|
||||
* `options`: The options for this control, as passed from the `options` property
|
||||
of an individual row definition.
|
||||
* `field`: Name of the field in `ngModel` which will hold the value for this
|
||||
|
@ -1,7 +1,29 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<span class="s-btn"
|
||||
ng-controller="DateTimeFieldController">
|
||||
<input type="text"
|
||||
ng-model="textValue"
|
||||
ng-blur="restoreTextValue(); ngBlur()"
|
||||
ng-class="{ error: textInvalid }">
|
||||
</input>
|
||||
<a class="ui-symbol icon icon-calendar"
|
||||
@ -11,8 +33,8 @@
|
||||
<mct-popup ng-if="picker.active">
|
||||
<div mct-click-elsewhere="picker.active = false">
|
||||
<mct-control key="'datetime-picker'"
|
||||
ng-model="ngModel"
|
||||
field="field"
|
||||
ng-model="pickerModel"
|
||||
field="'value'"
|
||||
options="{ hours: true }">
|
||||
</mct-control>
|
||||
</div>
|
||||
|
@ -20,12 +20,14 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div ng-controller="TimeRangeController">
|
||||
<div class="l-time-range-inputs-holder">
|
||||
<form class="l-time-range-inputs-holder"
|
||||
ng-submit="updateBoundsFromForm()">
|
||||
<span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
|
||||
<span class="l-time-range-input">
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{ format: parameters.format }"
|
||||
ng-model="ngModel.outer"
|
||||
ng-model="formModel"
|
||||
ng-blur="updateBoundsFromForm()"
|
||||
field="'start'"
|
||||
class="time-range-start">
|
||||
</mct-control>
|
||||
@ -36,12 +38,15 @@
|
||||
<span class="l-time-range-input" ng-controller="ToggleController as t2">
|
||||
<mct-control key="'datetime-field'"
|
||||
structure="{ format: parameters.format }"
|
||||
ng-model="ngModel.outer"
|
||||
ng-model="formModel"
|
||||
ng-blur="updateBoundsFromForm()"
|
||||
field="'end'"
|
||||
class="time-range-end">
|
||||
</mct-control>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="hidden">
|
||||
</form>
|
||||
|
||||
<div class="l-time-range-slider-holder">
|
||||
<div class="l-time-range-slider">
|
||||
|
@ -53,7 +53,9 @@ define(
|
||||
formatter.parse($scope.textValue) !== value) {
|
||||
$scope.textValue = formatter.format(value);
|
||||
$scope.textInvalid = false;
|
||||
$scope.lastValidValue = $scope.textValue;
|
||||
}
|
||||
$scope.pickerModel = { value: value };
|
||||
}
|
||||
|
||||
function updateFromView(textValue) {
|
||||
@ -61,6 +63,17 @@ define(
|
||||
if (!$scope.textInvalid) {
|
||||
$scope.ngModel[$scope.field] =
|
||||
formatter.parse(textValue);
|
||||
$scope.lastValidValue = $scope.textValue;
|
||||
}
|
||||
}
|
||||
|
||||
function updateFromPicker(value) {
|
||||
if (value !== $scope.ngModel[$scope.field]) {
|
||||
$scope.ngModel[$scope.field] = value;
|
||||
updateFromModel(value);
|
||||
if ($scope.ngBlur) {
|
||||
$scope.ngBlur();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,10 +82,18 @@ define(
|
||||
updateFromModel($scope.ngModel[$scope.field]);
|
||||
}
|
||||
|
||||
function restoreTextValue() {
|
||||
$scope.textValue = $scope.lastValidValue;
|
||||
updateFromView($scope.textValue);
|
||||
}
|
||||
|
||||
$scope.restoreTextValue = restoreTextValue;
|
||||
|
||||
$scope.picker = { active: false };
|
||||
|
||||
$scope.$watch('structure.format', setFormat);
|
||||
$scope.$watch('ngModel[field]', updateFromModel);
|
||||
$scope.$watch('pickerModel.value', updateFromPicker);
|
||||
$scope.$watch('textValue', updateFromView);
|
||||
|
||||
}
|
||||
|
@ -175,6 +175,13 @@ define(
|
||||
updateViewFromModel($scope.ngModel);
|
||||
}
|
||||
|
||||
function updateFormModel() {
|
||||
$scope.formModel = {
|
||||
start: (($scope.ngModel || {}).outer || {}).start,
|
||||
end: (($scope.ngModel || {}).outer || {}).end
|
||||
};
|
||||
}
|
||||
|
||||
function updateOuterStart(t) {
|
||||
var ngModel = $scope.ngModel;
|
||||
|
||||
@ -192,6 +199,7 @@ define(
|
||||
ngModel.inner.end
|
||||
);
|
||||
|
||||
updateFormModel();
|
||||
updateViewForInnerSpanFromModel(ngModel);
|
||||
updateTicks();
|
||||
}
|
||||
@ -213,6 +221,7 @@ define(
|
||||
ngModel.inner.start
|
||||
);
|
||||
|
||||
updateFormModel();
|
||||
updateViewForInnerSpanFromModel(ngModel);
|
||||
updateTicks();
|
||||
}
|
||||
@ -223,6 +232,14 @@ define(
|
||||
updateTicks();
|
||||
}
|
||||
|
||||
function updateBoundsFromForm() {
|
||||
$scope.ngModel = $scope.ngModel || {};
|
||||
$scope.ngModel.outer = {
|
||||
start: $scope.formModel.start,
|
||||
end: $scope.formModel.end
|
||||
};
|
||||
}
|
||||
|
||||
$scope.startLeftDrag = startLeftDrag;
|
||||
$scope.startRightDrag = startRightDrag;
|
||||
$scope.startMiddleDrag = startMiddleDrag;
|
||||
@ -230,10 +247,13 @@ define(
|
||||
$scope.rightDrag = rightDrag;
|
||||
$scope.middleDrag = middleDrag;
|
||||
|
||||
$scope.updateBoundsFromForm = updateBoundsFromForm;
|
||||
|
||||
$scope.ticks = [];
|
||||
|
||||
// Initialize scope to defaults
|
||||
updateViewFromModel($scope.ngModel);
|
||||
updateFormModel();
|
||||
|
||||
$scope.$watchCollection("ngModel", updateViewFromModel);
|
||||
$scope.$watch("spanWidth", updateSpanWidth);
|
||||
|
@ -67,21 +67,13 @@ define(
|
||||
mockScope.ngModel = { testField: 12321 };
|
||||
mockScope.field = "testField";
|
||||
mockScope.structure = { format: "someFormat" };
|
||||
mockScope.ngBlur = jasmine.createSpy('blur');
|
||||
|
||||
controller = new DateTimeFieldController(
|
||||
mockScope,
|
||||
mockFormatService
|
||||
);
|
||||
});
|
||||
|
||||
it("updates models from user-entered text", function () {
|
||||
var newText = "1977-05-25 17:30:00";
|
||||
|
||||
mockScope.textValue = newText;
|
||||
fireWatch("textValue", newText);
|
||||
expect(mockScope.ngModel.testField)
|
||||
.toEqual(mockFormat.parse(newText));
|
||||
expect(mockScope.textInvalid).toBeFalsy();
|
||||
fireWatch("ngModel[field]", mockScope.ngModel.testField);
|
||||
});
|
||||
|
||||
it("updates text from model values", function () {
|
||||
@ -91,16 +83,55 @@ define(
|
||||
expect(mockScope.textValue).toEqual("1977-05-25 17:30:00");
|
||||
});
|
||||
|
||||
describe("when valid text is entered", function () {
|
||||
var newText;
|
||||
|
||||
beforeEach(function () {
|
||||
newText = "1977-05-25 17:30:00";
|
||||
mockScope.textValue = newText;
|
||||
fireWatch("textValue", newText);
|
||||
});
|
||||
|
||||
it("updates models from user-entered text", function () {
|
||||
expect(mockScope.ngModel.testField)
|
||||
.toEqual(mockFormat.parse(newText));
|
||||
expect(mockScope.textInvalid).toBeFalsy();
|
||||
});
|
||||
|
||||
it("does not indicate a blur event", function () {
|
||||
expect(mockScope.ngBlur).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when a date is chosen via the date picker", function () {
|
||||
var newValue;
|
||||
|
||||
beforeEach(function () {
|
||||
newValue = 12345654321;
|
||||
mockScope.pickerModel.value = newValue;
|
||||
fireWatch("pickerModel.value", newValue);
|
||||
});
|
||||
|
||||
it("updates models", function () {
|
||||
expect(mockScope.ngModel.testField).toEqual(newValue);
|
||||
});
|
||||
|
||||
it("fires a blur event", function () {
|
||||
expect(mockScope.ngBlur).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes toggle state for date-time picker", function () {
|
||||
expect(mockScope.picker.active).toBe(false);
|
||||
});
|
||||
|
||||
describe("when user input is invalid", function () {
|
||||
var newText, oldValue;
|
||||
var newText, oldText, oldValue;
|
||||
|
||||
beforeEach(function () {
|
||||
newText = "Not a date";
|
||||
oldValue = mockScope.ngModel.testField;
|
||||
oldText = mockScope.textValue;
|
||||
mockScope.textValue = newText;
|
||||
fireWatch("textValue", newText);
|
||||
});
|
||||
@ -116,6 +147,11 @@ define(
|
||||
it("does not modify user input", function () {
|
||||
expect(mockScope.textValue).toEqual(newText);
|
||||
});
|
||||
|
||||
it("restores valid text values on request", function () {
|
||||
mockScope.restoreTextValue();
|
||||
expect(mockScope.textValue).toEqual(oldText);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not modify valid but irregular user input", function () {
|
||||
|
@ -91,6 +91,39 @@ define(
|
||||
.toHaveBeenCalledWith("ngModel", jasmine.any(Function));
|
||||
});
|
||||
|
||||
describe("when changes are made via form entry", function () {
|
||||
beforeEach(function () {
|
||||
mockScope.ngModel = {
|
||||
outer: { start: DAY * 2, end: DAY * 3 },
|
||||
inner: { start: DAY * 2.25, end: DAY * 2.75 }
|
||||
};
|
||||
mockScope.formModel = {
|
||||
start: DAY * 10000,
|
||||
end: DAY * 11000
|
||||
};
|
||||
// These watches may not exist, but Angular would fire
|
||||
// them if they did.
|
||||
fireWatchCollection("formModel", mockScope.formModel);
|
||||
fireWatch("formModel.start", mockScope.formModel.start);
|
||||
fireWatch("formModel.end", mockScope.formModel.end);
|
||||
});
|
||||
|
||||
it("does not immediately make changes to the model", function () {
|
||||
expect(mockScope.ngModel.outer.start)
|
||||
.not.toEqual(mockScope.formModel.start);
|
||||
expect(mockScope.ngModel.outer.end)
|
||||
.not.toEqual(mockScope.formModel.end);
|
||||
});
|
||||
|
||||
it("updates model bounds on request", function () {
|
||||
mockScope.updateBoundsFromForm();
|
||||
expect(mockScope.ngModel.outer.start)
|
||||
.toEqual(mockScope.formModel.start);
|
||||
expect(mockScope.ngModel.outer.end)
|
||||
.toEqual(mockScope.formModel.end);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when dragged", function () {
|
||||
beforeEach(function () {
|
||||
mockScope.ngModel = {
|
||||
|
@ -79,6 +79,9 @@ define(
|
||||
// Used to choose which form control to use
|
||||
key: "=",
|
||||
|
||||
// Allow controls to trigger blur-like events
|
||||
ngBlur: "&",
|
||||
|
||||
// The state of the form value itself
|
||||
ngModel: "=",
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user