부모창에서 자식창(팝업창) 띄워 검색 후 선택한 값 부모창으로 전달

olive3·2024년 4월 24일
0

구현사항

  1. 부모창에서 자식창 띄우기
  2. 자식창에서 검색, 페이지네이션 구현
  3. 자식창의 선택한 데이터를 부모창으로 전달해 부모창에서 데이터 출력

구현

1. 부모창에서 자식창 띄우기

부모창에서 상품 선택 버튼 클릭시 자식창(팝업창) 띄우기

<button type="button" class="popup_btn">상품 선택</button>
//팝업 창을 띄우는 함수
$('#popupBtn').click(function() {
    window.open("/shop/products/search", "_blank", "width=600px,height=450px");
});
  • 클래스명이 popup_btn을 클릭하면 팝업창을 띄우는 함수가 실행됩니다.
  • /shop/products/search GET 방식으로 요청했습니다.
  • 새로운 창을 열기 위해 _blank 속성을 지정하였습니다. 기본값이므로 생략 가능합니다.
  • 자식창 크기는 width=600px, height=400px 으로 지정하였습니다.

자식창

  • 참고로 데이터가 적어서 1,2,3,4개씩 보기로 구현하였지만 상황에 맞춰 limit을 설정하면 됩니다.

2. 자식창에서 검색, 페이지네이션 구현

yarn 입력, 1개씩 보기 선택 후 검색을 해보겠습니다.

QnaPopupSearchCondition

@Getter
@Setter
public class QnaPopupSearchCondition {
    
    private String keyword;
    private Integer page;
    private Integer limit;

    public QnaPopupSearchCondition() {
        this.page = 1;
        this.limit = 1;
    }
}
  • keyword, page, limit 세가지 쿼리파라미터를 편리하게 다루기 위해 QnaPopupSearchCondition 객체를 만들었습니다.
  • 파라미터가 없는 기본 생성자의 경우 page=1, limit=1을 디폴트값으로 넣어주었습니다.

ProductController

@GetMapping("/search")
public String searchPopupProducts(@ModelAttribute("condition") QnaPopupSearchCondition condition, Model model) {
    if (condition.getKeyword() != null) {
        PageRequest pageRequest = PageRequest.of(condition.getPage() - 1, condition.getLimit());
        Page<QnaPopupDto> qnaPopupDtos = productService.getPopupProducts(pageRequest, condition.getKeyword());
        model.addAttribute("products", qnaPopupDtos);
    } else {
        model.addAttribute("products", null);
    }

    return "community/qna/qna_popup";
}
  • 부모창에서 window.open() 과 자식창에서 페이지네이션을 고려했을 때 GET 방식이 편리하다 판단하여 GET 방식으로 구현하였습니다.
  • 현재 코드에서는 PageRequest객체를 생성하지않고 page, limit 그대로 넘겨도 되지만 추후에 정렬을 추가할 수도 있기 때문에 PageRequest 객체를 생성해 넘겼습니다.

ProductService

public Page<QnaPopupDto> getPopupProducts(Pageable pageable, String keyword) {
    return productRepository.findAllPopup(pageable, keyword).map(QnaPopupDto::new);
}
  • 리포지토리로 부터 반환받은 Product 리스트를 DTO로 변환해서 컨트롤러로 반환합니다.

ProductRepository

public Page<Product> findAllPopup(Pageable pageable, String keyword) {
    List<Product> content = queryFactory
            .selectFrom(product)
            .leftJoin(product.images, productImage)
            .on(product.id.eq(productImage.product.id), productImage.imageType.eq(ImageType.DISPLAY))
            .where(keywordLike(keyword))
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();

    JPAQuery<Long> countQuery = queryFactory
            .select(product.count())
            .from(product)
            .where(keywordLike(keyword));

    return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
    }
    
    
private BooleanExpression keywordLike(String keyword) {
    return hasText(keyword) ? product.name.contains(keyword) : null;
}

검색어인 keyword 를 where 조건문에 걸어주고 offset()과 limit()을 통해 페이지네이션을 구현하였습니다.

결과

