use an isolated scope
This commit is contained in:
@@ -58,7 +58,7 @@
|
||||
function mapElementTypeValues(fromModel, toModel) {
|
||||
var fromVariant = fromModel.variants[0];
|
||||
var toVariant = toModel.variants[0];
|
||||
|
||||
|
||||
for (var t = 0; t < fromVariant.tabs.length; t++) {
|
||||
var fromTab = fromVariant.tabs[t];
|
||||
var toTab = toVariant.tabs[t];
|
||||
@@ -72,12 +72,39 @@
|
||||
}
|
||||
|
||||
|
||||
function getBlockLabel(blockModel) {
|
||||
if(blockModel.labelInterpolator) {
|
||||
// We are just using the contentModel, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.)
|
||||
return blockModel.labelInterpolator(blockModel.contentModel);
|
||||
}
|
||||
return blockModel.contentTypeName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to create a scoped watcher for a property on a blockModel.
|
||||
*/
|
||||
function createPropWatcher(blockModel, prop) {
|
||||
|
||||
return function() {
|
||||
|
||||
// sync data:
|
||||
blockModel.contentModel[prop.alias] = prop.value;
|
||||
|
||||
// regenerate label.
|
||||
// TODO: could use a debounce.
|
||||
blockModel.label = getBlockLabel(blockModel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc factory
|
||||
* @name umbraco.factory.BlockEditorModelObject
|
||||
* @description A model object used to handle Block Editor data.
|
||||
**/
|
||||
function BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations) {
|
||||
function BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations, propertyScope) {
|
||||
|
||||
if (!propertyModelValue) {
|
||||
throw new Error("propertyModelValue cannot be undefined, to ensure we keep the binding to the angular model we need minimum an empty object.");
|
||||
@@ -93,6 +120,13 @@
|
||||
|
||||
this.scaffolds = [];
|
||||
|
||||
this.watchers = [];
|
||||
|
||||
this.isolatedScope = propertyScope.$new(true);
|
||||
this.isolatedScope.blockModels = {};
|
||||
|
||||
this.isolatedScope.$on("$destroy", this.onDestroyed.bind(this));
|
||||
|
||||
};
|
||||
|
||||
BlockEditorModelObject.prototype = {
|
||||
@@ -164,17 +198,23 @@
|
||||
|
||||
var udi = layoutEntry.udi;
|
||||
|
||||
var contentModel = this._getContentByUdi(udi);
|
||||
var contentModel = this._getDataByUdi(udi);
|
||||
|
||||
if (contentModel === null) {
|
||||
console.error("Couldnt find content model of "+udi)
|
||||
return null;
|
||||
}
|
||||
|
||||
var blockConfiguration = this.getBlockConfiguration(contentModel.contentTypeAlias);
|
||||
|
||||
if (blockConfiguration === null) {
|
||||
console.error("The block entry of "+udi+" is not begin initialized cause its contentTypeAlias is not allowed for this PropertyEditor")
|
||||
// This is not an allowed block type, therefor we return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
var blockModel = {};
|
||||
blockModel.key = String.CreateGuid();
|
||||
blockModel.key = String.CreateGuid().replace(/-/g, "");
|
||||
blockModel.config = angular.copy(blockConfiguration);
|
||||
blockModel.labelInterpolator = $interpolate(blockModel.config.label);
|
||||
|
||||
@@ -191,14 +231,51 @@
|
||||
|
||||
blockModel.contentModel = contentModel;
|
||||
blockModel.layoutModel = layoutEntry;
|
||||
blockModel.watchers = [];
|
||||
|
||||
// TODO: settings
|
||||
|
||||
// Add blockModel to our isolated scope to enable watching its values:
|
||||
this.isolatedScope.blockModels["_"+blockModel.key] = blockModel;
|
||||
|
||||
// Start watching each property value.
|
||||
var variant = blockModel.content.variants[0];
|
||||
for (var t = 0; t < variant.tabs.length; t++) {
|
||||
var tab = variant.tabs[t];
|
||||
for (var p = 0; p < tab.properties.length; p++) {
|
||||
var prop = tab.properties[p];
|
||||
|
||||
// Watch value of property since this is the only value we want to keep synced.
|
||||
// Do notice that it is not performing a deep watch, meaning that we are only watching primatives and changes directly to the object of property-value.
|
||||
// But we like to sync non-primative values as well! Yes, and this does happen, just not through this code, but through the nature of JavaScript.
|
||||
// Non-primative values act as references to the same data and are therefor synced.
|
||||
blockModel.watchers.push(this.isolatedScope.$watch("blockModels._"+blockModel.key+".content.variants[0].tabs["+t+"].properties["+p+"].value", createPropWatcher(blockModel, prop)));
|
||||
}
|
||||
}
|
||||
|
||||
return blockModel;
|
||||
|
||||
},
|
||||
|
||||
|
||||
removeDataAndDestroyModel: function(blockModel) {
|
||||
this.destroyBlockModel(blockModel);
|
||||
this.removeDataByUdi(blockModel.content.udi);
|
||||
},
|
||||
|
||||
destroyBlockModel: function(blockModel) {
|
||||
|
||||
// remove property value watchers:
|
||||
for (const w of blockModel.watchers) {
|
||||
w();
|
||||
}
|
||||
|
||||
// remove model from isolatedScope.
|
||||
delete this.isolatedScope.blockModels[blockModel.key];
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve block model of a layout entry
|
||||
* @return {Object} Scaffolded Block Content object.
|
||||
@@ -237,7 +314,7 @@
|
||||
}
|
||||
|
||||
var entry = {
|
||||
udi: this._createContent(contentTypeAlias)
|
||||
udi: this._createDataEntry(contentTypeAlias)
|
||||
}
|
||||
|
||||
if (blockConfiguration.settingsElementTypeAlias != null) {
|
||||
@@ -262,7 +339,10 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
var contentModel = this._getContentByUdi(layoutEntry.udi);
|
||||
var contentModel = this._getDataByUdi(layoutEntry.udi);
|
||||
if(contentModel === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
mapToPropertyModel(elementTypeContentModel, contentModel);
|
||||
|
||||
@@ -271,7 +351,7 @@
|
||||
},
|
||||
|
||||
// private
|
||||
_createContent: function(elementTypeAlias) {
|
||||
_createDataEntry: function(elementTypeAlias) {
|
||||
var content = {
|
||||
contentTypeAlias: elementTypeAlias,
|
||||
udi: udiService.create("element")
|
||||
@@ -280,40 +360,38 @@
|
||||
return content.udi;
|
||||
},
|
||||
// private
|
||||
_getContentByUdi: function(udi) {
|
||||
return this.value.data.find(entry => entry.udi === udi);
|
||||
_getDataByUdi: function(udi) {
|
||||
return this.value.data.find(entry => entry.udi === udi) || null;
|
||||
},
|
||||
|
||||
removeContent: function(entry) {
|
||||
const index = this.value.data.indexOf(entry)
|
||||
|
||||
removeDataByUdi: function(udi) {
|
||||
const index = this.value.data.findIndex(o => o.udi === udi);
|
||||
if (index !== -1) {
|
||||
this.value.splice(index, 1);
|
||||
this.value.data.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
removeContentByUdi: function(udi) {
|
||||
const index = this.value.data.findIndex(o => o.udi === udi);
|
||||
if (index !== -1) {
|
||||
this.value.splice(index, 1);
|
||||
onDestroyed: function() {
|
||||
|
||||
for (const key in this.isolatedScope.blockModels) {
|
||||
this.destroyBlockModel(this.isolatedScope.blockModels[key]);
|
||||
}
|
||||
|
||||
delete this.value;
|
||||
delete this.propertyEditorAlias;
|
||||
delete this.blockConfigurations;
|
||||
delete this.scaffolds;
|
||||
delete this.watchers;
|
||||
delete this.isolatedScope;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createModelObject: function(propertyModelValue, propertyEditorAlias, blockConfigurations) {
|
||||
return new BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations);
|
||||
createModelObject: function(propertyModelValue, propertyEditorAlias, blockConfigurations, propertyScope) {
|
||||
return new BlockEditorModelObject(propertyModelValue, propertyEditorAlias, blockConfigurations, propertyScope);
|
||||
},
|
||||
mapElementTypeValues: mapElementTypeValues,
|
||||
getBlockLabel: function(blockModel) {
|
||||
|
||||
|
||||
if(blockModel.labelInterpolator) {
|
||||
// We are just using the contentModel, since its a key/value object that is live synced. (if we need to add additional vars, we could make a shallow copy and apply those.)
|
||||
return blockModel.labelInterpolator(blockModel.contentModel);
|
||||
}
|
||||
|
||||
return blockModel.contentTypeName;
|
||||
}
|
||||
getBlockLabel: getBlockLabel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<button type="button" class="btn-reset umb-outline blockelement-imageblock-editor blockelement__draggable-element" focus-when="{{blockvm.focusThis}}" ng-click="vm.blockEditorApi.editBlock(blockItem)">
|
||||
<img src="{{blockItem.content.temp_image}}">
|
||||
<button type="button" class="btn-reset umb-outline blockelement-imageblock-editor blockelement__draggable-element" focus-when="{{vm.moveFocusToBlock === block}}" ng-click="vm.blockEditorApi.editBlock(block)">
|
||||
<img src="{{block.content.temp_image}}">
|
||||
</button>
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
<div class="blockelement-inlineblock-editor" ng-class="{'--open': bc.isOpen}" ng-controller="Umbraco.PropertyEditors.BlockEditor.InlineBlockEditor as bc">
|
||||
<button type="button" class="btn-reset umb-outline blockelement__draggable-element" ng-click="bc.openBlock(blockItem)" focus-when="{{blockvm.focusThis}}">
|
||||
<button type="button" class="btn-reset umb-outline blockelement__draggable-element" ng-click="bc.openBlock(block)" focus-when="{{vm.moveFocusToBlock === block}}">
|
||||
<span class="caret"></span>
|
||||
<i class="icon {{blockItem.content.icon}}"></i>
|
||||
<span>{{blockItem.label}}</span>
|
||||
<i class="icon {{block.content.icon}}"></i>
|
||||
<span>{{block.label}}</span>
|
||||
</button>
|
||||
<div class="blockelement-inlineblock-editor__inner" ng-if="bc.isOpen === true">
|
||||
<umb-element-content-editor content="blockItem.content"></umb-element-content-editor>
|
||||
<umb-element-content-editor content="block.content"></umb-element-content-editor>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<button type="button" class="btn-reset umb-outline blockelement-labelblock-editor blockelement__draggable-element" ng-click="vm.blockEditorApi.editBlock(blockItem)" focus-when="{{vm.focusThis}}">
|
||||
<i class="icon {{blockItem.content.icon}}"></i>
|
||||
<span>{{blockItem.label}}</span>
|
||||
<button type="button" class="btn-reset umb-outline blockelement-labelblock-editor blockelement__draggable-element" ng-click="vm.blockEditorApi.editBlock(block)" focus-when="{{vm.moveFocusToBlock === block}}">
|
||||
<i class="icon {{block.content.icon}}"></i>
|
||||
<span>{{block.label}}</span>
|
||||
</button>
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
ng-model="vm.firstProperty.value"
|
||||
ng-keypress="vm.submitOnEnter($event)"
|
||||
ng-blur="vm.onBlur()"
|
||||
focus-when="{{blockvm.focusThis}}"
|
||||
focus-when="{{vm.moveFocusToBlock === block}}"
|
||||
>
|
||||
</textarea>
|
||||
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@
|
||||
});
|
||||
|
||||
function BlockEditorBlockBlockController($scope, blockEditorService) {
|
||||
|
||||
/*
|
||||
var unsubscribe = [];
|
||||
var vm = this;
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
subscription();
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -5,7 +5,7 @@
|
||||
|
||||
<div ui-sortable="vm.sortableOptions" ng-model="vm.blocks">
|
||||
|
||||
<div ng-repeat="blockItem in vm.blocks track by blockItem.key">
|
||||
<div ng-repeat="block in vm.blocks track by block.key">
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@@ -14,25 +14,25 @@
|
||||
>
|
||||
</button>
|
||||
|
||||
<block-editor-block block="blockItem" focus-this-block="vm.moveFocusToBlock === blockItem" block-editor-api="vm.blockEditorApi" show-copy="vm.showCopy" class="umb-block-list__block">
|
||||
<div class="umb-block-list__block--content" ng-if="blockItem.view" ng-include="blockItem.view">
|
||||
<div class="umb-block-list__block">
|
||||
<div class="umb-block-list__block--content" ng-if="block.view" ng-include="block.view">
|
||||
</div>
|
||||
|
||||
<div class="umb-block-list__block--actions">
|
||||
<button type="button" class="btn-reset action --copy" localize="title" title="actions_copy" ng-click="vm.blockEditorApi.requestCopyBlock(blockItem);" ng-if="vm.showCopy">
|
||||
<button type="button" class="btn-reset action --copy" localize="title" title="actions_copy" ng-click="vm.blockEditorApi.requestCopyBlock(block);" ng-if="vm.showCopy">
|
||||
<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 action --delete" localize="title" title="actions_delete" ng-click="vm.blockEditorApi.requestDeleteBlock(blockItem);">
|
||||
<button type="button" class="btn-reset action --delete" localize="title" title="actions_delete" ng-click="vm.blockEditorApi.requestDeleteBlock(block);">
|
||||
<i class="icon icon-trash" aria-hidden="true"></i>
|
||||
<span class="sr-only">
|
||||
<localize key="general_delete">Delete</localize>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</block-editor-block>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+20
-17
@@ -51,15 +51,13 @@
|
||||
|
||||
vm.validationLimit = vm.model.config.validationLimit;
|
||||
|
||||
console.log("Model TESTS:", vm.model.value === null, typeof vm.model.value !== 'object')
|
||||
|
||||
// We need to ensure that the property model value is an object, this is needed for modelObject to recive a reference and keep that updated.
|
||||
if(vm.model.value === null || typeof vm.model.value !== 'object') {// testing if we have null or undefined value or if the value is set to another type than Object.
|
||||
if(typeof vm.model.value !== 'object' || vm.model.value === null) {// testing if we have null or undefined value or if the value is set to another type than Object.
|
||||
vm.model.value = {};
|
||||
}
|
||||
|
||||
// Create Model Object, to manage our data for this Block Editor.
|
||||
modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks);
|
||||
modelObject = blockEditorService.createModelObject(vm.model.value, vm.model.editor, vm.model.config.blocks, $scope);
|
||||
modelObject.loadScaffolding().then(onLoaded);
|
||||
|
||||
copyAllBlocksAction = {
|
||||
@@ -161,16 +159,20 @@
|
||||
|
||||
|
||||
function deleteBlock(block) {
|
||||
|
||||
var index = vm.blocks.indexOf(block);
|
||||
if(index !== -1) {
|
||||
vm.blocks.splice(index, 1);
|
||||
|
||||
var layoutIndex = layout.findIndex(entry => entry.udi === block.udi);
|
||||
var layoutIndex = layout.findIndex(entry => entry.udi === block.content.udi);
|
||||
if(layoutIndex !== -1) {
|
||||
vm.layout.splice(index, 1);
|
||||
layout.splice(index, 1);
|
||||
} else {
|
||||
throw new Error("Could not find layout entry of block with udi: "+block.content.udi)
|
||||
}
|
||||
|
||||
modelObject.removeContentByUdi(block.udi);
|
||||
vm.blocks.splice(index, 1);
|
||||
|
||||
modelObject.removeDataAndDestroyModel(block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +191,7 @@
|
||||
view: "views/common/infiniteeditors/elementeditor/elementeditor.html",
|
||||
size: blockModel.config.overlaySize || "medium",
|
||||
submit: function(elementEditorModel) {
|
||||
// To ensure syncronization gets tricked we transfer
|
||||
blockEditorService.mapElementTypeValues(elementEditorModel.content, blockModel.content)
|
||||
editorService.close();
|
||||
},
|
||||
@@ -397,7 +400,7 @@
|
||||
var moveFromIndex = runtimeSortVars.moveFromIndex;
|
||||
var moveToIndex = ui.item.index();
|
||||
|
||||
if (moveToIndex > -1 && moveFromIndex !== moveToIndex) {
|
||||
if (moveToIndex !== -1 && moveFromIndex !== moveToIndex) {
|
||||
var movedEntry = layout[moveFromIndex];
|
||||
layout.splice(moveFromIndex, 1);
|
||||
layout.splice(moveToIndex, 0, movedEntry);
|
||||
@@ -424,19 +427,19 @@
|
||||
deleteAllBlocksAction.isDisabled = vm.blocks.length === 0;
|
||||
|
||||
// validate limits:
|
||||
if (vm.validationLimit.min !== null) {
|
||||
if (vm.blocks.length < vm.validationLimit.min) {
|
||||
if (vm.propertyForm) {
|
||||
if (vm.validationLimit.min !== null && vm.blocks.length < vm.validationLimit.min) {
|
||||
vm.propertyForm.minCount.$setValidity("minCount", false);
|
||||
}
|
||||
else {
|
||||
vm.propertyForm.minCount.$setValidity("minCount", true);
|
||||
}
|
||||
}
|
||||
if (vm.validationLimit.max !== null && vm.blocks.length > vm.validationLimit.max) {
|
||||
vm.propertyForm.maxCount.$setValidity("maxCount", false);
|
||||
}
|
||||
else {
|
||||
vm.propertyForm.maxCount.$setValidity("maxCount", true);
|
||||
if (vm.validationLimit.max !== null && vm.blocks.length > vm.validationLimit.max) {
|
||||
vm.propertyForm.maxCount.$setValidity("maxCount", false);
|
||||
}
|
||||
else {
|
||||
vm.propertyForm.maxCount.$setValidity("maxCount", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user