Edit content from Block with content from another page

Hi,

After this question

I cooked my own without manual mode and I want to edit the text in the block (.k-block-type-card-text).

panel.plugin("cookbook/block-factory", {
  blocks: {
   card: {
      data() {
        return {
          text: "No text value"
        };
      },
      computed: {
        heading() {
          return this.page.text;
        },
        image() {     
            return this.page.image || {}
        },
        pageId() {
          return this.page ? this.page.id : '';
        },
        page() {
            return this.content.page[0] || {};
        },
      },
      watch: {
        "page": {
          handler (value) {
           if(this.pageId) {
            this.$api.get('pages/' + this.pageId.replace('/', '+')).then(page => {
              this.text = page.content.text.replace(/(<([^>]+)>)/gi, "") || this.text;
            });
           }
          },
          immediate: true
        }
      },
      template: `
        <div @dblclick="open">
          <k-aspect-ratio
            class="k-block-type-card-image"
            cover="true"
            ratio="1/1"
          >
            <img
              v-if="image.url"
              :src="image.url"
              alt=""
            >
          </k-aspect-ratio>
          <h2 class="k-block-type-card-heading">{{ heading }}</h2>
          <div class="k-block-type-card-text">{{ text }}</div>
        </div>
      `
    },
  }
});

card.yml is short :slight_smile:

name: field.blocks.card.name
icon: image
fields:
  page:
    type: pages
    max: 1
    query: kirby.page('photography').children.listed

I don’t know where I can make it editable (and I don’t want to edit the original ‘photography’ post).

Hm, you would probably need additional fields where you can actually store the content. So you would have to fill a new editable field with the response from the API and then store this field in the block in addition to the page id (which is currently the only value that is stored, the rest is fetch dynamically).

OK, add the field in card.yml I can do this, but for the next step, I don’t know where.
Is it in computed : ?

Hm, filling the new fields is not the problem, but I don’t really know how to decouple them from the watcher which will immediately undo every change.

So I need to do some tricky-ugly JS to fetch content ?

Let’s assume you add two new fields to your card.yml

name: field.blocks.card.name
icon: image
fields:
  page:
    type: pages
    max: 1
    query: kirby.page('photography').children.listed
  heading:
    type: writer
    inline: true
  text:
    type: writer
    inline: false

Then you can fill them with the content from the page:

panel.plugin("cookbook/block-factory", {
  blocks: {
   card: {
      data() {
        return {
        };
      },
      computed: {

        image() {
            return this.page.image || {}
        },
        pageId() {
          return this.page ? this.page.id : '';
        },
        page() {
            return this.content.page[0] || {};
        },
        headingField() {
          return this.field("heading");
        },
        textField() {
          return this.field("text");
        }
      },

      watch: {
        "page": {
          handler (value) {
            if(this.pageId) {
              this.$api.get('pages/' + this.pageId.replace('/', '+')).then(page => {
                  this.content.text = page.content.text.replace(/(<([^>]+)>)/gi, "") || '';
                  this.content.heading = page.content.title.replace(/(<([^>]+)>)/gi, "") || '';

            });
           }
          },
          immediate: true
        }
      },

      template: `
        <div @dblclick="open">
          <k-aspect-ratio
            class="k-block-type-card-image"
            cover="true"
            ratio="1/1"
          >
            <img
              v-if="image.url"
              :src="image.url"
              alt=""
            >
          </k-aspect-ratio>
          <h2 class="k-block-type-card-heading"">
            <k-writer
              ref="heading"
              :inline="headingField.inline"
              :marks="headingField.marks"
              :placeholder="headingField.placeholder || 'Add a heading'"
              :value="content.heading"
              @input="update({ heading: $event })"
            />
        </h2>
          <div class="k-block-type-card-text">
            <k-writer
            ref="text"
            :inline="textField.inline"
            :marks="textField.marks"
            :placeholder="textField.placeholder || 'Add some text'"
            :value="content.text"
            @input="update({ text: $event })"
          />
          </div>
        </div>
      `
    },
  }
});

But this misses a mechanism to stop the API call in the watcher, unless the page field really changes.

So you need some conditions that empty those fields again when the page field is empty etc.

This seems to work as expected:

     watch: {
        "page": {
          handler (value) {
            if(typeof value.id === 'undefined') {
              this.content.text = '';
              this.content.heading = '';
            }
            if(this.pageId) {
              this.$api.get('pages/' + this.pageId.replace('/', '+')).then(page => {
                if (value.id === page.id && this.content.text !== '') {
                  this.content.text = this.content.text;
                  this.content.heading = this.content.heading;
                } else {
                  this.content.text = page.content.text.replace(/(<([^>]+)>)/gi, "") || '';
                  this.content.heading = page.content.title.replace(/(<([^>]+)>)/gi, "") || '';
                }
            });
           }
          },
          immediate: true
        }
      },

Maybe refine conditions a bit.

1 Like

Wow! It’s perfect!

Try to understand, what is this.content?
Is there a video/doc about computed and watch?

I think I need to rewatch this :slight_smile: Kirby 3: Field Plugin Intro - YouTube