3

Assume I've got something like this

<div class="greet welcome" data-bind="scope: 'customer'">
    <!-- ko if: blockIsLoggedIn == '1'  -->
    <span data-bind="text: new String('<?= $block->escapeHtml(__('According to the block logic you are currently logged In')) ?>')"></span>
    <!-- /ko -->
    <!-- ko ifnot: blockIsLoggedIn == '1'  -->
    <span data-bind="text: new String('<?= $block->escapeHtml(__('as according to the block logic you are not currently logged in')) ?>')"></span>
    <!-- /ko -->
</div>
<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "customer": {
                    "component": "Magento_Customer/js/view/customer",
                    "blockIsLoggedIn": "<?= $block->escapeJs($block->isCustomerLoggedIn()); ?>"
                }
            }
        }
    }
}
</script>

And my block logic looks a bit like this

    /**
     * Check is Customer Logged In
     * @return int
     */
    public function isCustomerLoggedIn()
    {
        $isLoggedIn = $this->customerContext->getValue(\Magento\Customer\Model\Context::CONTEXT_AUTH);
        return (bool) $isLoggedIn ? 1 : 0;
    }

The trouble is the blockIsLoggedIn value is cached in FPC on page load. Which means you can't reliably use this approach.

Is it possible to convert this so this variable comes from an ajax lookup on a controller instead?

I'm aware of other ways in which to check whether you are logged in. This is just an example. I've had similar scenarios recently and I end up just using jQuery. When I use jQuery I feel like I'm reinventing the wheel.

Update

My logged in example has caused confusion. Let's say I want to use KO to fetch a review count for currently viewed product (I assume via ajax request) on product page - on page load - that works through FPC.

1
  • In summary I want to define a KO variable against a value returned from ajax request Commented Aug 4, 2020 at 19:01

3 Answers 3

3
+25

If your create your custom uicomponent like below

<script type="text/x-magento-init">
    {
      "*":{
          "Magento_Ui/js/core/app": {
              "components": {
                  "customer": {
                      "component":"Mbs_PunchHole/js/checklogin"                  
                  }
              }
          }
      }
    }
</script>

and your javascript file like below:

define([
    'uiComponent',
    'Magento_Customer/js/model/customer'
], function (Component, customer) {
    'use strict';

    return Component.extend({      
        blockIsLoggedIn: function() {
           return customer.isLoggedIn();
        }
    });
})


<div class="greet welcome" data-bind="scope: 'customer'">
    <!-- ko if: blockIsLoggedIn == '1'  -->
    <span data-bind="text: new String('<?= $block->escapeHtml(__('According to the block logic you are currently logged In')) ?>')"></span>
    <!-- /ko -->
    <!-- ko ifnot: blockIsLoggedIn == '1'  -->
    <span data-bind="text: new String('<?= $block->escapeHtml(__('as according to the block logic you are not currently logged in')) ?>')"></span>
    <!-- /ko -->
</div>

In the above snippet, the line customer.isLoggedin is an observable.

If you open the file vendor/magento/module-customer/view/frontend/web/js/model/customer.js, you will see it quite clearly.

Now, the technology of observable and escaping FPC issue relies on the section data. I suspect deep down, you'd prefer have a php/phtml solution rather than using javascript like the above solution.

If you want to keep your php function and yet enable an observable variable, you will need to take a look at how implementing a customer-data section in Magento.

the module https://bitbucket.org/magstaging/punchhole/src/master/ does pretty much just that.

