implement save
This commit is contained in:
@@ -51,30 +51,34 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) {
|
||||
@state()
|
||||
_node?: DocumentNode;
|
||||
|
||||
private _contentService?: UmbNodeStore;
|
||||
private _nodeStore?: UmbNodeStore;
|
||||
private _nodeSubscription?: Subscription;
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
|
||||
this.consumeContext('umbNodeStore', (contentService: UmbNodeStore) => {
|
||||
this._contentService = contentService;
|
||||
this.consumeContext('umbNodeStore', (nodeStore: UmbNodeStore) => {
|
||||
this._nodeStore = nodeStore;
|
||||
this._useNode();
|
||||
});
|
||||
}
|
||||
|
||||
private _onPropertyDataTypeChange(e: CustomEvent) {
|
||||
const target = (e.target as any)
|
||||
console.log(target.value)
|
||||
private _onPropertyValueChange(e: CustomEvent) {
|
||||
const target = (e.target as any);
|
||||
|
||||
// TODO: Set value.
|
||||
//this.nodeData.properties.find(x => x.propertyAlias === target.propertyAlias)?.tempValue = target.value;
|
||||
const property = this._node?.properties.find(x => x.alias === target.property.alias);
|
||||
if(property) {
|
||||
property.tempValue = target.value;
|
||||
} else {
|
||||
console.error('property was not found', target.property.alias);
|
||||
}
|
||||
}
|
||||
|
||||
private _useNode() {
|
||||
this._nodeSubscription?.unsubscribe();
|
||||
|
||||
this._nodeSubscription = this._contentService?.getById(parseInt(this.id)).subscribe(node => {
|
||||
this._nodeSubscription = this._nodeStore?.getById(parseInt(this.id)).subscribe(node => {
|
||||
if (!node) return; // TODO: Handle nicely if there is no node.
|
||||
this._node = node;
|
||||
});
|
||||
@@ -85,7 +89,10 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _onSave() {
|
||||
console.log('Save');
|
||||
// TODO: What if store is not present, what if node is not loaded....
|
||||
if(this._node) {
|
||||
this._nodeStore?.save([this._node]);
|
||||
}
|
||||
}
|
||||
|
||||
private _onSaveAndPreview() {
|
||||
@@ -95,6 +102,7 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) {
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._nodeSubscription?.unsubscribe();
|
||||
delete this._node;
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -114,7 +122,7 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) {
|
||||
<umb-node-property
|
||||
.property=${property}
|
||||
.value=${property.tempValue}
|
||||
@property-data-type-change=${this._onPropertyDataTypeChange}>
|
||||
@property-value-change=${this._onPropertyValueChange}>
|
||||
</umb-node-property>
|
||||
<hr />
|
||||
`)}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { css, html, LitElement } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { UmbContextConsumerMixin, UmbContextProviderMixin } from '../core/context';
|
||||
import { UmbRouteLocation, UmbRouter } from '../core/router';
|
||||
import { UmbNodeStore as UmbNodeStore } from '../core/stores/node.store';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import './content-tree.element';
|
||||
@@ -44,6 +43,8 @@ export class UmbContentSection extends UmbContextProviderMixin(UmbContextConsume
|
||||
// TODO: temp outlet solution
|
||||
const nodeId = location.params.nodeId;
|
||||
|
||||
this._outlet?.parentNode?.removeChild(this._outlet);
|
||||
|
||||
if (nodeId !== undefined) {
|
||||
const contentEditor = document.createElement('umb-content-editor');
|
||||
contentEditor.id = nodeId;
|
||||
|
||||
@@ -17,9 +17,39 @@ export class UmbNodeStore {
|
||||
return this.nodes.pipe(map(((nodes: Array<DocumentNode>) => nodes.find((node: DocumentNode) => node.id === id) || null)));
|
||||
}
|
||||
|
||||
// TODO: Use Node type, to not be specific about Document.
|
||||
// TODO: make sure UI somehow can follow the status of this action.
|
||||
save(data: DocumentNode[]) {
|
||||
// fetch from server and update store
|
||||
// TODO: use Fetcher API.
|
||||
let body:string;
|
||||
|
||||
try {
|
||||
body = JSON.stringify(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Use node type to hit the right API, or have a general Node API?
|
||||
fetch('/umbraco/backoffice/content/save',
|
||||
{
|
||||
method: 'POST',
|
||||
body: body,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
this._updateStore(data);
|
||||
});
|
||||
}
|
||||
|
||||
private _updateStore (fetchedNodes: Array<any>) {
|
||||
const storedNodes = this._nodes.getValue();
|
||||
let updated: any = [...storedNodes];
|
||||
const updated: DocumentNode[] = [...storedNodes];
|
||||
|
||||
fetchedNodes.forEach(fetchedNode => {
|
||||
const index = storedNodes.map(storedNode => storedNode.id).indexOf(fetchedNode.id);
|
||||
@@ -29,7 +59,7 @@ export class UmbNodeStore {
|
||||
updated[index] = fetchedNode;
|
||||
} else {
|
||||
// If the node is not in the store, add it
|
||||
updated = [...updated, fetchedNode];
|
||||
updated.push(fetchedNode);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ export interface DocumentNode {
|
||||
alias: string;
|
||||
icon: string; // TODO: should come from the doc type?
|
||||
properties: NodeProperty[];
|
||||
data: any; // TODO: define data type
|
||||
layout?: any; // TODO: define layout type - make it non-optional
|
||||
//data: any; // TODO: define data type
|
||||
//layout?: any; // TODO: define layout type - make it non-optional
|
||||
}
|
||||
|
||||
export interface DataTypeEntity {
|
||||
@@ -27,29 +27,32 @@ export interface NodeProperty {
|
||||
tempValue: string; // TODO: remove this - only used for testing
|
||||
}
|
||||
|
||||
export const data = [
|
||||
|
||||
export const data: Array<DocumentNode> = [
|
||||
{
|
||||
id: 1,
|
||||
key: '74e4008a-ea4f-4793-b924-15e02fd380d3',
|
||||
key: '74e4008a-ea4f-4793-b924-15e02fd380d1',
|
||||
name: 'Document 1',
|
||||
alias: 'document1',
|
||||
icon: 'document',
|
||||
properties: [
|
||||
{
|
||||
alias: 'myHeadline',
|
||||
label: 'Textarea label',
|
||||
label: 'Textarea label 1',
|
||||
description: 'this is a textarea property',
|
||||
dataTypeKey: 'dt-1',
|
||||
tempValue: 'hello world 1'
|
||||
},
|
||||
{
|
||||
alias: 'myDescription',
|
||||
label: 'Text string label',
|
||||
label: 'Text string label 1',
|
||||
description: 'This is the a text string property',
|
||||
dataTypeKey: 'dt-2',
|
||||
tempValue: 'Tex areaaaa 1'
|
||||
},
|
||||
],
|
||||
/*
|
||||
// Concept for stored values, better approach for variants, separating data from structure/configuration
|
||||
data: [
|
||||
{
|
||||
alias: 'myHeadline',
|
||||
@@ -60,7 +63,9 @@ export const data = [
|
||||
value: 'Teeeeexxxt areaaaaaa',
|
||||
},
|
||||
],
|
||||
*/
|
||||
/*
|
||||
// Concept for node layout, separation of design from config and data.
|
||||
layout: [
|
||||
{
|
||||
type: 'group',
|
||||
@@ -80,53 +85,26 @@ export const data = [
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
key: '74e4008a-ea4f-4793-b924-15e02fd380d3',
|
||||
key: '74e4008a-ea4f-4793-b924-15e02fd380d2',
|
||||
name: 'Document 2',
|
||||
alias: 'document2',
|
||||
icon: 'favorite',
|
||||
properties: [
|
||||
{
|
||||
alias: 'myHeadline',
|
||||
label: 'Textarea label',
|
||||
label: 'Textarea label 2',
|
||||
description: 'this is a textarea property',
|
||||
dataTypeKey: 'dt-1',
|
||||
tempValue: 'hello world 2'
|
||||
},
|
||||
{
|
||||
alias: 'myDescription',
|
||||
label: 'Text string label',
|
||||
label: 'Text string label 2',
|
||||
description: 'This is the a text string property',
|
||||
dataTypeKey: 'dt-2',
|
||||
tempValue: 'Tex areaaaa 2'
|
||||
},
|
||||
],
|
||||
data: [
|
||||
{
|
||||
alias: 'myHeadline',
|
||||
value: 'hello world',
|
||||
},
|
||||
{
|
||||
alias: 'myDescription',
|
||||
value: 'Teeeeexxxt areaaaaaa',
|
||||
},
|
||||
],
|
||||
/*
|
||||
layout: [
|
||||
{
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
type: 'property',
|
||||
alias: 'myHeadline'
|
||||
},
|
||||
{
|
||||
type: 'property',
|
||||
alias: 'myDescription'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
*/
|
||||
}
|
||||
];
|
||||
|
||||
@@ -141,6 +119,21 @@ class UmbContentData {
|
||||
getById (id: number) {
|
||||
return this._data.find(item => item.id === id);
|
||||
}
|
||||
|
||||
save(nodes: DocumentNode[]) {
|
||||
nodes.forEach( node => {
|
||||
const foundIndex = this._data.findIndex(item => item.id === node.id);
|
||||
if(foundIndex !== -1) {
|
||||
// replace
|
||||
this._data[foundIndex] = node;
|
||||
} else {
|
||||
// new
|
||||
this._data.push(node);
|
||||
}
|
||||
});
|
||||
//console.log('save:', nodes);
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
||||
export const umbContentData = new UmbContentData();
|
||||
@@ -1,5 +1,6 @@
|
||||
import { rest } from 'msw';
|
||||
import { umbContentData } from '../data/content.data';
|
||||
import { DocumentNode, umbContentData } from '../data/content.data';
|
||||
|
||||
|
||||
// TODO: add schema
|
||||
export const handlers = [
|
||||
@@ -9,9 +10,23 @@ export const handlers = [
|
||||
|
||||
const int = parseInt(id);
|
||||
const document = umbContentData.getById(int);
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json([document])
|
||||
);
|
||||
}),
|
||||
|
||||
rest.post('/umbraco/backoffice/content/save', (req, res, ctx) => {
|
||||
const data = req.body as any;
|
||||
if (!data) return;
|
||||
|
||||
|
||||
umbContentData.save(data);
|
||||
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json(data)
|
||||
);
|
||||
}),
|
||||
];
|
||||
@@ -32,8 +32,11 @@ class UmbNodeProperty extends LitElement {
|
||||
value?:string;
|
||||
|
||||
// TODO: maybe a bit messy with all the event listeners on the different levels:
|
||||
private _onPropertyDataTypeChange = (e :CustomEvent) => {
|
||||
private _onPropertyDataTypeChange = ( e:CustomEvent) => {
|
||||
this.value = (e.target as any).value;
|
||||
this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true }));
|
||||
// No need for this event to leave scope.
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -16,8 +16,13 @@ class UmbPropertyEditorTextarea extends LitElement {
|
||||
@property()
|
||||
value = '';
|
||||
|
||||
private onInput(e: InputEvent) {
|
||||
this.value = (e.target as HTMLInputElement).value;
|
||||
this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<uui-textarea .value=${this.value}></uui-textarea>`;
|
||||
return html`<uui-textarea .value=${this.value} @input=${this.onInput}></uui-textarea>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { firstValueFrom, map, Observable, ReplaySubject } from 'rxjs';
|
||||
import { UmbExtensionManifest, UmbExtensionRegistry, UmbManifestSectionMeta } from './core/extension';
|
||||
import { UmbExtensionManifest, UmbExtensionRegistry } from './core/extension';
|
||||
|
||||
export class UmbSectionContext {
|
||||
private _extensionRegistry!: UmbExtensionRegistry;
|
||||
|
||||
Reference in New Issue
Block a user