페이지네이션 + Tymeleaf

 <div class="pagination" th:if="${products != null and products.totalPages > 0}">
    <ul>
        <li>
            //< 에 해당하는 부분
            <a href="./qna_popup.html" th:href="@{/shop/products/search(keyword=${condition.keyword}, limit=${condition.limit})}"
               class="prev_first">
                <span class="material-symbols-outlined">navigate_before</span>
            </a>
        </li>
        //1,2,3에 해당하는 부분
        <li th:each="page : ${#numbers.sequence(1, products.totalPages)}">
            <a onclick="location.href='./qna_popup.html'" th:onclick="|location.href='@{/shop/products/search(keyword=${condition.keyword}, page=${page}, limit=${condition.limit})}'|"
               class="page" th:classappend="${page == products.number + 1} ? 'active'" th:text="${page}">1
            </a>
        </li>
		//> 에 해당하는 부분
        <li>
            <a th:href="@{/shop/products/search(keyword=${condition.keyword}, page=${products.totalPages}, limit=${condition.limit})}">
                <span class="material-symbols-outlined">navigate_next</span>
            </a>
        </li>
    </ul>
</div>
  • < 에 해당하는 부분
    • 1 페이지에 해당하는 부분이므로 쿼리파라미터로 keyword와 limit 세팅해 주었습니다. QnaPopupSearchCondition에서 기본생성자로 page=1로 설정됩니다.
  • 1,2,3 에 해당하는 부분
    • products.totalPages는 총 페이지 개수입니다. 현재 총 페이지 개수는 3이므로 ${#numbers.sequence(1, products.totalPages)} 는 1,2,3 값을 생성해서 page에 할당해줍니다.
    • 쿼리파라미터로 keyword와 page, limit 세가지 모두 세팅해 주었습니다.
    • th:classappend는 현재페이지에 css를 주기 위한 설정입니다.
  • > 에 해당하는 부분
    • 마지막 페이지에 해당하는 부분이므로 page에는 products.totalPages 값을 넣어주었습니다.

3. 자식창의 선택한 데이터를 부모창으로 전달해 부모창에서 데이터 출력

자식창에서 데이터 선택

yarn 을 검색해 나오는 결과 중 grey yarn 을 선택해보겠습니다.

<a href="javascript:void(0)" class="btn1"
   th:onclick="sendResult([[${product.id}]], [[${product.name}]], [[${product.price}]], [[${product.displayImage}]])">
   선택
</a>
//팝업창에서 사용자가 선택한 상품 데이터를 부모창으로 전달
<script th:inline="javascript">
    //팝업창에서 사용자가 선택한 상품 데이터를 부모창으로 전달
    function sendResult(id, name, price, image) {
        window.opener.setProductInfo(id, name, price, image);
        window.close();
    }
</script>

자식창에서 선택 버튼 클릭 시 sendResult() 함수가 실행됩니다.
sendResult() 함수는 상품id, 상품명, 상품가격, 상품 이미지 파일명 4가지 데이터를 부모창에 전달하면서 setProductInfo()를 호출하고 자식창을 닫아줍니다.

부모창에서 데이터 받은 데이터 채우기

<div class="item_wrap">
    <div class="thumbnail">
        <a href=""><img id="productImage" class="item_img" src="../../images/img_no.png" alt="상품 이미지"></a>
    </div>
    <div class="description">
        <span id="productName" class="item_name"></span>
        <span id="productPrice" class="item_price"></span>
        <input type="hidden" id="productId" th:field="*{productId}">
        <div class="btn_wrap">
            <button type="button" class="popup_btn info">상품 상세 정보</button>
            <button type="button" id="popupBtn" class="popup_btn">상품 선택</button>
        </div>
     </div>
</div>
<script th:inline="javascript">
    //팝업창에서 보낸 데이터 채우기
    function setProductInfo(id, name, price, image) {
        // $('#productId').value(id);
        $('#productName').text(name);
        $('#productPrice').text(price);
        $('#productImage').attr('src', '/productImages/' + image);
        $('.popup_btn.info').attr("onclick", "location.href='/shop/products/details/" + id + "'");
        $('.popup_btn.info').show();
    }
</script>

부모창에 데이터가 채워져야 할 부분에 자식창으로부터 전달받은 id, name, price, image 값을 세팅해주면 됩니다.


참고

https://9hyuk9.tistory.com/25

0개의 댓글