0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Twigとjs間の値の受け渡し

Last updated at Posted at 2024-05-31

EC-CUBEの画面の表示内容の制御を行う際に、
TwigとJS間の値の受け渡しで詰まったので、調べたことをまとめます。

《 目次 》

  1. js → Twig は NG、Twig → js は OK
  2. Ajax(非同期通信)でincludeする方法

js → Twig は NG、Twig → js は OK

TwigはPHPで実行されて、javascriptが動く前に動作が完了するので、javasriptの変数をTwigで使用するのは原理的に不可能です。

TwigでControllerから渡した値を使用して、jsにセットする方法

ShoppingController.php
    const PAYMENT_LIST = [
        PaymentType::CREDIT_CARD, // int
        PaymentType::POSTPAID,    // int
        PaymentType::PAYMENT_NONE // int
    ];

    /**
     * 支払方法選択画面を表示する
     *
     * @Route("/order/payment", name="order_payment", methods={"POST"})
     * @Template("Order/payment.twig")
     */
    public function payment(Request $request)
    {

        // $Order を取得

        return [
            'form' => $form->createView(),
            'Order' => $Order,
            'PaymentList' => self::PAYMENT_LIST,
        ];
    }
Order/payment.twig
{% block javascript %}
    <script>
        $(function() {
        
            var payments = [ {{ PaymentList|join(',') }} ];

            var checkedPayments = [];
            $('input:checked').each(function () {
                checkedPayments.push(Number($(this).val()));
                if ($.inArray(Number($(this).val()),payments) > -1) {
                    $(this).addClass('d_none');
                } else {
                    $(this).removeClass('d_none');
                }
                if (checkedPayments.every(payment =>payments.includes(payment))) {
                    $('#button').parent().addClass('none');
                } else {
                    $('.#button').parent().removeClass('none');
                }
            });
        });
    </script>
{% endblock javascript %}        

{% block main %}
    <ul class="radioUl jsCheckList require-input">
        {% for key, item in form.payments.children %}
            <li>
                <label>
                    <input type="radio" id="{{ item.vars.id }}" name="{{ item.vars.full_name }}" value="{{ item.vars.value }}" {{ item.vars.checked ? 'checked' : '' }}{% if key == 1 %} disabled{% endif %}><span>{{ item.vars.label }}</span>
                </label>
            </li>
        {% endfor %}
    </ul>
    {{ form_errors(form.corporate_flg) }}
{% endblock %}

《解説》

1.ControllerからTwigに値を渡し、Twigで値を出力する

{{ 値 }}で出力可能
配列も渡すことができます。

2.Twigからjsに値を渡す

script内にでもTwigの記述方法が有効なので、1の方法で出力可能

3.《応用》配列とinputのvalue等と比較する方法
3-1. Twigの配列をjsで扱う

{{ 値 }}は文字列として扱われるため、jsの配列にそのまま置換することはできません。
そのため、以下の方法でjsの配列にセットする必要があります。

// 1. カンマ区切りの文字列として出力し、jsの配列の初期値としてセット
var payments = [{{ PaymentList|join(',') }}];

// 2. forで取り出してjsの配列に追加
var payments = [];
{% for Payment in PaymentList %}
    payments.push({{ Payment }});
{% endfor %}

3-2. 配列に値が含まれているか比較
if ($.inArray(Number($(this).val()),payments) > -1) {

jQueryのinArray()を使用

inArrayメソッドは、配列を検索する時に「型」も同時にチェックしているという点に注意が必要なわけです。

※ int型のpaymentsに型を合わせるためにNumber()でキャスト

※ クラスの出し分けはaddClass(),removeClass()を使用
$('input:checked').each(function () {で複数のinputに適用

3-3. 配列同士の比較
// checkedPaymentsの要素がすべてpaymentsに含まれるかどうか
if (checkedPayments.every(payment =>payments.includes(payment))) {

■ 配列.some()
意味:配列内の要素で1つでも条件に合致するものがあるかどうか?
戻り値:真偽値

■ 配列.every()
意味:配列内のすべての要素が、条件に合致するかどうか?
戻り値:真偽値

《 おまけ 》 form_widgetは分解できる!

input[type="radio"]の選択肢ごとに挙動を変えたい場合など、for文で分解することができる!

 <ul class="radioUl jsCheckList require-input">
        {% for key, item in form.payments.children %}
            <li>
                <label>
                    <input type="radio" id="{{ item.vars.id }}" name="{{ item.vars.full_name }}" value="{{ item.vars.value }}" {{ item.vars.checked ? 'checked' : '' }}{% if key == 1 %} disabled{% endif %}><span>{{ item.vars.label }}</span>
                </label>
            </li>
        {% endfor %}
    </ul>

Ajax(非同期通信)でincludeする方法

そもそもAjaxって何??

非同期通信とは、一部の情報をサーバーに送信して、それを受け取り反映させる仕組みのこと。

つまり、画面遷移せずにバックエンド処理する手法です。

AjaxはEC-CUBEでもいろいろな場面で使用しています。
例)・検索ボタンを押したら、バックエンドで処理結果を返却して表示する
  ・ファイルをアップロードする際、バックエンドで処理を行いながら、画面に進捗を表示する
etc...

今回やりたかったこと

ロード時間の短縮のために、画面読み込みを同時並行で行いたい。
(カート画面からおすすめ商品エリアを切り離して読み込みする)

《実装内容》

CartController.php
    /**
     * カート画面.
     *
     * @Route("/cart", name="cart", methods={"GET", "POST"})
     * @Template("Cart/top.twig")
     */
    public function index(Request $request)
    {
        
        // カート処理
    
         return ['Carts' => $ViewCarts];
    }
Cart/top.twig
{% block javascript %}
    <script>
        $(function() {
            // おすすめ商品の表示
            viewLoading('recommend_area');
            $.ajax({
                url: '{{ url('recommend_group_list', {'type': 1}) }}',
                type: 'GET',
            }).done(function(data) {
                $.when(
                    $('#recommend_area').html(data)
                ).done(function(){
                    // 商品画像スライド表示
                    slickload();
                    // ボタン連打対応
                    doubleClickGuard(".recommend_item_view_btn");
                });
            }).fail(function() {
                console.log('Failed');
            });
        });

        // ローディング中画像表示
        function viewLoading(eid) {
            $('#'+eid).html('<div class="loading_icon"></div>');
        }
    </script>
{% endblock javascript %}

{% block main %}

    {# カート 表示内容 #}


    {# おすすめ商品 #}
    <div id="recommend_area" class="completeSec"></div>
    
{% endblock %}
RecommendController.php
    /**
     * おすすめ商品の表示
     * ※会員毎に出しわけます。
     * type -> 1:カート、2:注文完了
     *
     * @Route("/recommend/{type}/groups", name="recommend_group_list", methods={"GET"})
     * @Template("Recommend/group_list.twig")
     */
    public function loadRecommendItemGroups(Request $request, $type)
    {
        log_info("おすすめ商品の表示 : " . $type);
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
            // ログインしている場合は、会員情報を取得
            $Customer = $this->getUser();
        } else {
            $Customer = null;
        }

        // おすすめ商品の取得
        $recommendItemGroups = $this->recommendItemGroupRepository->findRecommendGroupByCustomerRank($type, $Customer);

        return ['RecommendItemGroups' => $recommendItemGroups];
    }
Recommend/group_list.twig
{% if RecommendItemGroups|length > 0 %}
    <section>

        {# おすすめ商品 表示内容 #}
    
    </section>
{% endif %}

《解説》

1.カート画面表示

Controllerで処理をしてTwigでカート画面を表示する
Twig読み込み → Ajax発火 → url(recommend_group_list)にアクセス

    $.ajax({
        url: '{{ url('recommend_group_list', {'type': 1}) }}',
        type: 'GET',   
    })
2.おすすめ商品を取得

Controllerで処理をしてTwigを返却

ただし、返却先はAjaxのため、そのまま画面表示することはできないのでので要注意!

3.Ajaxで表示処理

Ajax処理が完了したら、カート画面上のおすすめ商品表示エリアに、戻り値のtwigの内容をセットする

    
    }).done(function(data) {
        // 成功した場合の処理
        $.when(
            $('#recommend_area').html(data)
        ).done(function(){
            // 商品画像スライド表示
            slickload();
            // ボタン連打対応
            doubleClickGuard(".recommend_item_view_btn");
        });      
    }).fail(function() {
        // 失敗した場合の処理  
         console.log('Failed');
    });

.when().done()を使って、Twigの内容が完全に読み込まれた後に、画像スライド表示のjsを起動します。

.when()の記述方法は少し特殊なので、以下を参考にすること。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?