Reduce fieldController redundancies

[ci skip]
pull/3701/head
Alex Dik 9 years ago committed by Jens Ulferts
parent e582c304f3
commit 5aeccc2d2c
  1. 38
      frontend/app/components/inplace-edit/directives/display-pane/display-pane.directive.js
  2. 54
      frontend/app/components/inplace-edit/directives/edit-pane/edit-pane.directive.js
  3. 6
      frontend/app/components/inplace-edit/directives/field-display/display-spent-time/display-spent-time.directive.html
  4. 17
      frontend/app/components/inplace-edit/directives/field-display/display-spent-time/display-spent-time.directive.js
  5. 10
      frontend/app/components/inplace-edit/directives/field-display/display-user/display-user.directive.html
  6. 11
      frontend/app/components/inplace-edit/directives/field-display/display-user/display-user.directive.js
  7. 12
      frontend/app/components/inplace-edit/directives/field-display/display-version/display-version.directive.html
  8. 12
      frontend/app/components/inplace-edit/directives/field-display/display-version/display-version.directive.js
  9. 16
      frontend/app/components/inplace-edit/directives/field-edit/edit-date-range/edit-date-range.directive.js
  10. 14
      frontend/app/components/inplace-edit/directives/field-edit/edit-date/edit-date.directive.html
  11. 13
      frontend/app/components/inplace-edit/directives/field-edit/edit-date/edit-date.directive.js
  12. 2
      frontend/app/components/inplace-edit/directives/field-edit/edit-drop-down/edit-drop-down.directive.html
  13. 8
      frontend/app/components/inplace-edit/directives/field-edit/edit-drop-down/edit-drop-down.directive.js
  14. 2
      frontend/app/components/inplace-edit/directives/field-edit/edit-duration/edit-duration.directive.html
  15. 28
      frontend/app/components/inplace-edit/directives/field-edit/edit-duration/edit-duration.directive.js
  16. 2
      frontend/app/components/inplace-edit/directives/field-edit/edit-wiki-textarea/edit-wiki-textarea.directive.html
  17. 7
      frontend/app/components/inplace-edit/directives/field-edit/edit-wiki-textarea/edit-wiki-textarea.directive.js
  18. 16
      frontend/app/components/inplace-edit/directives/main-pane/main-pane.directive.js
  19. 15
      frontend/app/components/inplace-edit/directives/work-package-field/work-package-field.directive.js
  20. 17
      frontend/app/components/inplace-edit/services/inplace-edit-field.service.js
  21. 5
      frontend/app/components/inplace-edit/services/work-package-field.service.js
  22. 2
      frontend/app/templates/inplace-edit/display/fields/boolean.html
  23. 2
      frontend/app/templates/inplace-edit/display/fields/date-range.html
  24. 2
      frontend/app/templates/inplace-edit/display/fields/date.html
  25. 2
      frontend/app/templates/inplace-edit/display/fields/embedded.html
  26. 4
      frontend/app/templates/inplace-edit/display/fields/text.html
  27. 2
      frontend/app/templates/inplace-edit/display/fields/wiki-textarea.html
  28. 2
      frontend/app/templates/inplace-edit/edit/fields/boolean.html
  29. 2
      frontend/app/templates/inplace-edit/edit/fields/float.html
  30. 2
      frontend/app/templates/inplace-edit/edit/fields/integer.html
  31. 2
      frontend/app/templates/inplace-edit/edit/fields/text.html
  32. 2
      frontend/app/templates/inplace-edit/edit/fields/textarea.html
  33. 4
      frontend/app/work_packages/directives/work-package-comment-directive.js
  34. 10
      frontend/app/work_packages/services/editable-fields-state.js
  35. 8
      frontend/tests/unit/tests/components/inplace-edit/directives/edit-drop-down.directive.test.js
  36. 17
      frontend/tests/unit/tests/work_packages/services/editable-fields-state-test.js

@ -40,11 +40,13 @@ function inplaceEditorDisplayPane(EditableFieldsState, $timeout, I18n) {
controllerAs: 'displayPaneController',
link: function(scope, element, attrs, fieldController) {
var field = scope.field;
scope.fieldController = fieldController;
scope.editableFieldsState = EditableFieldsState;
scope.$watchCollection('editableFieldsState.workPackage.form', function() {
var strategy = scope.field.getInplaceDisplayStrategy();
var strategy = field.getInplaceDisplayStrategy();
if (strategy !== scope.displayStrategy) {
scope.displayStrategy = strategy;
@ -53,15 +55,13 @@ function inplaceEditorDisplayPane(EditableFieldsState, $timeout, I18n) {
});
// TODO: extract this when more placeholders come
if (scope.field.name === 'description') {
if (field.name === 'description') {
scope.displayPaneController.placeholder = I18n.t('js.label_click_to_enter_description');
}
scope.$watch('editableFieldsState.errors', function(errors) {
if (errors) {
if (errors[scope.scope.field.name]) {
scope.displayPaneController.startEditing();
}
if (errors && errors[field.name]) {
scope.displayPaneController.startEditing();
}
}, true);
@ -86,36 +86,24 @@ function inplaceEditorDisplayPane(EditableFieldsState, $timeout, I18n) {
inplaceEditorDisplayPane.$inject = ['EditableFieldsState', '$timeout', 'I18n'];
function InplaceEditorDisplayPaneController($scope, EditableFieldsState, HookService) {
function InplaceEditorDisplayPaneController($scope, HookService) {
var field = $scope.field;
this.placeholder = $scope.field.defaultPlaceholder;
this.placeholder = field.defaultPlaceholder;
this.startEditing = function() {
var fieldController = $scope.fieldController;
fieldController.isEditing = true;
};
this.isReadValueEmpty = function() {
return $scope.field.isEmpty();
};
this.getReadValue = function() {
return $scope.field.format();
};
// for dynamic type that is set by plugins
// refactor to a service method the whole extraction
this.getDynamicDirectiveName = function() {
return HookService.call('workPackageOverviewAttributes', {
type: EditableFieldsState.workPackage.schema.props[$scope.field.name].type,
field: $scope.field.name,
workPackage: EditableFieldsState.workPackage
type: field.resource.schema.props[field.name].type,
field: field.name,
workPackage: field.resource
})[0];
};
// expose work package to the dynamic directive
this.getWorkPackage = function() {
return EditableFieldsState.workPackage;
};
}
InplaceEditorDisplayPaneController.$inject = ['$scope', 'EditableFieldsState', 'HookService'];
InplaceEditorDisplayPaneController.$inject = ['$scope', 'HookService'];

@ -30,9 +30,7 @@ angular
.module('openproject.inplace-edit')
.directive('inplaceEditorEditPane', inplaceEditorEditPane);
function inplaceEditorEditPane(WorkPackageFieldService, EditableFieldsState, FocusHelper,
$timeout) {
function inplaceEditorEditPane(EditableFieldsState, FocusHelper, $timeout) {
return {
transclude: true,
replace: true,
@ -41,31 +39,29 @@ function inplaceEditorEditPane(WorkPackageFieldService, EditableFieldsState, Foc
controllerAs: 'editPaneController',
controller: InplaceEditorEditPaneController,
link: function(scope, element, attrs, fieldController) {
var field = scope.field;
scope.fieldController = fieldController;
scope.editableFieldsState = EditableFieldsState;
scope.editPaneController.isRequired = function() {
return WorkPackageFieldService.isRequired(
EditableFieldsState.workPackage,
this.field
);
return field.isRequired();
};
scope.$watchCollection('editableFieldsState.workPackage.form', function(form) {
var strategy = scope.field.getInplaceEditStrategy();
var strategy = field.getInplaceEditStrategy();
if (scope.field.name === 'date' && strategy === 'date') {
if (field.name === 'date' && strategy === 'date') {
form.pendingChanges = EditableFieldsState.getPendingFormChanges();
form.pendingChanges['startDate'] =
form.pendingChanges['dueDate'] =
fieldController.writeValue ? fieldController.writeValue['dueDate'] : null;
field.value ? field.value['dueDate'] : null;
}
if (strategy !== scope.strategy) {
scope.strategy = strategy;
scope.templateUrl = '/templates/inplace-edit/edit/fields/' +
scope.strategy + '.html';
fieldController.updateWriteValue();
}
});
@ -94,10 +90,10 @@ function inplaceEditorEditPane(WorkPackageFieldService, EditableFieldsState, Foc
});
}
scope.$watch('fieldController.writeValue', function(writeValue) {
scope.$watch('field.value', function(value) {
if (scope.fieldController.isEditing) {
var pendingChanges = EditableFieldsState.getPendingFormChanges();
pendingChanges[scope.field.name] = writeValue;
pendingChanges[field.name] = value;
}
}, true);
scope.$on('workPackageRefreshed', function() {
@ -105,12 +101,12 @@ function inplaceEditorEditPane(WorkPackageFieldService, EditableFieldsState, Foc
});
scope.$watch('fieldController.isEditing', function(isEditing) {
var efs = EditableFieldsState, field = scope.field.name;
var efs = EditableFieldsState;
if (isEditing && !efs.editAll.state && !efs.forcedEditState) {
scope.focusInput();
} else if (efs.editAll.state && efs.editAll.isFocusField(field)) {
} else if (efs.editAll.state && efs.editAll.isFocusField(field.name)) {
$timeout(function () {
var focusElement = element.find('.focus-input');
focusElement.length && focusElement.focus()[0].select();
@ -120,8 +116,7 @@ function inplaceEditorEditPane(WorkPackageFieldService, EditableFieldsState, Foc
}
};
}
inplaceEditorEditPane.$inject = ['WorkPackageFieldService', 'EditableFieldsState', 'FocusHelper',
'$timeout'];
inplaceEditorEditPane.$inject = ['EditableFieldsState', 'FocusHelper', '$timeout'];
function InplaceEditorEditPaneController($scope, $element, $location, $timeout, $q, $rootScope,
@ -137,6 +132,7 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout,
};
var vm = this;
var field = $scope.field;
// go full retard
var uploadPendingAttachments = function(wp) {
@ -166,13 +162,13 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout,
submit.reject(e);
};
pendingFormChanges[$scope.field.name] = fieldController.writeValue;
pendingFormChanges[field.name] = field.value;
if (vm.editForm.$invalid) {
var acknowledgedValidationErrors = Object.keys(vm.editForm.$error);
acknowledgedValidationErrors.forEach(function(error) {
if (vm.editForm.$error[error]) {
detectedViolations.push(I18n.t('js.inplace.errors.' + error, {
field: fieldController.getLabel()
field: field.getLabel()
}));
}
});
@ -180,7 +176,7 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout,
}
if (detectedViolations.length) {
EditableFieldsState.errors = EditableFieldsState.errors || {};
EditableFieldsState.errors[$scope.field.name] = detectedViolations.join(' ');
EditableFieldsState.errors[field.name] = detectedViolations.join(' ');
showErrors();
submit.reject();
} else {
@ -195,11 +191,12 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout,
);
result.then(angular.bind(this, function(updatedWorkPackage) {
submit.resolve();
field.resource = _.extend(field.resource, updatedWorkPackage);
$scope.$emit('workPackageUpdatedInEditor', updatedWorkPackage);
$scope.$on('workPackageRefreshed', function() {
fieldController.state.isBusy = false;
fieldController.isEditing = false;
fieldController.updateWriteValue();
});
uploadPendingAttachments(updatedWorkPackage);
})).catch(handleFailure);
@ -224,27 +221,26 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout,
this.discardEditing = function() {
$scope.fieldController.isEditing = false;
delete EditableFieldsState.submissionPromises['work_package'];
delete EditableFieldsState.getPendingFormChanges()[$scope.field.name];
$scope.fieldController.updateWriteValue();
delete EditableFieldsState.getPendingFormChanges()[field.name];
if (
EditableFieldsState.errors &&
EditableFieldsState.errors.hasOwnProperty($scope.field.name)
EditableFieldsState.errors.hasOwnProperty(field.name)
) {
delete EditableFieldsState.errors[$scope.field.name];
delete EditableFieldsState.errors[field.name];
}
};
this.isActive = function() {
return EditableFieldsState.isActiveField($scope.field.name);
return EditableFieldsState.isActiveField(field.name);
};
this.markActive = function() {
EditableFieldsState.submissionPromises['work_package'] = {
field: $scope.field.name,
field: field.name,
thePromise: this.submitField,
prepend: true
};
EditableFieldsState.currentField = $scope.field.name;
EditableFieldsState.currentField = field.name;
};
function afterError() {
@ -262,8 +258,6 @@ function InplaceEditorEditPaneController($scope, $element, $location, $timeout,
$scope.$watch('editableFieldsState.editAll.state', function(state) {
$scope.fieldController.isEditing = state;
$scope.fieldController.lockFocus = true;
!state && $scope.fieldController.updateWriteValue();
});
}
InplaceEditorEditPaneController.$inject = ['$scope', '$element', '$location', '$timeout', '$q',

@ -1,13 +1,13 @@
<div class="spent-time-wrapper">
<span ng-if="field.isEmpty()">{{ displayPaneController.placeholder }}</span>
<span ng-if="field.isEmpty()">{{ field.defaultPlaceholder }}</span>
<span ng-if="!field.isEmpty()">
<span ng-if="customEditorController.isLinkViewable()">
<a href="{{ customEditorController.getPath() }}">
{{ displayPaneController.getReadValue() }}
{{ field.text }}
</a>
</span>
<span ng-if="!customEditorController.isLinkViewable()">
{{ displayPaneController.getReadValue() }}
{{ field.text }}
</span>
</span>
</div>

@ -35,26 +35,23 @@ function inplaceDisplaySpentTime() {
restrict: 'E',
transclude: true,
replace: true,
require: '^inplaceEditorDisplayPane',
templateUrl: '/components/inplace-edit/directives/field-display/display-spent-time/' +
'display-spent-time.directive.html',
controller: InplaceDisplaySpentTimeController,
controllerAs: 'customEditorController',
link: function(scope, element, attrs, controllers) {
scope.displayPaneController = controllers[0];
}
controllerAs: 'customEditorController'
};
}
function InplaceDisplaySpentTimeController(EditableFieldsState) {
function InplaceDisplaySpentTimeController($scope) {
var field = $scope.field;
this.isLinkViewable = function() {
return EditableFieldsState.workPackage.links.timeEntries;
return field.resource.links.timeEntries;
};
this.getPath = function() {
return EditableFieldsState.workPackage.links.timeEntries.href;
return field.resource.links.timeEntries.href;
};
}
InplaceDisplaySpentTimeController.$inject = ['EditableFieldsState'];
InplaceDisplaySpentTimeController.$inject = ['$scope'];

@ -1,14 +1,14 @@
<span class="user-avatar--container">
<img class="user-avatar--avatar"
ng-if="customEditorController.getUserName() && customEditorController.getUser().props.avatar"
ng-src="{{customEditorController.getUser().props.avatar}}" alt="Avatar" title="{{customEditorController.getUserName()}}" />
ng-if="customEditorController.getUserName() && customEditorController.user.props.avatar"
ng-src="{{customEditorController.user.props.avatar}}" alt="Avatar" title="{{customEditorController.getUserName()}}" />
<span class="user-avatar--user-with-role">
<span class="user-avatar--user" ng-if="customEditorController.getUserName()">
<a ng-if="customEditorController.getUser().props.subtype !== 'Group'" ng-href="{{ customEditorController.userPath(customEditorController.getUser().props.id) }}" ng-bind="customEditorController.getUser().props.name"
<a ng-if="customEditorController.user.props.subtype !== 'Group'" ng-href="{{ customEditorController.userPath(customEditorController.user.props.id) }}" ng-bind="customEditorController.user.props.name"
class="user-field-user-link"/>
<span ng-if="customEditorController.getUser().props.subtype == 'Group'" ng-bind="customEditorController.getUser().props.name" class="user-field-user-link"/>
<span ng-if="customEditorController.user.props.subtype == 'Group'" ng-bind="customEditorController.user.props.name" class="user-field-user-link"/>
</span>
<span class="user-avatar--user" ng-if="!customEditorController.getUserName()"> - </span>
<span class="user-avatar--role" ng-if="customEditorController.getUser().props.role" ng-bind="customEditorController.getUser().props.role"/>
<span class="user-avatar--role" ng-if="customEditorController.user.props.role" ng-bind="customEditorController.user.props.role"/>
</span>
</span>

@ -49,16 +49,17 @@ function inplaceDisplayUser() {
}
function InplaceDisplayUserController($scope, PathHelper) {
var field = $scope.field;
this.userPath = PathHelper.staticUserPath;
this.getUser = function() {
return $scope.inplaceEditorDisplayPane.getReadValue();
};
this.user = field.text;
this.getUserName = function() {
var user = this.getUser();
var user = this.user;
if (user && user.props && (user.props.firstName || user.props.lastName)) {
return user.props.firstName + ' ' + user.props.lastName;
}
return null;
};
}
InplaceDisplayUserController.$inject = ['$scope', 'PathHelper'];

@ -1,11 +1,11 @@
<div class="version-wrapper">
<span ng-if="!displayPaneController.getReadValue()">-</span>
<span ng-if="displayPaneController.getReadValue() && customEditorController.isVersionLinkViewable()">
<a href="{{customEditorController.pathHelper.staticVersionPath(displayPaneController.getReadValue().props.id)}}">
{{displayPaneController.getReadValue().props.name}}
<span ng-if="!field.text">-</span>
<span ng-if="field.text && customEditorController.isVersionLinkViewable()">
<a ng-href="{{ customEditorController.versionLink }}">
{{field.text.props.name}}
</a>
</span>
<span ng-if="displayPaneController.getReadValue() && !customEditorController.isVersionLinkViewable()">
{{displayPaneController.getReadValue().props.name}}
<span ng-if="field.text && !customEditorController.isVersionLinkViewable()">
{{field.text.props.name}}
</span>
</div>

@ -35,23 +35,21 @@ function inplaceDisplayVersion() {
restrict: 'E',
transclude: true,
replace: true,
require: '^inplaceEditorDisplayPane',
templateUrl: '/components/inplace-edit/directives/field-display/display-version/' +
'display-version.directive.html',
controller: InplaceDisplayVersionController,
controllerAs: 'customEditorController',
controllerAs: 'customEditorController'
link: function(scope, element, attrs, displayPaneController) {
scope.displayPaneController = displayPaneController;
}
};
}
function InplaceDisplayVersionController($scope, PathHelper) {
this.pathHelper = PathHelper;
var field = $scope.field;
this.versionLink = field.text && PathHelper.staticVersionPath(field.text.props.id);
this.isVersionLinkViewable = function() {
var version = $scope.displayPaneController.getReadValue();
var version = field.text;
return version.links.definingProject && version.links.definingProject.href;
}
}

@ -37,14 +37,14 @@ function inplaceEditorDateRange(TimezoneService, I18n, $timeout, WorkPackageFiel
restrict: 'E',
transclude: true,
replace: true,
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-date-range/' +
'edit-date-range.directive.html',
controller: function() {},
controllerAs: 'customEditorController',
link: function(scope, element, attrs, fieldController) {
link: function(scope, element) {
var field = scope.field;
var customDateFormat = 'YYYY-MM-DD';
function getTitle(labelName) {
@ -56,8 +56,8 @@ function inplaceEditorDateRange(TimezoneService, I18n, $timeout, WorkPackageFiel
});
}
scope.startDate = fieldController.writeValue.startDate;
scope.endDate = fieldController.writeValue.dueDate;
scope.startDate = field.value.startDate;
scope.endDate = field.value.dueDate;
var form = element.parents('.inplace-edit--form'),
inputStart = element.find('.inplace-edit--date-range-start-date'),
inputEnd = element.find('.inplace-edit--date-range-end-date'),
@ -81,10 +81,10 @@ function inplaceEditorDateRange(TimezoneService, I18n, $timeout, WorkPackageFiel
startDatepicker = new Datepicker(divStart, inputStart, scope.startDate);
endDatepicker = new Datepicker(divEnd, inputEnd, scope.endDate);
startDatepicker.onChange = function(date) {
scope.startDate = fieldController.writeValue.startDate = date;
scope.startDate = field.value.startDate = date;
if (startDatepicker.prevDate.isAfter(endDatepicker.prevDate)) {
scope.startDateIsChanged = true;
scope.endDate = fieldController.writeValue.dueDate = scope.startDate;
scope.endDate = field.value.dueDate = scope.startDate;
endDatepicker.setDate(scope.endDate);
}
};
@ -93,10 +93,10 @@ function inplaceEditorDateRange(TimezoneService, I18n, $timeout, WorkPackageFiel
startDatepicker.onEdit();
};
endDatepicker.onChange = function(date) {
scope.endDate = fieldController.writeValue.dueDate = date;
scope.endDate = field.value.dueDate = date;
if (endDatepicker.prevDate.isBefore(startDatepicker.prevDate)) {
scope.endDateIsChanged = true;
scope.startDate = fieldController.writeValue.startDate = scope.endDate;
scope.startDate = field.value.startDate = scope.endDate;
startDatepicker.setDate(scope.startDate);
}
};

@ -1,9 +1,9 @@
<div class="inplace-edit--date">
<input ng-model="fieldController.writeValue"
ng-change="onEdit()"
ng-click="showDatepicker()"
title="{{ fieldController.editTitle }}"
class="inplace-edit--date"
type="text" />
<div class="inplace-edit--date-picker"></div>
<input ng-model="field.value"
ng-change="onEdit()"
ng-click="showDatepicker()"
title="{{ fieldController.editTitle }}"
class="inplace-edit--date"
type="text" />
<div class="inplace-edit--date-picker"></div>
</div>

@ -48,8 +48,9 @@ function inplaceEditorDate(EditableFieldsState, TimezoneService, $timeout, Datep
controller: function() {},
controllerAs: 'customEditorController',
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
link: function(scope, element) {
var field = scope.field;
var form = element.parents('.inplace-edit--form'),
input = element.find('.inplace-edit--date'),
datepickerContainer = element.find('.inplace-edit--date-picker'),
@ -59,13 +60,11 @@ function inplaceEditorDate(EditableFieldsState, TimezoneService, $timeout, Datep
form.scope().editPaneController.submit(false);
};
if(scope.fieldController.writeValue) {
scope.fieldController.writeValue = customFormattedDate(scope.fieldController.writeValue);
}
field.value = field.value && customFormattedDate(field.value);
datepicker = new Datepicker(datepickerContainer, input, scope.fieldController.writeValue);
datepicker = new Datepicker(datepickerContainer, input, field.value);
datepicker.onChange = function(date) {
scope.fieldController.writeValue = date;
field.value = date;
};
scope.onEdit = function() {
datepicker.onEdit();

@ -1,7 +1,7 @@
<div class="dropdown-wrapper">
<select
ng-disabled="fieldController.state.isBusy"
ng-model="fieldController.writeValue.props.href"
ng-model="field.value.props.href"
title="{{ fieldController.editTitle }}"
class="inplace-edit-select">
<option ng-repeat="item in customEditorController.allowedValues" value="{{ item.href }}"

@ -43,13 +43,13 @@ function inplaceEditorDropDown(EditableFieldsState, FocusHelper) {
controllerAs: 'customEditorController',
link: function(scope, element, attrs, fieldController) {
var selected = scope.field.format();
var field = scope.field;
var selected = field.format();
scope.fieldController = fieldController;
scope.customEditorController.selected = selected && selected.props && selected.props.name;
scope.fieldController.state.isBusy = true;
fieldController.state.isBusy = true;
scope.customEditorController.updateAllowedValues(scope.field.name).then(function() {
scope.customEditorController.updateAllowedValues(field.name).then(function() {
fieldController.state.isBusy = false;
if (!EditableFieldsState.forcedEditState) {

@ -5,4 +5,4 @@
ng-disabled="fieldController.state.isBusy"
ng-required="fieldController.isRequired"
title="{{ fieldController.editTitle }}"
ng-model="customEditorController.writeValue" />
ng-model="field.value" />

@ -35,33 +35,23 @@ function inplaceEditorDuration() {
restrict: 'E',
transclude: true,
replace: true,
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-duration/' +
'edit-duration.directive.html',
controllerAs: 'customEditorController',
controller: function() {},
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
if (fieldController.writeValue === null) {
scope.customEditorController.writeValue = null;
} else {
scope.customEditorController.writeValue = Number(
moment
.duration(fieldController.writeValue)
.asHours()
.toFixed(2)
);
link: function(scope) {
var field = scope.field;
if (field.value) {
field.value = Number(moment.duration(field.value).asHours().toFixed(2));
}
scope.$watch('customEditorController.writeValue', function(value) {
if (value === null) {
fieldController.writeValue = null;
} else {
// get rounded minutes so that we don't have to send 12.223000000003
// to the server
scope.$watch('field.value', function(value) {
if(value) {
var minutes = Number(moment.duration(value, 'hours').asMinutes().toFixed(2));
fieldController.writeValue = moment.duration(minutes, 'minutes');
field.value = moment.duration(minutes, 'minutes');
}
});
}

@ -9,7 +9,7 @@
name="value"
ng-disabled="fieldController.state.isBusy"
ng-required="fieldController.isRequired"
ng-model="fieldController.writeValue.raw"
ng-model="field.value.raw"
title="{{ fieldController.editTitle }}">
</textarea>
<div class="inplace-edit--preview" ng-if="customEditorController.isPreview && !fieldController.state.isBusy">

@ -42,7 +42,6 @@ function inplaceEditorWikiTextarea(AutoCompleteHelper, $timeout) {
controllerAs: 'customEditorController',
link: function(scope, element) {
scope.fieldController = scope.$parent.fieldController;
$timeout(function() {
AutoCompleteHelper.enableTextareaAutoCompletion(element.find('textarea'));
// set as dirty for the script to show a confirm on leaving the page
@ -76,7 +75,9 @@ function inplaceEditorWikiTextarea(AutoCompleteHelper, $timeout) {
inplaceEditorWikiTextarea.$inject = ['AutoCompleteHelper', '$timeout'];
function InplaceEditorWikiTextareaController($scope,$sce, TextileService, EditableFieldsState) {
function InplaceEditorWikiTextareaController($scope, $sce, TextileService, EditableFieldsState) {
var field = $scope.field;
this.isPreview = false;
this.previewHtml = '';
this.autocompletePath = '/work_packages/auto_complete.json';
@ -91,7 +92,7 @@ function InplaceEditorWikiTextareaController($scope,$sce, TextileService, Editab
$scope.fieldController.state.isBusy = true;
TextileService.renderWithWorkPackageContext(EditableFieldsState.workPackage.form,
$scope.fieldController.writeValue.raw)
field.value.raw)
.then(angular.bind(this, function(r) {
this.previewHtml = $sce.trustAsHtml(r.data);

@ -34,20 +34,6 @@ function inplaceEditorMainPane() {
return {
transclude: true,
replace: true,
scope: false,
templateUrl: '/components/inplace-edit/directives/main-pane/main-pane.directive.html',
controller: InplaceEditorMainPaneController,
controllerAs: 'mainPaneController'
templateUrl: '/components/inplace-edit/directives/main-pane/main-pane.directive.html'
};
}
function InplaceEditorMainPaneController($scope, $timeout) {
// controller is invoked before linker
$timeout(function() {
this.saveTitle = I18n.t('js.inplace.button_save', { attribute: $scope.field.name });
this.saveAndSendTitle = I18n.t('js.inplace.button_save_and_send',
{ attribute: $scope.field.name });
this.cancelTitle = I18n.t('js.inplace.button_cancel', { attribute: $scope.field.name });
});
}
InplaceEditorMainPaneController.$inject = ['$scope', '$timeout'];

@ -49,22 +49,13 @@ function workPackageField() {
function WorkPackageFieldController($scope, EditableFieldsState, inplaceEditField) {
this.state = EditableFieldsState;
$scope.field = new inplaceEditField(EditableFieldsState.workPackage, this.fieldName);
var field = $scope.field;
this.updateWriteValue = function() {
this.writeValue = EditableFieldsState.editAll.getFieldValue($scope.field.name)
|| _.cloneDeep($scope.field.getValue());
};
if ($scope.field.isEditable()) {
if (field.isEditable()) {
this.state.isBusy = false;
this.isEditing = this.state.forcedEditState;
this.updateWriteValue();
this.editTitle = I18n.t('js.inplace.button_edit', { attribute: $scope.field.getLabel() });
this.editTitle = I18n.t('js.inplace.button_edit', { attribute: field.getLabel() });
}
$scope.$watch('fieldController.writeValue', angular.bind(this, function (newValue) {
EditableFieldsState.editAll.addFieldValue(this.fieldName, newValue);
}));
}
WorkPackageFieldController.$inject = ['$scope', 'EditableFieldsState', 'inplaceEditField'];

@ -34,12 +34,21 @@ function inplaceEditField(WorkPackageFieldService) {
function Field(resource, name) {
this.resource = resource;
this.name = name;
this.value = this.getValue();
Object.defineProperties(this, {
text: {
get: function() {
return this.format();
}
}
});
}
_.forOwn(WorkPackageFieldService, function (method, name) {
Field.prototype[name] = function () {
return method(this.resource, this.name);
}
_.forOwn(WorkPackageFieldService, function (property, name) {
Field.prototype[name] = _.isFunction(property) && function () {
return property(this.resource, this.name);
} || property;
});
return Field;

@ -30,9 +30,6 @@ angular
.module('openproject.services')
.service('WorkPackageFieldService', WorkPackageFieldService);
WorkPackageFieldService.$inject = ['$q', '$http', 'I18n', 'WorkPackagesHelper', 'HookService',
'EditableFieldsState'];
function WorkPackageFieldService($q, $http, I18n, WorkPackagesHelper, HookService,
EditableFieldsState ) {
@ -414,3 +411,5 @@ function WorkPackageFieldService($q, $http, I18n, WorkPackagesHelper, HookServi
return WorkPackageFieldService;
}
WorkPackageFieldService.$inject = ['$q', '$http', 'I18n', 'WorkPackagesHelper', 'HookService',
'EditableFieldsState'];

@ -1 +1 @@
<span ng-bind="displayPaneController.getReadValue()"></span>
<span ng-bind="field.text"></span>

@ -1 +1 @@
<op-date date-value="displayPaneController.getReadValue().startDate" no-date-text="displayPaneController.getReadValue().noStartDate"></op-date>&nbsp;&nbsp;-&nbsp;&nbsp;<op-date date-value="displayPaneController.getReadValue().dueDate" no-date-text="displayPaneController.getReadValue().noEndDate"></op-date>
<op-date date-value="field.text.startDate" no-date-text="field.text.noStartDate"></op-date>&nbsp;&nbsp;-&nbsp;&nbsp;<op-date date-value="field.text.dueDate" no-date-text="field.text.noEndDate"></op-date>

@ -1 +1 @@
<op-date date-value="displayPaneController.getReadValue()" no-date-text="displayPaneController.placeholder"></op-date>
<op-date date-value="field.text" no-date-text="field.defaultPlaceholder"></op-date>

@ -1 +1 @@
<span ng-bind="displayPaneController.getReadValue().props.name || displayPaneController.getReadValue().props.value || displayPaneController.placeholder"></span>
<span ng-bind="field.text.props.name || field.text.props.value || field.defaultPlaceholder"></span>

@ -1,2 +1,2 @@
<span ng-if="displayPaneController.isReadValueEmpty()" ng-bind="displayPaneController.placeholder"></span>
<span ng-if="!displayPaneController.isReadValueEmpty()" ng-bind="displayPaneController.getReadValue()"></span>
<span ng-if="field.isEmpty()" ng-bind="field.defaultPlaceholder"></span>
<span ng-if="!field.isEmpty()" ng-bind="field.text"></span>

@ -1 +1 @@
<span ng-bind-html="displayPaneController.getReadValue().html || displayPaneController.placeholder"></span>
<span ng-bind-html="field.text.html || field.defaultPlaceholder"></span>

@ -5,6 +5,6 @@
name="value"
ng-disabled="fieldController.state.isBusy"
title="{{ fieldController.editTitle }}"
ng-model="fieldController.writeValue" />
ng-model="field.value" />
<label for="checkbox-switch-{{ field.name }}" title="{{ fieldController.editTitle }}"></label>
</div>

@ -5,4 +5,4 @@
ng-disabled="fieldController.state.isBusy"
ng-required="fieldController.isRequired"
title="{{ fieldController.editTitle }}"
ng-model="fieldController.writeValue" />
ng-model="field.value" />

@ -4,4 +4,4 @@
ng-disabled="fieldController.state.isBusy"
ng-required="fieldController.isRequired"
title="{{ fieldController.editTitle }}"
ng-model="fieldController.writeValue" />
ng-model="field.value" />

@ -4,4 +4,4 @@
ng-disabled="fieldController.state.isBusy"
ng-required="fieldController.isRequired"
title="{{ fieldController.editTitle }}"
ng-model="fieldController.writeValue" />
ng-model="field.value" />

@ -3,6 +3,6 @@
name="value"
ng-disabled="fieldController.state.isBusy"
ng-required="fieldController.isRequired"
ng-model="fieldController.writeValue.raw"
ng-model="field.value.raw"
title="{{ fieldController.editTitle }}">
</textarea>

@ -40,10 +40,14 @@ module.exports = function(
NotificationsService) {
function commentFieldDirectiveController($scope, $element) {
var field = {};
$scope.field = field;
var ctrl = this;
ctrl.state = EditableFieldsState;
ctrl.field = 'activity-comment';
ctrl.writeValue = { raw: '' };
field.value = ctrl.writeValue;
ctrl.editTitle = I18n.t('js.inplace.button_edit', { attribute: I18n.t('js.label_comment') });
ctrl.placeholder = I18n.t('js.label_add_comment_title');

@ -66,19 +66,9 @@ module.exports = function($q, $rootScope, NotificationsService) {
editAll: {
focusField: 'subject',
fieldValues: {},
addFieldValue: function (field, value) {
this.fieldValues[field] = value;
},
getFieldValue: function (field) {
return this.fieldValues[field];
},
cancel: function () {
this.stop();
this.fieldValues = {};
},
get allowed() {

@ -59,15 +59,15 @@ describe('Inplace editor drop-down directive', function() {
scope.field = {
getAllowedValues: sinon.stub().returns(allowedValuePromise),
format: sinon.stub().returns({ props: { name: allowedValues[0].name } }),
isRequired: sinon.stub().returns(true)
};
isRequired: sinon.stub().returns(true),
value: { props: { href: allowedValues[0].href } }
};
// severing dependency from the work package field directive as described by
// http://busypeoples.github.io/post/testing-angularjs-hierarchical-directives
element = angular.element(html);
var workPackageFieldController = {
state: { isBusy: false },
writeValue: { props: { href: allowedValues[0].href } }
state: { isBusy: false }
};
element.data('$workPackageFieldController', workPackageFieldController);

@ -100,22 +100,5 @@ describe('EditableFieldsState service', function () {
it('is not allowed if th WP update action does not exist', function () {
expect(eAll.allowed).to.be.false;
});
describe('field value storage', function () {
var field = 'my_field', value = 'my_val';
beforeEach(function () {
eAll.addFieldValue(field, value);
});
it('saves and retrieves the correct value', function () {
expect(eAll.getFieldValue(field)).to.equal(value);
});
it('clears stored values when cancel is called', function () {
eAll.cancel();
expect(eAll.getFieldValue(field)).to.be.falsy;
});
});
});
});

Loading…
Cancel
Save