implement save

This commit is contained in:
Niels Lyngsø
2022-05-31 20:52:16 +02:00
parent 8fe7a12db1
commit f7eab5d3b0
8 changed files with 108 additions and 53 deletions

View File

@@ -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 />
`)}

View File

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

View File

@@ -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);
}
})

View File

@@ -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();

View File

@@ -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)
);
}),
];

View File

@@ -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() {

View File

@@ -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>`;
}
}

View File

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