Cutures can now be changed and the editor updated to the variant specific values without reloading, split view is re-enabled so working on getting that showing now.

This commit is contained in:
Shannon
2018-07-13 15:53:59 +10:00
parent 072c1a6764
commit 0e41c2dfcd
11 changed files with 168 additions and 104 deletions

View File

@@ -21,7 +21,6 @@
$scope.page.listViewPath = null;
$scope.page.isNew = $scope.isNew ? true : false;
$scope.page.buttonGroupState = "init";
$scope.page.culture = $scope.culture;
$scope.page.hideActionsMenu = infiniteMode ? true : false;
$scope.page.hideChangeVariant = infiniteMode ? true : false;
$scope.allowOpen = true;
@@ -56,39 +55,68 @@
bindEvents();
// set first app to active
// TODO: We need to track active
$scope.content.apps[0].active = true;
setActiveCulture();
}
/**
* The content item(s) are loaded into an array and this will set the active content item based on the current culture.
* If the content item is invariant, then only one item exists in the array.
*/
function setActiveCulture() {
// set the active variant
var activeCultureSet = false;
var activeVariant = null;
_.each($scope.content.variants, function (v) {
if (!activeCultureSet) {
if (v.language.culture === $scope.page.culture) {
v.active = true;
$scope.content.currentVariant = v;
activeCultureSet = true;
}
if (v.language.culture === $scope.culture) {
v.active = true;
activeVariant = v;
}
else {
v.active = false;
}
});
if (!activeCultureSet) {
if (!activeVariant) {
// set the first variant to active
$scope.content.variants[0].active = true;
$scope.content.currentVariant = $scope.content.variants[0];
}
activeVariant = $scope.content.variants[0];
}
//If there are no editors yet then create one with the current content.
//if there's already a main editor then update it with the current content.
//The model that is assigned to the editor contains the current content variant along
//with a copy of the contentApps. This is required because each editor renders it's own
//content apps section and the content apps contains the view for editing content itself
//and we need to assign a view model to the subView so that it is scoped to the current
//editor so that split views work. This is a bit hacky but it's required because the content
//app stuff isn't built to have a scoped model to an editor, it's built to have a single global
//model but that doesn't work for having split view.
if (!activeVariant.apps) {
//copy from the main model
activeVariant.apps = angular.copy($scope.content.apps);
}
//the assign the activeVariant to a view model to the content app
var contentApp = _.find(activeVariant.apps, function (a) {
return a.alias === "content";
});
contentApp.viewModel = activeVariant;
// create new editor for split view
if ($scope.editors.length === 0) {
var editor = {
content: $scope.content
content: activeVariant
};
$scope.editors.push(editor);
}
else if ($scope.editors.length === 1) {
$scope.editors[0].content = $scope.content;
$scope.editors[0].content = activeVariant;
}
else {
//fixme - need to fix something here if we are re-loading a content item that is in a split view
}
}
}
function bindEvents() {
//bindEvents can be called more than once and we don't want to have multiple bound events
@@ -487,7 +515,21 @@
$scope.openInSplitView = function (selectedVariant) {
console.log(selectedVariant);
//only the content app can be selected since no other apps are shown, and because we copy all of these apps
//to the "editors" we need to update this across all editors
for (var e = 0; e < $scope.editors.length; e++) {
var editor = $scope.editors[e];
for (var i = 0; i < editor.content.apps.length; i++) {
var app = editor.content.apps[i];
if (app.alias === "content") {
app.active = true;
}
else {
app.active = false;
}
}
}
var editor = {};
// hacking animation states - these should hopefully be easier to do when we upgrade angular
@@ -575,6 +617,12 @@
}
};
$scope.$watch('culture', function (newVal, oldVal) {
if (newVal !== oldVal) {
setActiveCulture();
}
});
//ensure to unregister from all events!
$scope.$on('$destroy', function () {
for (var e in evts) {

View File

@@ -201,7 +201,7 @@ Use this directive to construct a header inside the main editor window.
**/
(function() {
(function () {
'use strict';
function EditorHeaderDirective(iconHelper, $location, editorService) {
@@ -213,17 +213,17 @@ Use this directive to construct a header inside the main editor window.
scope.vm.currentVariant = "";
function onInit() {
setCurrentVariant(scope.variants);
setCurrentVariant();
}
function setCurrentVariant(variants) {
angular.forEach(variants, function (variant) {
if(variant.active) {
function setCurrentVariant() {
angular.forEach(scope.variants, function (variant) {
if (variant.active) {
scope.vm.currentVariant = variant;
}
});
}
scope.goBack = function () {
if (scope.onBack) {
scope.onBack();
@@ -235,11 +235,11 @@ Use this directive to construct a header inside the main editor window.
$location.search("cculture", variant.language.culture);
};
scope.openIconPicker = function() {
scope.openIconPicker = function () {
var iconPicker = {
icon: scope.icon.split(' ')[0],
color: scope.icon.split(' ')[1],
submit: function(model) {
submit: function (model) {
if (model.icon) {
if (model.color) {
scope.icon = model.icon + " " + model.color;
@@ -251,35 +251,44 @@ Use this directive to construct a header inside the main editor window.
}
editorService.close();
},
close: function() {
close: function () {
editorService.close();
}
};
editorService.iconPicker(iconPicker);
};
scope.closeSplitView = function() {
if(scope.onCloseSplitView) {
scope.closeSplitView = function () {
if (scope.onCloseSplitView) {
scope.onCloseSplitView();
}
};
scope.openInSplitView = function(event, variant) {
if(scope.onOpenInSplitView) {
scope.openInSplitView = function (event, variant) {
if (scope.onOpenInSplitView) {
scope.vm.dropdownOpen = false;
scope.onOpenInSplitView({"variant": variant});
scope.onOpenInSplitView({ "variant": variant });
}
};
//TODO: Change this, we cannot watch the whole model
//scope.$watch('variants', function(newValue, oldValue){
// if(!newValue) return;
// if(newValue === oldValue) return;
// setCurrentVariant(newValue);
//}, true);
onInit();
//watch for the active culture changing, if it changes, update the current variant
if (scope.variants) {
scope.$watch(function () {
for (var i = 0; i < scope.variants.length; i++) {
var v = scope.variants[i];
if (v.active) {
return v.language.culture;
}
}
return scope.vm.currentVariant.language.culture; //should never get here
}, function (newValue, oldValue) {
if (newValue !== scope.vm.currentVariant.language.culture) {
setCurrentVariant();
}
});
}
}
var directive = {

View File

@@ -1,48 +1,26 @@
(function() {
'use strict';
(function () {
'use strict';
function EditorSubViewsDirective() {
function EditorSubViewsDirective() {
function link(scope, el, attr, ctrl) {
function link(scope, el, attr, ctrl) {
scope.activeView = {};
}
// set toolbar from selected navigation item
function setActiveView(items) {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-editor-sub-views.html',
scope: {
subViews: "=",
model: "="
},
link: link
};
for (var index = 0; index < items.length; index++) {
return directive;
}
var item = items[index];
if (item.active && item.view) {
scope.activeView = item;
}
}
}
// watch for navigation changes
scope.$watch('subViews', function(newValue, oldValue) {
if (newValue) {
setActiveView(newValue);
}
}, true);
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-editor-sub-views.html',
scope: {
subViews: "=",
model: "="
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective);
angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective);
})();

View File

@@ -43,7 +43,9 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH
}
var currentRouteParams = null;
var globalQueryStrings = ["mculture"];
//A list of query strings defined that when changed will not cause a reload of the route
var nonRoutingQueryStrings = ["mculture", "cculture"];
/** execute code on each successful route */
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
@@ -131,15 +133,17 @@ app.run(['userService', '$q', '$log', '$rootScope', '$route', '$location', 'urlH
var allowRoute = true;
//the only time that we want to cancel is if any of the globalQueryStrings have changed
//in which case the number of parts need to be equal before comparing values
if (_.keys(currUrlParts).length == _.keys(nextUrlParts).length) {
//The only time that we want to cancel is if only any of the nonRoutingQueryStrings have changed/added.
//If any of the other parts have changed we do not cancel
var currRoutingKeys = _.difference(_.keys(currUrlParts), nonRoutingQueryStrings);
var nextRoutingKeys = _.difference(_.keys(nextUrlParts), nonRoutingQueryStrings);
var diff = _.difference(currRoutingKeys, nextRoutingKeys);
//if the routing parameter keys are the same, we'll compare their values to see if any have changed and if so then the routing will be allowed.
if (diff.length == 0) {
var partsChanged = 0;
_.each(currUrlParts, function (value, key) {
if (globalQueryStrings.indexOf(key) === -1) {
if (value.toLowerCase() !== nextUrlParts[key].toLowerCase()) {
partsChanged++;
}
_.each(currRoutingKeys, function (k) {
if (currUrlParts[k] != nextUrlParts[k]) {
partsChanged++;
}
});
if (partsChanged === 0) {

View File

@@ -161,7 +161,7 @@ app.config(function ($routeProvider) {
.when('/:section/:tree/:method?/:id', {
//This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
template: "<div ng-include='templateUrl'></div>",
//This controller will execute for this route, then we replace the template dynamnically based on the current tree.
//This controller will execute for this route, then we replace the template dynamically based on the current tree.
controller: function ($scope, $route, $routeParams, treeService) {
if (!$routeParams.tree || !$routeParams.method) {

View File

@@ -17,12 +17,12 @@
<umb-editor-header
menu="page.menu"
hide-menu="page.hideActionsMenu"
name="editor.content.currentVariant.name"
name="editor.content.name"
hide-icon="true"
hide-description="true"
hide-alias="true"
navigation="editor.content.apps"
variants="editor.content.variants"
variants="content.variants"
hide-change-variant="page.hideChangeVariant"
on-back="backToListView()"
show-back-button="page.listViewPath !== null"
@@ -35,7 +35,7 @@
<umb-editor-sub-views
ng-if="!page.loading"
sub-views="editor.content.apps"
model="editor.content">
model="content">
</umb-editor-sub-views>
</umb-editor-container>
</div>

View File

@@ -67,10 +67,9 @@
<span class="umb-variant-switcher__state" ng-if="!variant.validationError && variant.isEdited && variant.state === 'Unpublished'"><localize key="content_unpublishedPendingChanges"></localize></span>
<span class="umb-variant-switcher__state" ng-if="variant.isEdited === false">{{variant.state}}</span>
</a>
<!-- Commented out as opposed to removed until we fully support split view (U4-11290) -->
<!--
<div ng-if="splitViewOpen !== true" class="umb-variant-switcher__split-view" ng-click="openInSplitView($event, variant)">Open in split view</div>
-->
</umb-dropdown-item>
</umb-dropdown>

View File

@@ -3,12 +3,12 @@
<div
id="sub-view-{{$index}}"
class="umb-editor-sub-view"
ng-repeat="subView in subViews"
ng-repeat="subView in subViews track by subView.alias"
ng-class="'sub-view-' + subView.name"
val-sub-view>
<div class="umb-editor-sub-view__content"
ng-show="activeView.name == subView.name"
ng-show="subView.active === true"
ng-include="subView.view">
</div>

View File

@@ -1,24 +1,42 @@
(function () {
"use strict";
function ContentAppContentController($scope) {
function ContentAppContentController($scope, $timeout) {
var vm = this;
vm.loading = true;
function onInit() {
//select the first one in the list
//TODO: We need to track the active one
vm.content = $scope.model.variants[0];
vm.content.active = true;
//TODO: This is pretty ugly since this component inherits the scope from umbEditorSubViews which is supplied a
//'model' which is the entire content object passed from the server which we can't use because this 'app' needs to be
//in the context of the current culture within a split view. So the content controller will assign a special 'viewModel' to the
//subView so that we have a model in the context of the editor.
//Ideally this would be a directive and we can just pass a model in but because contentApps currently are
//rendered purely based on views that won't work. Perhaps we can consider dynamically compiling directives like
// https://www.codelord.net/2015/05/19/angularjs-dynamically-loading-directives/
angular.forEach(vm.content.tabs, function(group){
vm.content = $scope.subView.viewModel;
angular.forEach(vm.content.tabs, function (group) {
group.open = true;
});
vm.loading = false;
}
onInit();
$scope.$watch(function () {
return $scope.subView.viewModel.language.culture;
}, function (newVal, oldVal) {
if (newVal !== oldVal) {
vm.loading = true;
$timeout(function () {
onInit();
});
}
});
}
angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ContentController", ContentAppContentController);

View File

@@ -1,6 +1,6 @@
<div class="form-horizontal" ng-controller="Umbraco.Editors.Content.Apps.ContentController as vm">
<div class="umb-expansion-panel" ng-repeat="group in vm.content.tabs | filter: { hide : '!' + true } track by group.label">
<div class="umb-expansion-panel" ng-if="!vm.loading" ng-repeat="group in vm.content.tabs | filter: { hide : '!' + true } track by group.label">
<div class="umb-expansion-panel__header" ng-click="group.open = !group.open">
<div>{{ group.label }}</div>

View File

@@ -6,7 +6,7 @@
* @description
* The controller for the content editor
*/
function ContentEditController($scope, $routeParams, contentResource) {
function ContentEditController($scope, $rootScope, $routeParams, contentResource) {
var infiniteMode = $scope.model && $scope.model.infiniteMode;
@@ -23,7 +23,15 @@ function ContentEditController($scope, $routeParams, contentResource) {
$scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty;
$scope.page = $routeParams.page;
$scope.isNew = infiniteMode ? $scope.model.create : $routeParams.create;
$scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; //load the default culture selected in the main tree if any
//load the default culture selected in the main tree if any
$scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture;
//Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered.
//This is so we can listen to changes on the cculture parameter since that will not cause a route change
// and then we can pass in the updated culture to the editor
$scope.$on('$routeUpdate', function (event, next) {
$scope.culture = next.params.cculture;
});
}
angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController);