EC-CUBEの画面の表示内容の制御を行う際に、
TwigとJS間の値の受け渡しで詰まったので、調べたことをまとめます。
《 目次 》
- js → Twig は NG、Twig → js は OK
- Ajax(非同期通信)でincludeする方法
js → Twig は NG、Twig → js は OK
TwigはPHPで実行されて、javascriptが動く前に動作が完了するので、javasriptの変数をTwigで使用するのは原理的に不可能です。
TwigでControllerから渡した値を使用して、jsにセットする方法
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,
];
}
{% 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...
今回やりたかったこと
ロード時間の短縮のために、画面読み込みを同時並行で行いたい。
(カート画面からおすすめ商品エリアを切り離して読み込みする)
《実装内容》
/**
* カート画面.
*
* @Route("/cart", name="cart", methods={"GET", "POST"})
* @Template("Cart/top.twig")
*/
public function index(Request $request)
{
// カート処理
return ['Carts' => $ViewCarts];
}
{% 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 %}
/**
* おすすめ商品の表示
* ※会員毎に出しわけます。
* 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];
}
{% 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()
の記述方法は少し特殊なので、以下を参考にすること。