-
Notifications
You must be signed in to change notification settings - Fork 106
Adding new types of nodes
- Add a new backend block. It should implement
\Snowdog\Menu\Api\NodeTypeInterface
. Define it indi.xml
. The block will be directly injected into the menu editor. - Create a new vue component for admin and define it in
di.xml
. - Create a node template in
view/frontend/templates/menu/node_type
.
The menu tree configurator in admin panel is built with Vue.js.
Every node type has its own vue component located in view/adminhtml/web/vue/menu-type
. You can check existing components for reference.
UI initialization starts in view/adminhtml/templates/menu/nodes.phtml
. We initialize menu.js
and pass a list of paths of Vue components that are assigned to each node type using "vueComponents"
property:
$vueComponents = $block->getVueComponents();
<script type="text/x-magento-init">
{
"*": {
"menuNodes": {
"vueComponents": <?= json_encode($vueComponents) ?>,
// ...
}
}
}
</script>
To show a new node in UI we need to add a new vue component via di.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Snowdog\Menu\Model\VueProvider">
<arguments>
<argument name="components" xsi:type="array">
<item name="component_name" xsi:type="string">component-file-name</item>
</argument>
</arguments>
</type>
</config>
We need to define here:
-
component_name
: for examplecms_block
-
component-file-name
: for examplecms-block
Then in view/adminhtml/web/vue/menu-type/
we need to add {component-file-name}.vue
, for example cms-block.vue
.
In the new vue file, we need to register our component (component_name
ex. cms_block
):
<template>
...
</template>
<script>
define(['Vue'], function(Vue) {
Vue.component('component_name', {
props: {
// ...
},
template: template,
// ...
})
})
</script>
Replace component_name
with a proper name, for example: cms_block
.
Newly created block with additional method should be added via di.xml
defining block instance and node type code (code will be stored in database):
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Snowdog\Menu\Model\NodeTypeProvider">
<arguments>
<argument name="providers" xsi:type="array">
<item name="my_node_type" xsi:type="object">Foo\Bar\Block\NodeType\MyNode</item>
</argument>
</arguments>
</type>
</config>
-
my_node_type
: for example:cms_block
(the same ascomponent_name
) -
Foo\Bar\Block\NodeType\MyNode
: for example:Snowdog\Menu\Block\NodeType\CmsBlock
In our cms_block
example it would be:
<item name="cms_block" xsi:type="object">Snowdog\Menu\Block\NodeType\CmsBlock</item>
When saving menu changes we send a form postrequest that contains several fields like:
form_key, id, title, identifier, css_class, stores[], serialized_nodes
.
serialized_nodes
data is stored in a hidden field created with ui_Component in snowmenu_menu_form.xml
. It has to be updated to save menu data. A watcher in app.vue
watches the jsonList
element. When data changes, it triggers a custom event and updates data in serialized_nodes
field.
In mounted
hook, we wait for the UI Component hidden field to be loaded and then update its value by passing the JSON list.
app.vue:
// watcher
watch: {
jsonList: function (newValue) {
this.updateSerializedNodes(newValue)
}
},
// update method:
updateSerializedNodes(value) {
const updateEvent = new Event('change');
const serializedNodeInput = document.querySelector('[name="serialized_nodes"]');
// update serialized_nodes input value
serializedNodeInput.value = value;
// trigger change event to set value
serializedNodeInput.dispatchEvent(updateEvent);
}
The list
and item
objects are passed from app.vue
to child components.
As they are objects, they are passed by reference, and editing them in child components updates the value of serialized_nodes
in app.vue
.