8

I am initializing the same uiElement multiple times on the same page (on checkout_cart_index / one per cart item)

 <div class="configuration-check" data-mage-init=
 '
 { "Magento_Ui/js/core/app":
 {"components":
 { "configurationCheck" :
 {
 "component": "configurationCheck",
 "template" : "Example_Upload/configuration-check",
 "config" : <?php echo $itemInfo->asJson() ?>
 }
 }
 }
 }
 '
 data-bind="scope:'configurationCheck'"
 >
 <!-- ko template: getTemplate() --><!-- /ko -->
 </div>

configuration-check.js looks like this:

define([
 'uiComponent'
], function (Component) {
 'use strict';
 return Component.extend({
 defaults: {
 template: 'Example_Upload/configuration-check'
 },
 /** @inheritdoc */
 initialize: function () {
 this._super();
 return this;
 },
 getInfo: function (field) {
 return this[field];
 }
 });
});

In the template I use getInfo() to get the config data which was given in the initialization.

Now the effect is, that both cart items show the same data, even they are different.

Can this easily be fixed or am I doing a completely wrong approach?

EDIT: I found how to create multiple instances of a view - but how can this be done during Magento's initialization of the components?

EDIT2: I think I need a different component name for each instance. Maybe it can work with a different name or namespace setting in the JSON passed to data-mage-init - but I did not yet find the right combination to make it work.

asked Nov 24, 2017 at 9:16
1
  • I could not find a solution for this. I tried with different component name but it did not work. Could you help me? Commented Jan 17, 2021 at 15:07

2 Answers 2

6

Just after putting the bounty I found the answer myself:

Multiple instances are created, when the key below the "components" structure has a unique ID. The same shall be used in the data-bind attribute:

$htmlId = $block->getJsId() // returns a unique ID
 <div class="configuration-check" data-mage-init=
 '
 { "Magento_Ui/js/core/app":
 {
 "components":
 { "configurationCheck-<?php echo $htmlId ?>" :
 {
 "component": "configurationCheck",
 "template" : "Example_Upload/configuration-check",
 "config" : <?php echo $itemInfo->asJson() ?>
 }
 }
 }
 }
 '
 data-bind="scope:'configurationCheck-<?php echo $htmlId ?>'"
 >
 <!-- ko template: getTemplate() --><!-- /ko -->
 </div>

That's it ... so simple.

Update 2022:

Values defined inside default: are being shared between component instances.

Values defined inside initialize() method are scoped

define(['uiComponent','jquery', 'ko', 'mage/translate'],
 function (Component, ,ドル ko, $t) {
 'use strict';
 return Component.extend({
 defaults: {
 template: 'Example_Upload/configuration-check'
 },
 initialize: function (config) {
 this._super();
 this.configCheck = ''; // scoped
 },
 });
 });
Miroslav Petroff
1,8821 gold badge16 silver badges22 bronze badges
answered Nov 27, 2017 at 19:31
2
  • 4
    This is not working for me, after changing with unique ID still loading same data for all the items. doing exactly same as you have suggested. Commented Jun 25, 2018 at 10:58
  • 2
    @ParthThummar seems like values on default: { } scope still being shared, but if you assign them in initialize() function then the values get separeted Commented Jan 26, 2022 at 11:26
-1

I solved this problem without HTML template (using phtml only), but your can adapt to work with HTML template too.

First, declare an unique name to section and pass a unique id to the component (can be the same id):

<script type="text/x-magento-init">
{
 "*": {
 "Magento_Ui/js/core/app": {
 "components": {
 "section-<?php echo $block->getJsId(); ?>": {
 "component": "Vendor_ModuleName/js/sampleModule",
 "jsId": "<?php echo $block->getJsId(); ?>"
 }
 }
 }
 }
}
</script>

Now at your component in the initialize function, you will declare your observable variable with a dynamic name using eval:

...
initialize: function() {
 this._super();
 eval('this.someVarName' + this.jsId + ' = ' + ' ko.observable({}) ' + ';')
...

It will create an observable variable with a name like someVarName-blockName-0, after you can apply data to the dynamic variable:

...
 let var1 = { sample: 'test' }
 eval('this.someVarName' + this.jsId + ' ( ' + ' var1 ' + ' ) ' + ';')
...

Finally, on your phtml you can bind the data:

<!-- ko scope: 'section-<?php echo $block->getJsId(); ?>' -->
 <!-- ko if: someVarName<?php echo $block->getJsId(); ?>() -->
 <p data-bind="text:someVarName<?php echo $block->getJsId(); ?>().sample"></p>
 <!-- /ko -->
<!-- /ko -->

It will bind the variable someVarName-blockName-0().sample

answered Oct 27, 2021 at 11:39
2
  • The Alex suggestion doesn't work properly. And you shouldn't down the question just because for you is difficult. Commented Apr 19, 2022 at 11:17
  • 1
    Alex's suggestion does work, I implemented it and it has already been deployed to production with hundreds of people using it. Look at ivanaugustobd's comment. I do not think your answer is "difficult", it is just unnecessarily complicated. If we can solve the issue with one line of code, there is no reason to do all that stuff. That is exactly how the downvote works. Commented Apr 19, 2022 at 16:03

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.