Develop new widget - Part 3 - Use multiple widget instances in the Layout Editor
From TomatoCMS Documentation
At the editing layout page, I assume that we want to show the links in 3 containers. Each container has 4 columns.
First, you can create 12 columns container, then drag and drop 4 columns container to it. Finally, drag and drop the links widget to 4 columns container. And repeat this step 3 times.
Contents |
TIP
Instead of dragging and dropping the widget from the top of page to the container, you can click on the [+] icon in the header bar of the widget. It will create new widget instance. Then you can drag and drop the clone widget to other container.
It is easy to show multiple widget instances in the Layout Editor.
But, there is a problem. When I try to click link in the Link Provider or click on the Add button, it also add the link to all of widget instances:
We can find out why this issue occurs easily. Because in the widget configuration form, there is no way to distinguish the widget instances.
For example, in the adding link function:
/application/modules/utility/widgets/links/config.phtml
... <div class="t_a_top"> <table id="utilityLinksTable" class="t_a_ui_table"> ... </table> </div> ... <script type="text/javascript"> function addLink(route, url, title) { ... $(tr).append($(td)).appendTo($('#utilityLinksTable tbody')); $('#utilityLinks').val($.toJSON(getLinks())); }; ... </script>
If we have 3 widget instances in the Layout Editor, we also have 3 configuration forms, and therefore there are 3 elements having the same Id of utilityLinksTable.
That is why the new link will be added to all of widget instances.
This part shows you how to resolve this issue.
So, if you want to use multiple instances of same widget when using the Layout Editor (of course when there is Javascript code in your widget configuration form), please follow the guides below:
Wrapping entire configuration form in a div container
Put entire widget configuration form in a div tag, and set a class for the div container.
Here is an example showing that I put widget configuration form in a div with class of t_utility_links:
/application/modules/utility/widgets/links/config.phtml
<link href="<?php echo $this->APP_STATIC_SERVER; ?>/js/jquery.ui/themes/base/ui.core.css" media="screen" rel="stylesheet" type="text/css" /> <link href="<?php echo $this->APP_STATIC_SERVER; ?>/js/jquery.ui/themes/base/ui.accordion.css" media="screen" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="<?php echo $this->APP_STATIC_SERVER; ?>/js/jquery.ui/ui.core.js"></script> <script type="text/javascript" src="<?php echo $this->APP_STATIC_SERVER; ?>/js/jquery.ui/ui.accordion.js"></script> <div class="t_utility_links"> ... </div> <script type="text/javascript"> ... </script>
Using CSS class selector instead of ID selector
To get the element in the configuration form, we should use CSS class selector instead of ID selector.
For example:
/application/modules/utility/widgets/links/config.phtml
BEFORE:
... <input type="hidden" id="utilityLinks" name="links" class="t_widget_input" /> <script type="text/javascript"> ... function removeLink(link) { $(link).parents('tr').remove(); $('#utilityLinks').val($.toJSON(getLinks())); return false; }; ... </script>
AFTER:
... <div class="t_utility_links"> ... <input type="hidden" class="utilityLinks" name="links" class="t_widget_input" /> </div> <script type="text/javascript"> ... function removeLink(link) { // (1) var root = $(link).parents('.t_utility_links'); $(link).parents('tr').remove(); // (2) $(root).find('.utilityLinks').val($.toJSON(getLinks())); return false; }; ... </script>
As you see, we can access the element in widget configuration form by accessing the widget container first (1) then find the element via CSS class selector (2).
Using namespace for Javascript functions
If you look at the configuration form, you will see that we create some Javascript functions:
/application/modules/utility/widgets/links/config.phtml
... <script type="text/javascript"> function addLink(route, url, title) { ... }; function removeLink(link) { ... }; function getLinks() { ... }; ... </script>
What happen if in the Layout Editor, there is another widget in other module having Javascript methods with the same name?
So, in this case, you should create a class and put all Javascript methods to our class.
For instance, first, I will create a new namespace:
... <script type="text/javascript"> 'Utility.Widgets.Links'.namespace(); ... </script>
Then, moves all Javascript functions to the namespace. In this case, each function will be a static method:
... <script type="text/javascript"> 'Utility.Widgets.Links'.namespace(); Utility.Widgets.Links = function() {}; Utility.Widgets.Links.addLink = function(route, url, title, link) { ... }; Utility.Widgets.Links.removeLink = function(link) { ... }; Utility.Widgets.Links.getLinks = function(root) { ... }; </script>
Now, it is safe to make settings for multiple instances of widget using the Layout Editor:
Below is the final result after creating 3 instances of the links widget:




