magento / magento2

Prior to making any Submission(s), you must sign an Adobe Contributor License Agreement, available here at: https://opensource.adobe.com/cla.html. All Submissions you make to Adobe Inc. and its affiliates, assigns and subsidiaries (collectively “Adobe”) are subject to the terms of the Adobe Contributor License Agreement.
http://www.magento.com
Open Software License 3.0
11.56k stars 9.32k forks source link

Linking custom javascript through template header question #1731

Closed CoreyCole closed 9 years ago

CoreyCole commented 9 years ago

I'm trying to link a module-specific custom javascript file to phtml template.

var config = {
    map: {
        '*': {
            discountCode:           'Magento_Checkout/js/discount-codes',
            shoppingCart:           'Magento_Checkout/js/shopping-cart',
            regionUpdater:          'Magento_Checkout/js/region-updater',
            opcOrderReview:         'Magento_Checkout/js/opc-order-review',
            sidebar:                'Magento_Checkout/js/sidebar',
            payment:                'Magento_Checkout/js/payment',
            paymentAuthentication:  'Magento_Checkout/js/payment-authentication',
            succes:                 'Magento_Checkout/js/success' // <-- my custom javascript
        }
    }
};
<action method="addItem">
   <type>js</type>
   <name>../web/js/success.js</name>
   <params/>
</action>

I've looked at the dev docs page that talks about custom javascript, but adding header links this way doesn't seem to be explained there. I'm not trying to replace or extend a widget - I'm just trying to add an onload function to a page so that it can make a web service call.

Is there a better magento2 way of linking custom javascript to a phtml template? Or is this old way of adding links to the header through layout xml explained elsewhere or not documented yet?

guz-anton commented 9 years ago

Hi Corey, As about requirejs cofig: it should looks like

var config = {
    map: {
        '*': {
            succes:                 'Magento_<Module>/js/success' // <-- my custom javascript
        }
    }
};

in case you want to overwrite whole js file by your own. It is enough to overwrite only one string rather than whole requirejs-config.js.

But Javascript app from Checkout doesn't use anywhere success.js. So looks like you file would never be called.

zanilee commented 9 years ago

Hi, @CoreyCole! To include a script which lives in Magento/<Module>/view/frontend/web/js/ via layout, you should add the following code to the module’s page-specific layout xml:

<head>
  <script src="Magento_Checkout::js/success.js"/>
</head>

There’s an example for including script to a theme in Frontend Developer Guide.

CoreyCole commented 9 years ago

Thanks @zanilee, so in app/design/frontend/Magento/blank/Magento_Checkout/layout I made a file named success_head_blocks.xml modeled after default_head_blocks.xml like directed in the frontend dev guide containing:

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <head>
        <script type="text/javascript" src="Magento_Checkout::js/success.js"></script>
    </head>
</page>

But I'm still not getting a signal that the javascript has been linked to the success page. I tried adding this line to app/code/Magento/Checkout/etc/frontend/page_types.xml

<type id="success_head_blocks.xml" label="Head link for success page javascript"/>

But still no luck.

zanilee commented 9 years ago

Hi, @CoreyCole! To link your script on the Success page in a frontend theme, you should extend the checkout_onepage_success.xml file. To do this, creat the checkout_onepage_success.xml file in /app/design/frontend/<Vendor>/<theme>/Magento_Checkout/layout/ And in this file add the link to your script. So the file code will look like this:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <head>
        <script src="Magento_Checkout::js/success.js "/>
    </head>
</page>
CoreyCole commented 9 years ago

Thanks @zanilee! Got it to link - just had to rename the file. For clarity for anyone viewing this thread in the future, my checkout_onepage_success.xml file lives in /app/design/frontend/<Vendor>/<theme>/Magento_Checkout/layout/ and my javascript file lives in /pub/static/frontend/<Vendor>/<theme>/en_US/Magento_Checkout/js/

CoreyCole commented 9 years ago

Now I've run in to issues loading the jQuery library - getting Uncaught ReferenceError: jQuery is not defined. With my javascript from old Magento:

"use strict";
jQuery.noConflict();
(function($) {
    $(function() {
        alert("my man");
    });
})(jQuery);

