From 91653f9c3678b3a3fbb02c7b9df91642e3b8b9f3 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Wed, 18 Aug 2021 14:40:38 +0300 Subject: [PATCH] refactor(stacks): move custom templates selector to component (#5418) * feat(app): introduce web-editor form component * refactor(stacks): move custom templates selector to component * fix(stacks): validate form for template --- .../code-editor/codeEditorController.js | 8 +- .../custom-template-selector.controller.js | 36 ++++++ .../custom-template-selector.html | 32 +++++ .../custom-template-selector/index.js | 16 +++ .../components/form-components/index.js | 5 + .../form-components/web-editor-form/index.js | 19 +++ .../web-editor-form.controller.js | 12 ++ .../web-editor-form/web-editor-form.html | 13 ++ ...it-form-additional-file-item.controller.js | 3 - app/portainer/components/index.js | 3 +- .../stacks/create/createStackController.js | 40 +++---- .../views/stacks/create/createstack.html | 112 +++++------------- 12 files changed, 190 insertions(+), 109 deletions(-) create mode 100644 app/portainer/components/custom-template-selector/custom-template-selector.controller.js create mode 100644 app/portainer/components/custom-template-selector/custom-template-selector.html create mode 100644 app/portainer/components/custom-template-selector/index.js create mode 100644 app/portainer/components/form-components/index.js create mode 100644 app/portainer/components/form-components/web-editor-form/index.js create mode 100644 app/portainer/components/form-components/web-editor-form/web-editor-form.controller.js create mode 100644 app/portainer/components/form-components/web-editor-form/web-editor-form.html diff --git a/app/portainer/components/code-editor/codeEditorController.js b/app/portainer/components/code-editor/codeEditorController.js index 66e24666f..26363c108 100644 --- a/app/portainer/components/code-editor/codeEditorController.js +++ b/app/portainer/components/code-editor/codeEditorController.js @@ -1,12 +1,18 @@ angular.module('portainer.app').controller('CodeEditorController', function CodeEditorController($document, CodeMirrorService, $scope) { var ctrl = this; + this.$onChanges = function $onChanges({ value }) { + if (value && value.currentValue && ctrl.editor) { + ctrl.editor.setValue(value.currentValue); + } + }; + this.$onInit = function () { $document.ready(function () { var editorElement = $document[0].getElementById(ctrl.identifier); ctrl.editor = CodeMirrorService.applyCodeMirrorOnElement(editorElement, ctrl.yml, ctrl.readOnly); if (ctrl.onChange) { - ctrl.editor.on('change', (...args) => $scope.$apply(() => ctrl.onChange(...args))); + ctrl.editor.on('change', (...args) => $scope.$evalAsync(() => ctrl.onChange(...args))); } if (ctrl.value) { ctrl.editor.setValue(ctrl.value); diff --git a/app/portainer/components/custom-template-selector/custom-template-selector.controller.js b/app/portainer/components/custom-template-selector/custom-template-selector.controller.js new file mode 100644 index 000000000..04f09be50 --- /dev/null +++ b/app/portainer/components/custom-template-selector/custom-template-selector.controller.js @@ -0,0 +1,36 @@ +class CustomTemplateSelectorController { + /* @ngInject */ + constructor($async, CustomTemplateService, Notifications) { + Object.assign(this, { $async, CustomTemplateService, Notifications }); + + this.selectedTemplate = null; + this.templates = null; + } + + async handleChangeTemplate(templateId) { + this.selectedTemplate = this.templates.find((t) => t.id === templateId); + this.onChange(templateId); + } + + $onChanges({ value }) { + if (value && value.currentValue && this.templates) { + this.handleChangeTemplate(value.currentValue); + } + } + + $onInit() { + return this.$async(async () => { + try { + const templates = await this.CustomTemplateService.customTemplates(this.stackType); + this.templates = templates.map((template) => ({ ...template, label: `${template.Title} - ${template.Description}` })); + if (this.value) { + this.handleChangeTemplate(this.value); + } + } catch (err) { + this.Notifications.error('Failure', err, 'Unable to retrieve Custom Templates'); + } + }); + } +} + +export default CustomTemplateSelectorController; diff --git a/app/portainer/components/custom-template-selector/custom-template-selector.html b/app/portainer/components/custom-template-selector/custom-template-selector.html new file mode 100644 index 000000000..26c74ed8a --- /dev/null +++ b/app/portainer/components/custom-template-selector/custom-template-selector.html @@ -0,0 +1,32 @@ +
+
+ +
+ + No custom templates are available. Head over to the custom template view to create one. +
+
+ + +
+
+ Information +
+
+
+
+
+
+
+ +
diff --git a/app/portainer/components/custom-template-selector/index.js b/app/portainer/components/custom-template-selector/index.js new file mode 100644 index 000000000..6e677618d --- /dev/null +++ b/app/portainer/components/custom-template-selector/index.js @@ -0,0 +1,16 @@ +import angular from 'angular'; +import controller from './custom-template-selector.controller.js'; + +export const customTemplateSelector = { + templateUrl: './custom-template-selector.html', + controller, + bindings: { + newTemplatePath: '@', + stackType: '<', + + value: '<', + onChange: '<', + }, +}; + +angular.module('portainer.app').component('customTemplateSelector', customTemplateSelector); diff --git a/app/portainer/components/form-components/index.js b/app/portainer/components/form-components/index.js new file mode 100644 index 000000000..532ca3c33 --- /dev/null +++ b/app/portainer/components/form-components/index.js @@ -0,0 +1,5 @@ +import angular from 'angular'; + +import { webEditorForm } from './web-editor-form'; + +export default angular.module('portainer.app.components.form', []).component('webEditorForm', webEditorForm).name; diff --git a/app/portainer/components/form-components/web-editor-form/index.js b/app/portainer/components/form-components/web-editor-form/index.js new file mode 100644 index 000000000..bc1c80a07 --- /dev/null +++ b/app/portainer/components/form-components/web-editor-form/index.js @@ -0,0 +1,19 @@ +import controller from './web-editor-form.controller.js'; + +export const webEditorForm = { + templateUrl: './web-editor-form.html', + controller, + + bindings: { + identifier: '@', + placeholder: '@', + yml: '<', + value: '<', + + onChange: '<', + }, + + transclude: { + description: '?editorDescription', + }, +}; diff --git a/app/portainer/components/form-components/web-editor-form/web-editor-form.controller.js b/app/portainer/components/form-components/web-editor-form/web-editor-form.controller.js new file mode 100644 index 000000000..6a5f3305e --- /dev/null +++ b/app/portainer/components/form-components/web-editor-form/web-editor-form.controller.js @@ -0,0 +1,12 @@ +class WebEditorFormController { + /* @ngInject */ + constructor() { + this.editorUpdate = this.editorUpdate.bind(this); + } + + editorUpdate(cm) { + this.onChange(cm.getValue()); + } +} + +export default WebEditorFormController; diff --git a/app/portainer/components/form-components/web-editor-form/web-editor-form.html b/app/portainer/components/form-components/web-editor-form/web-editor-form.html new file mode 100644 index 000000000..2dc4a9447 --- /dev/null +++ b/app/portainer/components/form-components/web-editor-form/web-editor-form.html @@ -0,0 +1,13 @@ + +
+ Web editor +
+
+ +
+
+
+ +
+
+
diff --git a/app/portainer/components/forms/git-form/git-form-additional-files-panel/git-form-additional-file-item/git-form-additional-file-item.controller.js b/app/portainer/components/forms/git-form/git-form-additional-files-panel/git-form-additional-file-item/git-form-additional-file-item.controller.js index f712c9b61..dd1097b15 100644 --- a/app/portainer/components/forms/git-form/git-form-additional-files-panel/git-form-additional-file-item/git-form-additional-file-item.controller.js +++ b/app/portainer/components/forms/git-form/git-form-additional-files-panel/git-form-additional-file-item/git-form-additional-file-item.controller.js @@ -1,7 +1,4 @@ class GitFormAdditionalFileItemController { - /* @ngInject */ - constructor() {} - onChangePath(value) { const fieldIsInvalid = typeof value === 'undefined'; if (fieldIsInvalid) { diff --git a/app/portainer/components/index.js b/app/portainer/components/index.js index 6291cb0b5..bef46f4d7 100644 --- a/app/portainer/components/index.js +++ b/app/portainer/components/index.js @@ -2,5 +2,6 @@ import angular from 'angular'; import gitFormModule from './forms/git-form'; import porAccessManagementModule from './accessManagement'; +import formComponentsModule from './form-components'; -export default angular.module('portainer.app.components', [gitFormModule, porAccessManagementModule]).name; +export default angular.module('portainer.app.components', [gitFormModule, porAccessManagementModule, formComponentsModule]).name; diff --git a/app/portainer/views/stacks/create/createStackController.js b/app/portainer/views/stacks/create/createStackController.js index 168179142..fd4d66bb1 100644 --- a/app/portainer/views/stacks/create/createStackController.js +++ b/app/portainer/views/stacks/create/createStackController.js @@ -1,6 +1,6 @@ import angular from 'angular'; -import _ from 'lodash-es'; import uuidv4 from 'uuid/v4'; + import { AccessControlFormData } from '../../../components/accessControlForm/porAccessControlFormModel'; angular @@ -25,6 +25,8 @@ angular WebhookHelper, clipboard ) { + $scope.onChangeTemplateId = onChangeTemplateId; + $scope.formValues = { Name: '', StackFileContent: '', @@ -60,9 +62,9 @@ angular } }; - $scope.$on('$destroy', function() { + $scope.$on('$destroy', function () { $scope.state.isEditorDirty = false; - }) + }); $scope.onChangeFormValues = onChangeFormValues; @@ -219,8 +221,8 @@ angular }); }; - $scope.editorUpdate = function (cm) { - $scope.formValues.StackFileContent = cm.getValue(); + $scope.onChangeFileContent = function onChangeFileContent(value) { + $scope.formValues.StackFileContent = value; $scope.state.editorYamlValidationError = StackHelper.validateYAML($scope.formValues.StackFileContent, $scope.containerNames); $scope.state.isEditorDirty = true; }; @@ -244,15 +246,18 @@ angular } }; - $scope.onChangeTemplate = async function onChangeTemplate(template) { - try { - $scope.formValues.StackFileContent = undefined; - $scope.selectedTemplate = template; - $scope.formValues.StackFileContent = await CustomTemplateService.customTemplateFile(template.Id); - } catch (err) { - Notifications.error('Failure', err, 'Unable to retrieve Custom Template file'); - } - }; + function onChangeTemplateId(templateId) { + return $async(async () => { + try { + $scope.state.templateId = templateId; + + const fileContent = await CustomTemplateService.customTemplateFile(templateId); + $scope.onChangeFileContent(fileContent); + } catch (err) { + this.Notifications.error('Failure', err, 'Unable to retrieve Custom Template file'); + } + }); + } async function initView() { var endpointMode = $scope.applicationState.endpoint.mode; @@ -261,13 +266,6 @@ angular $scope.state.StackType = 1; } - try { - const templates = await CustomTemplateService.customTemplates($scope.state.StackType); - $scope.templates = _.map(templates, (template) => ({ ...template, label: `${template.Title} - ${template.Description}` })); - } catch (err) { - Notifications.error('Failure', err, 'Unable to retrieve Custom Templates'); - } - try { const endpoint = EndpointProvider.currentEndpoint(); $scope.composeSyntaxMaxVersion = endpoint.ComposeSyntaxMaxVersion; diff --git a/app/portainer/views/stacks/create/createstack.html b/app/portainer/views/stacks/create/createstack.html index abb4c935b..db9b28dda 100644 --- a/app/portainer/views/stacks/create/createstack.html +++ b/app/portainer/views/stacks/create/createstack.html @@ -81,31 +81,7 @@ - -
-
- Web editor -
-
- - You can get more information about Compose file format in the official documentation. - -
{{ state.editorYamlValidationError }}
-
-
-
- -
-
-
- +
@@ -138,63 +114,34 @@ auto-update="true" show-auth-explanation="true" > - -
-
- -
- - - No custom templates are available. Head over to the custom template view to create one. - + + + + + + + You can get more information about Compose file format in the official documentation. + +
+ {{ state.editorYamlValidationError }}
-
- -
-
- Information -
-
-
-
-
-
-
- - -
-
- Web editor -
-
-
{{ state.editorYamlValidationError }}
-
-
-
- -
-
-
-
- + + + @@ -211,9 +158,8 @@ class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !createStackForm.$valid - || (state.Method === 'editor' && (!formValues.StackFileContent || state.editorYamlValidationError)) + || ((state.Method === 'editor' || state.Method === 'template') && (!formValues.StackFileContent || state.editorYamlValidationError)) || (state.Method === 'upload' && (!formValues.StackFile || state.uploadYamlValidationError)) - || (state.Method === 'template' && (!formValues.StackFileContent || !selectedTemplate || state.editorYamlValidationError)) || (state.Method === 'repository' && ((!formValues.RepositoryURL || !formValues.ComposeFilePathInRepository) || (formValues.RepositoryAuthentication && !formValues.RepositoryPassword))) || !formValues.Name" ng-click="deployStack()"