Pass data to a template using a ViewModel
We’ve created a view model, but now to get access to that ViewModel. We’ll do that by injecting it as an argument to the block, and we will do this with layout XML.
Within view/frontend/layout
let’s open up the blog post list XML file. And rather than having this block
tag be a self-closing tag, let’s open it up.
Within it, we will create an arguments
node that has an argument
child node. The name of this argument can be whatever we wish. We will name it “post”, but append an “_vm” to the end, to signify this is a ViewModel and to avoid ambiguity with posts in our code. This will have an xsi:type
of object
, and the value of this argument will be the fully qualified class name to our ViewModel.
view/frontend/layout/blog_post_list.xml
<?xml version="1.0"?> <page layout="1column" xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="main"> <block name="blog.list" template="Macademy_Blog::post/list.phtml"> <arguments> <argument name="post_vm" xsi:type="object">Macademy\\Blog\\ViewModel\\Post</argument> </arguments> </block> </referenceContainer> </body> </page>
Now this post
ViewModel is accessible as data of the block. Let’s create a new $postVm
variable, and we will assign it the result of $block->getData('post_vm')
. This “post_vm” references the name we assigned in the layout XML (toggle back to show).
Just like a $block
typehint is defined at the top of this template file, we should also create one for this new $postVm
variable to add intellisense lookups to this variable. So, let’s create a new typehint using @var
for our new $postVm
variable that references the Macademy\\Blog\\ViewModel\\Post
class.
Now we can create a foreach
loop that calls our Post ViewModel’s getList
function. We will iterate over this, assign the value to $post
. Let’s space out these lines for readability. I also like to take the approach of not caring how this HTML is outputted with indents on the frontend, but instead optimize the indentation for backend readability. This makes your code easier to read from a programming standpoint and really doesn’t have any downsides.
Let’s update the hard-coded id with a call to $post->getData()
, and we will fetch the id. We’ll do the same for the title by calling $post->getData('title')
. We can also just call both of these with getId()
or getTitle()
, I prefer the longhand getData
approach though because it’s more explicit and you can Cmd+Click or Alt+Click into it in IDEs.
Macademy_Blog::post/list.phtml
<?php /** @var Magento\\Framework\\View\\Element\\Template $block */ /** @var Macademy\\Blog\\ViewModel\\Post $postVm */ $postVm = $block->getData('post_vm'); ?> <div class="blog-post-list"> <h1><?= __('Blog posts') ?></h1> <ul> <?php foreach ($postVm->getList() as $post): ?> <li> <a href="<?= $block->getUrl('blog/post/detail', ['id' => $post->getData('id')]) ?>"> <?= $post->getData('title') ?> </a> </li> <?php endforeach ?> </ul> <p><?= __('Post count: %1', 3) ?></p> </div>
Now when we refresh the page, we can see that our new post data is being pulled in from the ViewModel.