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 @@
+
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 @@
-
-
-
+