3

When we worked directly with blocks, it was easy to pass arguments to the block in layout (e.g. default.xml) like:

<block template="..." class="..." name="...">
    <arguments>
        <argument name="foo" xsi:type="string">bar</argument>
    </arguments>
</block>

When now using view models (I know the advantages of using view models), I don't see a way to pass arguments, hence I often need to reject view models and use the block directly again.

Here is how a view model is used:

<block name="orangeco.new.viewmodel" template="OrangeCompany_Catalog::example.phtml">
    <arguments>
        <argument name="view_model" xsi:type="object" shared="[true|false]">OrangeCompany\Catalog\ViewModel\MyNewViewModel</argument>
    </arguments>
</block>

The view model is a shared instance by default (which is good), so in the shared case it doesn't make any sense to pass arguments in the layout. However, I sometimes want to reuse the block type (and the view model type), hence, passing arguments to the view model like we did with blocks would be desirable.

Is there a way to use view models and be able to customize them via arguments from layout?

1

In fact, the answer is quite simple, although a little bit more effort is required. We can use virtualTypes to achieve the effect.

For the example in question we can add the following to the etc/di.xml file:

<virtualType name="OrangeCompany\Catalog\ViewModel\CustomViewModel" type="OrangeCompany\Catalog\ViewModel\MyNewViewModel">
    <arguments>
        <argument name="foo" xsi:type="string">bar</argument>
    </arguments>
</virtualType>

We can then use it in layout xml file:

<block name="orangeco.new.viewmodel" template="OrangeCompany_Catalog::example.phtml">
    <arguments>
        <argument name="view_model" xsi:type="object">OrangeCompany\Catalog\ViewModel\CustomViewModel</argument>
    </arguments>
</block>

So an entry to di.xml is required. Regarding the sharing of the instances, only instances of the same virtualType are shared.

1

There is another mechanism which may work depending on exactly what you're doing.

Add the argument you want to the block, along with the view model itself. Then in your template, when you call a method of the view model, pass the argument from the block. Like this:

<block name="orangeco.new.viewmodel" template="OrangeCompany_Catalog::example.phtml">
<arguments>
    <argument name="view_model" xsi:type="object">OrangeCompany\Catalog\ViewModel\MyNewViewModel</argument>
    <argument name="foo" xsi:type="string">bar</argument>
</arguments>

Then in the template

$block->viewModel->someViewModelMethod($block->foo);

Not ideal, as the argument is not going into the ViewModel's constructor, but on the plus side, it places the argument in the layout.xml, not the di.xml, so it should be easier to follow the code.

1
  • Thanks. This is a way to consider, indeed. Best would be a solution like it is done with helper classes: ``` <block class="..." name="..."> <arguments> <argument name="..." xsi:type="helper" helper="HelperClass::helperMethod"> <param name="name">value</param> </argument> </arguments> </block> ``` Maybe I will submit a feature request. – Creepin Nov 12 '20 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.