I've also tried using define, like mentioned in the docs here, but I get Uncaught Error: Mismatched anonymous define() module: with the following js:

define(["jquery"], function($){
    alert("my man");
})(jquery);

But I don't think that's necessary because when I look in Chrome's debug console, jquery.js is loaded no matter what. What am I doing wrong here?

CoreyCole commented 9 years ago

Got it to work. Couldn't wrap everything anonymously. I'll paste my working code below:

require(["jquery"], function($){
    alert("my man");
});
guz-anton commented 9 years ago

Just notes for history :

"use strict";
jQuery.noConflict();
(function($) {
    $(function() {
        alert("my man");
    });
})(jQuery);

This code in Magento JS environment will have an issue with async JS load. jQuery is loaded via RequireJs. So sometime or always code can produce ReferenceError: jQuery is not defined. So your JS code should be covered with define or require.

define(["jquery"], function($){
    alert("my man");
})(jquery);

the part (jquery) is redundant. Also define is used for defining components. It's callback isnot called until someone requires it.

require(["jquery"], function($){
    alert("my man");
});

Last example finally uses require to list it's dependencies. Its good. But this example missed use strict notation and waiting for DOM Ready event.

So final example could look like:

require([
    'jquery',
    'domReady!'
], function($){
    `use strict`;

    alert( 'my man' );
});

or with less dependencies

require([
    'jquery'
], function($){
    `use strict`;

    $(function () {
        alert( 'my man' );
    });
});
Vinai commented 9 years ago

@guz-anton Thank you, this is really useful to a backend guy like me!

cs221313 commented 8 years ago

When I put head in default.xml file. I found I can call the js file directly. I don't need requirejs-config.js.

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <head>
        <script src="js/topads.js "/>
    </head>
</page>

My js file is at app/design/frontend/MyNamespace/MyTheme/web/js/topads/js. I don't think it's correct way. Can anyone help me to explain it?

guz-anton commented 8 years ago

Hi David,

I found I can call the js file directly

You can put custom .js file on the page via layout. This example correct from technical point of view:

<page>
    <head>
        <script src="js/topads.js "/>

But it will be loaded on every page inside area.

I don't think it's correct way.

Main question: what do you want to do in JS file? Common task is to manipulate with Html node or communicate with other widgets. In first case it handy to use jQuery -> so you should list it in own dependencies. In second case you should wait for loading external widgets (It doesn't happen immediate). -> so you should list them in own dependencies.

correct way

  1. Use define in your custom JS file
  2. Load JS file via declarative binding.
cs221313 commented 8 years ago

Thank you so much for your reply.

I think my js is belong the first case that you mentioned, right? my js code is as following:

require(['jquery', 'domReady!'], function($){ 'use strict'; $(function () { var oClose_input = $('.close_remind input'), oClose_div = $('.close_remind div.close_remind_img'), oHead_advertising = $('.head_advertising'), close_remind = 'close_remind', oValue = cookieGet('close_remind'), oDate = new Date(), oHours = oDate.getHours(), oValue = cookieGet(close_remind); if(oValue){ oHead_advertising.css({display:"none"}); } //0点0分 //误差一分钟; if(!parseInt(oHours)){ if(!oDate.getMinutes()){ oHead_advertising.css({display:"block"}); //到了明天的00点00分清除cookie值; delCookie(close_remind); } } oClose_div.click(function (){ if(oClose_input[0].checked){ cookie(close_remind,true,1); } oHead_advertising.css({display:'none'}); }); }); });

cookie = function (name,value,iDay){ if(iDay){ var oDate=new Date(); oDate.setDate(oDate.getDate()+iDay); document.cookie=name+'='+value+';path=/;expires='+oDate.toGMTString(); }else{ document.cookie=name+'='+value+';path=/';
} }; cookieGet = function (name){ var arr=document.cookie.split('; '); for(var i=0; i<arr.length; i++){ var arr2=arr[i].split('='); if(arr2[0]==name){ return arr2[1]; } } return ''; }; delCookie = function (name){ jQuery.cookie(name,'asdf',-10); };

The purpose is to hide the top ads when click to close button. The feature is at the www.whoo.com.cn

I did not use requires-config.js file. Does this file only needed in second case? image

I have a question what's the difference between define and require?