Modal of Bootstrap

Chúng ta thường xuyên dùng modal để confirm, login,… Vậy làm thế nào để sử dụng nó một cách tiện lợi, dễ tuỳ chỉnh, dùng nhiều nơi và chỉ sửa một nơi.

Ta có thể chia một modal bao gồm các phần:

  • Header (modal-header):
    • Hiển thị title.
    • Ẩn modal khi click x.
  • Body (modal-body, modal-footer):
    • Hiển thị nội dung

Đặt vấn đề

Ví dụ một page hiển thị thông tin 50 products và các options edit, delete. Các option này nếu sử dụng modal tĩnh => template cần render ra 100 cái modal.
Vậy Ý tưởng ở đây là khi click edit hoặc delete sẽ append một modal tương ứng vào .

Phương án giải quyết

Template cho page:

1
2
3
4
5
6
7
8
9
10
{# Một button để kích hoạt modal, có href dẫn tới `deleteAction` trong controller. #}
<button href="/delete" class="js-modal-trigger">Delete</button>

{# Thêm đoạn html modal vào cuối page, nơi để append content modal. #}
<div class="modal js-modal-wrapper" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
</div>
</div>
</div>

Template modal.html.twig chung gồm:

1
2
3
4
5
6
7
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">{% block title %}{% endblock %}</h4>
</div>
<div class="modal-body">
{% block content %}{% endblock %}
</div>

Template cụ thể extends modal.html.twig để render ra modal-content mình cần, ví dụ như example.html.twig:

1
2
3
4
5
6
7
8
9
10
11
{% extends "modal.html.twig" %}
{% block title %}Confirmation{% endblock %}
{% block content %}
<p><span>Are you sure you want to delete ?</span></p>
<form method="post" action="{{ link }}">
<div class="form-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><i class="fa fa-close"></i> Cancel</button>
<button type="submit" class="btn btn-danger"><i class="fa fa-trash"></i> Delete</button>
</div>
</form>
{% endblock %}

deleteAction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
*@Route("/delete", name="delete_action")
*@Method({"GET", "POST"})
*/
public function deleteAction(Request $request)
{
if ($request->isMethod('POST')) {
//code xử lý khi form trên modal được submit
}

//render ra nội dung của modal kèm đường dẫn cho form trong modal
return $this->>render('example.html.twig', [
'link' => '/delete'
]);
}

Javascrip xử lý:

1
2
3
4
import Modal from 'bootstrap.native';

//Sử dụng fetch để xử lý ajax request, có thể thay bằng XMLHttpRequest
import 'whatwg-fetch';

  • Tạo Modal

    1
    2
    3
    4
    5
    6
    const modalEl = document.querySelector('.js-modal-wrapper');
    const modalInstance = new Modal(modalEl,
    {
    backdrop: 'static',
    keyboard: false
    });
  • Detect sự kiện click cho page

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    document.addEventListener('click', (e) => {
    const triggerEl = e.target;
    if (!triggerEl || !triggerEl.matches('.js-modal-trigger')) {
    return;
    }

    //Lấy href render content của modal và ngăn sự kiện click của button
    e.preventDefault();
    const url = triggerEl.href;

    // Gửi ajax request lấy được nội dung của modal, sau đó append vào modalInstance đã tạo ở trên, và cuối cùng là show modal.
    fetch(url, { credentials: 'same-origin' })
    .then(response => response.text())
    .then((body) => {
    modalInstance.setContent(body);
    modalInstance.show();
    });
    });

Lưu ý

Nếu trong thẻ dùng để kích hoạt modal mà có chứa thẻ khác như:

1
2
3
<a class="js-modal-trigger">
<i></i>
</a>

Điều này sẽ dẫn tới khi click vào có thể là thẻ i, để tránh điều này thì trong javascript ta cần sửa một chút như sau:

1
2
3
4
document.addEventListener('click', (e) => {
const triggerEl = e.target.closest('a');
//...
}