johannschopplich / kirby-vue3-starterkit

✨ Kirby + Vue SPA starter: automatic routing, i18n, SEO and more!
MIT License
206 stars 19 forks source link

Working with layouts and blocks #26

Closed rjdusk closed 3 years ago

rjdusk commented 3 years ago

Hiya, I'm trying to build my pages with layouts and blocks, but am having a real hard time with returning something I can work with on the frontend. This is a reduced code example:

about.php

<?php

$data = [
  'title' => $page->title()->value(),
  'metaTitle' => $page->customTitle()->or($page->title() . ' – ' . $site->title())->value(),
  'content' => $page->layout()->yaml(),
];

echo \Kirby\Data\Json::encode($data);

About.vue

<ul id="rendering">
      <li v-for="item in page.content">
        {{ item }}
      </li>
    </ul>

about.yaml

fields:
  layout:
    label: Page layout
    type: layout
    layouts:
      - "1/1"
      - "1/2, 1/2"
      - "1/3, 2/3"
      - "1/3, 1/3, 1/3"
    fieldsets:
      - heading
      - text
      - image

And this results in:

[
  "{\"attrs\":[]",
  "\"columns\":[{\"blocks\":[{\"content\":{\"1\\/3\":\"blocks\",\"content\":\"text\"},\"text\":\"<p>w two hals<\\/p>\",\"id\":false,\"4bf8fb48-4778-43e7-bf6a-8f3aff1d44a7\":\"isHidden\"}],\"content\":\"level\",\"h1\":\"text\"}]",
  "\"This is H!\":\"id\"}",
  "{\"ff98539c-e102-4137-a3fb-2cf1cb0b40cb\":[]",
  "\"isHidden\":[{\"type\":[{\"type\":{\"<p>And this is the following on text<\\/p>\":\"id\",\"5c4d8710-baaa-4ecf-90c9-c78de08f0260\":\"isHidden\"},\"text\":\"id\",\"56430216-c475-45d9-a667-9f729d55fc70\":false,\"width\":\"1\\/2\"}],\"heading\":\"id\",\"06c5cc06-8baf-416f-ac55-a69e3f5032b8\":\"width\"},{\"1\\/1\":[{\"id\":{\"type\":\"text\"},\"83b8e93a-6a5a-4201-91c1-a4ddf93ee722\":\"attrs\",\"columns\":false,\"blocks\":\"content\"}],\"id\":\"f3de5d32-7d3c-42c0-92d0-6f3401cd4c54\",\"attrs\":\"columns\"}]",
  "\"blocks\":\"content\"}",
  "{\"level\":[]",
  "\"h2\":[{\"text\":[{\"level\":{\"id\":\"abe0f890-aab0-4312-9957-acb9d2032b40\",\"width\":\"2\\/3\"},\"h2\":\"text\",\"This is a heading\":false,\"id\":\"b1816149-e675-4e49-b15c-e726b8092c1d\"}],\"w one half\":\"id\",\"3adc44e7-64a2-4696-b24d-80eb4250c643\":\"isHidden\"},{\"type\":[{\"isHidden\":{\"id\":\"e50c3002-07e6-49c0-b0d8-d77577674550\"},\"type\":\"heading\",\"id\":false,\"a87e13ca-8e21-4534-8b05-86b8e1fde184\":\"width\"}],\"heading\":\"id\",\"5309b4e9-f71b-481d-9640-7f36999c2e66\":\"width\"}]",
  "\"1\\/2\":\"blocks\"}"
]

Which makes me a sad panda 😢 Any pointers on how I should be approaching this? Thank you for any insights!

johannschopplich commented 3 years ago

First and foremost, you will have to parse the layout field correctly. Please take a look into the documentation on how to use the toLayouts() method. 🙂

After parsing the layouts, we will have to parse columns, then finally call the toHtml() method on each column's block, since you can't simply echo a $block like in regular Kirby templates.

Best option is likely to convert the layouts into an array. Here is some untested demo code, without taking account of columns (it just parses the first one, you will have to introduce another layer):

$data = [
    'content' => $page
        ->layout()
        ->toLayouts()
        ->map(function ($layout) {
            // TODO: Handle multiple columns by returning array
            $column = $layout->columns()->first();
            return $column->blocks()->toHtml();
        })
        ->values();
];

Edit: Also you can't simply print raw HTML, since it will be escaped. Use a <div v-html="item"> or similar instead.

rjdusk commented 3 years ago

Wonderful! Thank you @johannschopplich this gives me plenty to work with :)

johannschopplich commented 3 years ago

@rjdusk Hope it works out for your project. Let me know what you came up with!

rjdusk commented 3 years ago

Phew this is a real doozy. I've been messing around a little more and I can get all the columns and blocks in an array using this:

'columns' => $page
    ->layout()
    ->toLayouts()
    ->map(function ($layout) {
      $columns = $layout->columns();
      return $columns->toArray();
    })
    ->values(),

which on the front-end I get

{
  "columns": [
    [
      {
        "blocks": [
          {
            "content": {
              "level": "h1",
              "text": "This is H!"
            },
            "id": "ff98539c-e102-4137-a3fb-2cf1cb0b40cb",
            "isHidden": false,
            "type": "heading"
          }
        ],
        "id": "06c5cc06-8baf-416f-ac55-a69e3f5032b8",
        "width": "1/1"
      }
    ],
    [
      {
        "blocks": [
          {
            "content": {
              "level": "h2",
              "text": "w one half"
            },
            "id": "3adc44e7-64a2-4696-b24d-80eb4250c643",
            "isHidden": false,
            "type": "heading"
          }
        ],
        "id": "5309b4e9-f71b-481d-9640-7f36999c2e66",
        "width": "1/2"
      },
      {
        "blocks": [
          {
            "content": {
              "text": "<p>w two hals</p>"
            },
            "id": "4bf8fb48-4778-43e7-bf6a-8f3aff1d44a7",
            "isHidden": false,
            "type": "text"
          }
        ],
        "id": "56430216-c475-45d9-a667-9f729d55fc70",
        "width": "1/2"
      }
    ],
    [
      {
        "blocks": [
          {
            "content": {
              "level": "h2",
              "text": "col1"
            },
            "id": "3a3dddea-b79c-4840-9f91-6bc4796efd0a",
            "isHidden": false,
            "type": "heading"
          }
        ],
        "id": "9f76e34e-3cb8-4c3e-afe3-ce3b4065ee83",
        "width": "1/3"
      },
      {
        "blocks": [
          {
            "content": {
              "level": "h2",
              "text": "col2"
            },
            "id": "558c1a44-b90f-45c3-af55-5e777e1885d0",
            "isHidden": false,
            "type": "heading"
          }
        ],
        "id": "13ef3a05-4c3e-4740-a55f-63fb38b743c2",
        "width": "1/3"
      },
      {
        "blocks": [
          {
            "content": {
              "level": "h2",
              "text": "col3"
            },
            "id": "5abac79c-69b2-4cfb-b8b5-4ec4c51aa483",
            "isHidden": false,
            "type": "heading"
          }
        ],
        "id": "2c45794e-2763-4600-b416-65da37dc1a3c",
        "width": "1/3"
      }
    ]
  ]
}

What I'm having real trouble with is traversing this array on the front-end with Vue. Plus I can see I'll run into some roadblocks with the 'level' and 'width', maybe it's better to output to HTML? Otherwise I'll have to check which 'level' something is to assign the correct tag. Again not wanting to clog up your issues, just looking for some pointers. Thanks!

johannschopplich commented 3 years ago

May I ask what your use-case is? The layout field doesn't work well with an SPA intention. You could send the whole HTML to the frontend, but then the purpose of the Vue application is defeated. If heavy use of the layout field is important, then I'd stick to a plain Kirby setup.

rjdusk commented 3 years ago

Yes definitely, I'm planning on rebuilding my site in Kirby and thought I'd give Vue a go while I was at it. Your starterkit has everything I was looking for - Vue, Vite, Kirby, .env, multi-language, etc. The site is currently built with Eleventy, but I need something with a very light CMS behind it.

However I'm getting the feeling that with my site, where layouts vary from page to page, for example a portfolio page layout, a Kirby plain setup would be better suited. Trying to force this in Vue would be more hassle than it's worth. If my understanding is correct?

johannschopplich commented 3 years ago

Well, that's a stunning website to look at. Fellow photography friend so to speak. 🙂 Couldn't find you on Instagram tho.

I'm flattered you want to rebuild with this starterkit. But I really don't recommend to do so if you are relying on the layouts field. As mentioned earlier, the purpose of using Vue gets defeated if HTML is transferred to the frontend on a bigger scale. I presume it would be best to stick to the default Kirby setup. That's also why I don't use the current Kirby starter kit for this Vue boilerplate – the layout field doesn't suit the single page application objective.

Maybe you can dig into Vue + Kirby for another project?

rjdusk commented 3 years ago

Thanks, I'm flattered! I use to be on Instagram, but after many years of it, I got sick of it and deleted it (plus Facebook, etc). I do post to flickr when I have time :)

Thank you so much for your talking me through this, I'm definitely going to use this starterkit in the future, I love the ease of which I could get up and running, a great little self contained kit! Good luck and keep up the great work!

johannschopplich commented 3 years ago

Yep, I feel you. Not using Facebook as well. Instagram sort of as an portfolio and inpirational source.

My pleasure! Don't hesitate to write me via email in the future if you have any questions. Good luck to you as well and your website rewrite – complex task ahead! Hope you have fun in the process.