Ensures that FormController hierarchy is maintained with infinite editors

This commit is contained in:
Shannon
2020-07-17 12:21:33 +10:00
parent 0afdd69416
commit 4d06094696
11 changed files with 146 additions and 82 deletions

View File

@@ -137,7 +137,7 @@
// This directive allows for us to run a custom $compile for the view within the repeater which allows
// us to maintain a $scope hierarchy with the rendered view based on the $scope that initiated the
// infinite editing. The retain the $scope hiearchy a special $parentScope property is passed in to the model.
function EditorRepeaterDirective($http, $templateCache, $compile) {
function EditorRepeaterDirective($http, $templateCache, $compile, angularHelper) {
function link(scope, el, attr, ctrl) {
var editor = scope && scope.$parent ? scope.$parent.model : null;
@@ -159,14 +159,24 @@
});
// NOTE: the 'model' name here directly affects the naming convention used in infinite editors, this why you access the model
// like $scope.model.If this is changed, everything breaks.This is because we are entirely reliant upon ng - include and inheriting $scopes.
// like $scope.model.If this is changed, everything breaks.This is because we are entirely reliant upon ng-include and inheriting $scopes.
// by default without a $parentScope used for infinite editing the 'model' propety will be set because the view creates the scopes in
// ng-repeat by ng-repeat="model in editors"
templateScope.model = editor;
element.html(response.data);
element.show();
$compile(element.contents())(templateScope);
// if a parentForm is supplied then we can link them but to do that we need to inject a top level form
if (editor.$parentForm) {
element.html("<ng-form name='infiniteEditorForm'>" + response.data + "</ng-form>");
}
$compile(element)(templateScope);
// if a parentForm is supplied then we can link them
if (editor.$parentForm) {
editor.$parentForm.$addControl(templateScope.infiniteEditorForm);
}
});
}

View File

@@ -280,7 +280,7 @@ Opens an overlay to show a custom YSOD. </br>
templateScope.model = scope.model;
element.html(response.data);
element.show();
$compile(element.contents())(templateScope);
$compile(element)(templateScope);
});
}
}

View File

@@ -1 +1 @@
<umb-block-list-property-editor property-form="propertyForm" model="model"/>
<umb-block-list-property-editor model="model"/>

View File

@@ -1,16 +1,14 @@
<ng-form name="blockRowForm" val-server-match="{ 'contains' : block.content.key }">
<div class="blockelement-inlineblock-editor"
ng-controller="Umbraco.PropertyEditors.BlockEditor.InlineBlockEditor as vm"
ng-class="{ '--error': blockRowForm.$invalid }">
<button type="button" class="btn-reset umb-outline blockelement__draggable-element"
ng-click="vm.openBlock(block)"
ng-focus="block.focus">
<span class="caret"></span>
<i class="icon {{block.content.icon}}"></i>
<span>{{block.label}}</span>
</button>
<div class="blockelement-inlineblock-editor__inner" ng-class="{'--singleGroup':block.content.variants[0].tabs.length === 1}" ng-if="block.active === true">
<umb-element-editor-content model="block.content"></umb-element-editor-content>
</div>
<div class="blockelement-inlineblock-editor"
ng-controller="Umbraco.PropertyEditors.BlockEditor.InlineBlockEditor as vm"
ng-class="{ '--error': blockRowForm.$invalid }">
<button type="button" class="btn-reset umb-outline blockelement__draggable-element"
ng-click="vm.openBlock(block)"
ng-focus="block.focus">
<span class="caret"></span>
<i class="icon {{block.content.icon}}"></i>
<span>{{block.label}}</span>
</button>
<div class="blockelement-inlineblock-editor__inner" ng-class="{'--singleGroup':block.content.variants[0].tabs.length === 1}" ng-if="block.active === true">
<umb-element-editor-content model="block.content"></umb-element-editor-content>
</div>
</ng-form>
</div>

View File

@@ -1,10 +1,8 @@
<ng-form name="blockRowForm" val-server-match="{ 'contains' : block.content.key }">
<button type="button" class="btn-reset umb-outline blockelement-labelblock-editor blockelement__draggable-element"
ng-click="api.editBlock(block, block.hideContentInOverlay, index)"
ng-focus="block.focus"
ng-class="{ '--active': block.active, '--error': blockRowForm.$invalid }"
val-server-property-class="">
<i class="icon {{block.content.icon}}"></i>
<span>{{block.label}}</span>
</button>
</ng-form>
<button type="button" class="btn-reset umb-outline blockelement-labelblock-editor blockelement__draggable-element"
ng-click="api.editBlock(block, block.hideContentInOverlay, index, parentForm)"
ng-focus="block.focus"
ng-class="{ '--active': block.active, '--error': blockRowForm.$invalid }"
val-server-property-class="">
<i class="icon {{block.content.icon}}"></i>
<span>{{block.label}}</span>
</button>