5
  • Thanks for the feedback. I had a feeling that my example would confuse the issue. I've done that sort of thing github.com/DominicWatts/CustomerLogin for a login check. To clarify my issue has been if I want to detect currently viewed product or similar this is where that approach falls down I think. Ideally a need a scenario where I can pass a parameter like a product ID of current product. Thats why I went the ajax controller route. Commented Aug 3, 2020 at 8:04
  • no problem Dominic, let's see how the community feels the gap.. I will try to make a more relevant answer but I am a bit tight at the moment starting the week Commented Aug 3, 2020 at 17:35
  • I have now installed your module. However, It is not obvious how to use it. Are you able to put a README or something? Commented Aug 4, 2020 at 18:42
  • The above module is the comment? It's just a proof of concept. Login in as customer and go /xigen_customerlogin. But it only relates in terms using private content. It doesn't really relate in terms of what's being asked. Although with FPC enabled it might demonstrate my point. Commented Aug 4, 2020 at 18:58
  • This will work on checkout pages that are non cacheable. Outside checkout you should use `Magento_Customer/js/customer-data'. See my answers. But other than that I agree and your module example is good example to check. Commented Aug 6, 2020 at 21:18
1

I guess it is not about AJAX but about checking i user is logged or not in knockout js context. Then here is your answer:

In general:

You can use **customer-data.js (**outside of checkout context)

define([
    'Magento_Customer/js/customer-data',
], function(customerData) {
    //...
    var customer = customerData.get('customer');

    if (customer().fullname && customer().firstname) {
        // loged
    }
    else {
        // not logged
    }
    
});

In checkout context you can use Magento_Customer/js/model/customer.js

define([
    'Magento_Customer/js/model/customer'
], function(customer) {
    'use strict';

    if(customer.isLoggedIn()) {
        // ...
    }
    else {
      // ..
    }
});

Also you use translation module in JS

instead of:

<span data-bind="text: new String('<?= $block->escapeHtml(__('as according to the block logic you are not currently logged in')) ?>')"></span>

do this:

<span data-bind="text: getMessage()">

and in your js define dependency 'mage/translate' and use it to set variable/ko variables like

$t('Some string')

In your case:

I see here you want to edit template used by 'Magento_Customer/js/view/customer'

I guess you could use here mixin:

customer-mixin.js (in your theme Magento_Customer/js/view/) or in your module

define([
  'mage/translate'
], function ($t) {
  'use strict';

  var mixin = {
    getMessage: function() {
      if (this.customer().fullname && this.customer().firstname) {
        return $t('Your message for logged users that can be translated');
      } else {
        return $t('Your message for non logged users that can be translated');
      }
    }
  }

  return function(target) {
    return target.extend(mixin);
  };
});

and in requirejs-config.js (in your theme in Magento_Customer/require-config.js) or in your module (then path to mixin will be different)

var config = {
  config: {
    mixins: {
      'Magento_Customer/js/view/customer': {
        'Magento_Customer/js/view/customer-mixin': true
      },
    }
  }
};

I wrote it more or less from memory so if there will be any issues let me know.

UPDATE:

You are right. To fetch count reviews you can use ajax. Check how Magento module review works.

Example: http://demo-acm-2.bird.eu/joust-duffle-bag.html

Look at process-reviews.js However they could add there perhaps some localStorage to cache result.

In general you should use JS and ajax request or and in some cases customer data sections.

I also recommend to look at https://devdocs.magento.com/guides/v2.4/extension-dev-guide/cache/page-caching/private-content.html and public content section

And if needed read few articles related to using sections like: https://amasty.com/blog/how-to-use-sections-in-magento-2

1
  • Makes sense although let's say I want to fetch a review count for currently viewed product on load that works through FPC. I think maybe my logged in example has confused the issue. Although your translation point is useful to know. Commented Aug 6, 2020 at 19:28
0

I'm a backend dev so my frontend skills are limited. But I've put together something to demonstrate what my current understanding is how to achieve what I'm describing. But it seems to me like it's long winded and I'm very much reinventing the wheel or that there is an easier way to achieve what I'm doing.

https://github.com/DominicWatts/Ajax

So I've got a controller which could do anything dynamic

https://github.com/DominicWatts/Ajax/blob/87cca1700e9e0ca1fa3da3e4bd060037d2b149fb/Controller/Ajax/Fetch.php#L43-L49

            return $this->jsonResponse([
                'reviewFound' => true,
                'reviewWording' => __(
                    "%1 reviews for product %2",
                    22,
                    $product
                )]);

By anything I mean literally anything. You are not bound by the restrictions of what private data functionality currently does and you don't have to use plugins to get it to do more.

If you go here review/ajax/index

You see this

https://github.com/DominicWatts/Ajax/blob/master/view/frontend/templates/ajax/index.phtml

Hello ajax/index.phtml

<input type="hidden" id="product-id" value="123" />

<div class="greet welcome" data-bind="scope: 'ajax'">
    <!-- ko template: getTemplate() -->
    <p data-bind="text: html"><?= $block->escapeHtml('Review count to go here'); ?></p>
    <!-- /ko -->
</div>

<script type="text/x-magento-init">
    {
        "*": {
            "Magento_Ui/js/core/app": {
                "components": {
                    "ajax": {
                        "component": "Xigen_Ajax/review/ajax",
                        "template" : "Xigen_Ajax/review/ajax"
                    }
                }
            }
        }
    }
</script>

Inside my script

https://github.com/DominicWatts/Ajax/blob/master/view/frontend/web/review/ajax.js

I've got the ajax requst

https://github.com/DominicWatts/Ajax/blob/87cca1700e9e0ca1fa3da3e4bd060037d2b149fb/view/frontend/web/review/ajax.js#L16-L19

            var url = '/review/ajax/fetch',
                param = 'product=' + $('#product-id').val(),
                reviewWording,
                reviewFound = false;

Which catchs the paramater to send from the hidden input, fetches and then assigns the values for the template based on the result from the controller.

https://github.com/DominicWatts/Ajax/blob/87cca1700e9e0ca1fa3da3e4bd060037d2b149fb/view/frontend/web/review/ajax.js#L34-L36

            this.response = reviewWording;
            this.reviewFound = reviewFound;
            this.observe(['response']);

Then in my template https://github.com/DominicWatts/Ajax/blob/master/view/frontend/web/template/review/ajax.html

I can do something like this

<p data-bind="html: response"></p>
<!-- ko if: reviewFound == '1'  -->
<span>Review is found</span>
<!-- /ko -->
<!-- ko ifnot: reviewFound == '1'  -->
<span>Review is not found</span>
<!-- /ko -->

Which gives me the following

enter image description here

And here is my ajax response

enter image description here

And here is Knockout context debugging show variables set

enter image description here

3
  • 1
    More or less this works. You use ajax in javascript uiComponent and that's ok. But don't use async:false. Set default values for reviewFound and response (check how it is done in shipping.js in module-checkout. See how is added: 'errorValidationMessage: ko.observable(false) and later used in other methods in this file). After removing async false use done and complete callback. See process-reviews.js from module-review as an example. Commented Aug 6, 2020 at 21:57
  • Can you demonstrate? Thanks Commented Aug 6, 2020 at 23:17
  • 1
    Check - github.com/DominicWatts/Ajax/pull/1 not ideal. Just wanted to show in which direction you should go. Commented Aug 7, 2020 at 1:44

Your Answer

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

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