View File

@@ -18,49 +18,10 @@
<div class="__plus" ng-style="{'left':inlineCreateButtonCtrl.plusPosX}">+</div>
</button>
<div class="umb-block-list__block" ng-class="{'--active':layout.$block.active}">
<umb-block-list-scoped-block
ng-if="layout.$block.config.stylesheet"
class="umb-block-list__block--content blockelement__draggable-element"
view="{{layout.$block.view}}"
stylesheet="/{{::layout.$block.config.stylesheet}}"
api="vm.blockEditorApi"
block="layout.$block"
index="$index">
</umb-block-list-scoped-block>
<umb-block-list-block
ng-if="!layout.$block.config.stylesheet"
class="umb-block-list__block--content"
view="{{layout.$block.view}}"
api="vm.blockEditorApi"
block="layout.$block"
index="$index">
</umb-block-list-block>
<div class="umb-block-list__block--actions">
<button type="button" class="btn-reset umb-outline action --settings" localize="title" title="actions_editSettings"
ng-click="vm.blockEditorApi.openSettingsForBlock(layout.$block, $index);"
ng-if="layout.$block.showSettings === true">
<i class="icon icon-settings" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_settings">Settings</localize>
</span>
</button>
<button type="button" class="btn-reset umb-outline action --copy" localize="title" title="actions_copy" ng-click="vm.blockEditorApi.copyBlock(layout.$block);" ng-if="layout.$block.showCopy === true">
<i class="icon icon-documents" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_copy">Copy</localize>
</span>
</button>
<button type="button" class="btn-reset umb-outline action --delete" localize="title" title="actions_delete" ng-click="vm.blockEditorApi.requestDeleteBlock(layout.$block);">
<i class="icon icon-trash" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_delete">Delete</localize>
</span>
</button>
</div>
</div>
<umb-block-list-row block-editor-api="vm.blockEditorApi"
layout="layout"
index="$index">
</umb-block-list-row>
</div>
</div>

View File

@@ -0,0 +1,45 @@
<ng-form name="vm.blockRowForm" val-server-match="{ 'contains' : vm.layout.$block.content.key }">
<div class="umb-block-list__block" ng-class="{'--active':vm.layout.$block.active}">
<umb-block-list-scoped-block ng-if="vm.layout.$block.config.stylesheet"
class="umb-block-list__block--content blockelement__draggable-element"
view="{{vm.layout.$block.view}}"
stylesheet="/{{::vm.layout.$block.config.stylesheet}}"
api="vm.blockEditorApi"
block="vm.layout.$block"
index="vm.index"
parent-form="vm.blockRowForm">
</umb-block-list-scoped-block>
<umb-block-list-block ng-if="!vm.layout.$block.config.stylesheet"
class="umb-block-list__block--content"
view="{{vm.layout.$block.view}}"
api="vm.blockEditorApi"
block="vm.layout.$block"
index="vm.index"
parent-form="vm.blockRowForm">
</umb-block-list-block>
<div class="umb-block-list__block--actions">
<button type="button" class="btn-reset umb-outline action --settings" localize="title" title="actions_editSettings"
ng-click="vm.blockEditorApi.openSettingsForBlock(vm.layout.$block, vm.index);"
ng-if="vm.layout.$block.showSettings === true">
<i class="icon icon-settings" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_settings">Settings</localize>
</span>
</button>
<button type="button" class="btn-reset umb-outline action --copy" localize="title" title="actions_copy" ng-click="vm.blockEditorApi.copyBlock(vm.layout.$block);" ng-if="vm.layout.$block.showCopy === true">
<i class="icon icon-documents" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_copy">Copy</localize>
</span>
</button>
<button type="button" class="btn-reset umb-outline action --delete" localize="title" title="actions_delete" ng-click="vm.blockEditorApi.requestDeleteBlock(vm.layout.$block);">
<i class="icon icon-trash" aria-hidden="true"></i>
<span class="sr-only">
<localize key="general_delete">Delete</localize>
</span>
</button>
</div>
</div>
</ng-form>

View File

@@ -17,10 +17,10 @@
controller: BlockListController,
controllerAs: "vm",
bindings: {
model: "=",
propertyForm: "="
model: "="
},
require: {
propertyForm: "^form",
umbProperty: "?^umbProperty",
umbVariantContent: '?^^umbVariantContent',
umbVariantContentEditors: '?^^umbVariantContentEditors',
@@ -28,7 +28,7 @@
}
});
function BlockListController($scope, editorService, clipboardService, localizationService, overlayService, blockEditorService, udiService, serverValidationManager) {
function BlockListController($scope, editorService, clipboardService, localizationService, overlayService, blockEditorService, udiService, serverValidationManager, angularHelper) {
var unsubscribe = [];
var modelObject;
@@ -65,6 +65,16 @@
vm.$onInit = function() {
if (!vm.umbVariantContent) {
// not found, then fallback to searching the scope chain, this may be needed when DOM inheritance isn't maintained but scope
// inheritance is (i.e.infinite editing)
var found = angularHelper.traverseScopeChain($scope, s => s && s.vm && s.vm.constructor.name === "umbVariantContentController");
vm.umbVariantContent = found ? found.vm : null;
if (!vm.umbVariantContent) {
throw "Could not find umbVariantContent in the $scope chain";
}
}
// set the onValueChanged callback, this will tell us if the block list model changed on the server
// once the data is submitted. If so we need to re-initialize
vm.model.onValueChanged = onServerValueChanged;
@@ -258,7 +268,7 @@
blockObject.active = true;
}
function editBlock(blockObject, openSettings, blockIndex) {
function editBlock(blockObject, openSettings, blockIndex, parentForm) {
// this must be set
if (blockIndex === undefined) {
@@ -289,6 +299,7 @@
var blockEditorModel = {
$parentScope: $scope, // pass in a $parentScope, this maintains the scope inheritance in infinite editing
$parentForm: parentForm || vm.propertyForm, // pass in a $parentForm, this maintains the FormController hierarchy with the infinite editing view (if it contains a form)
hideContent: blockObject.hideContentInOverlay,
openSettings: openSettings === true,
liveEditing: liveEditing,
@@ -345,6 +356,8 @@
var amountOfAvailableTypes = vm.availableBlockTypes.length;
var blockPickerModel = {
$parentScope: $scope, // pass in a $parentScope, this maintains the scope inheritance in infinite editing
$parentForm: vm.propertyForm, // pass in a $parentForm, this maintains the FormController hierarchy with the infinite editing view (if it contains a form)
availableItems: vm.availableBlockTypes,
title: vm.labels.grid_addElement,
orderBy: "$index",
@@ -378,7 +391,7 @@
if (inlineEditing === true) {
activateBlock(vm.layout[createIndex].$block);
} else if (inlineEditing === false && vm.layout[createIndex].$block.hideContentInOverlay !== true) {
editBlock(vm.layout[createIndex].$block, false, createIndex);
editBlock(vm.layout[createIndex].$block, false, createIndex, blockPickerModel.$parentForm);
}
}
}
@@ -521,6 +534,7 @@
});
}
// TODO: We'll need to pass in a parentForm here too
function openSettingsForBlock(block, blockIndex) {
editBlock(block, true, blockIndex);
}

View File

@@ -18,7 +18,8 @@
view: "@",
block: "=",
api: "<",
index: "<"
index: "<",
parentForm: "<"
}
}
);
@@ -32,6 +33,7 @@
$scope.block = model.block;
$scope.api = model.api;
$scope.index = model.index;
$scope.parentForm = model.parentForm;
};
// We need to watch for changes on primitive types and upate the $scope values.

View File

@@ -0,0 +1,34 @@
(function () {
"use strict";
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbBlockListRow
* @description
* renders each row for the block list editor
*/
angular
.module("umbraco")
.component("umbBlockListRow", {
templateUrl: 'views/propertyeditors/blocklist/umb-block-list-row.html',
controller: BlockListRowController,
controllerAs: "vm",
bindings: {
blockEditorApi: "<",
layout: "<",
index: "<"
}
}
);
function BlockListRowController($scope) {
var vm = this;
vm.$onInit = function () {
};
}
})();

View File

@@ -20,7 +20,8 @@
view: "@",
block: "=",
api: "<",
index: "<"
index: "<",
parentForm: "<"
}
}
);
@@ -34,6 +35,7 @@
$scope.block = model.block;
$scope.api = model.api;
$scope.index = model.index;
$scope.parentForm = model.parentForm;
var shadowRoot = $element[0].attachShadow({mode:'open'});
shadowRoot.innerHTML = `