코딩하는 털보

11 to 9, Day 14 본문

Diary/Eleven to Nine

11 to 9, Day 14

이정인 2021. 3. 18. 18:23

11 to 9, Day 14

Today, ToDoList

  • Toy Project - NGMA
    • 일정 리스트 전체 선택
    • 일정 리스트 페이징
    • 일정 수정시 기본 데이터
    • 짝꿍 취소하기

일정 전체 선택

thead의 체크박스

<th scope="col"><input class="form-check-input" type="checkbox" id="checkAllBox" onclick="checkAll();"></th>

checkAll() 함수

function checkAll() {
    if ($('input#checkAllBox').is(':checked')==false) {
        $('input[name="scheduleId"]').prop('checked',false);
    } else {
        $('input[name="scheduleId"]').prop('checked',true);
    }
}

이상하게 attr()로 하면 개별로 변경된 엘리먼트들은 제어되지 않아서 prop으로 대신했다.


페이징

기존 메소드는 냅두고 Page 처리할 수 있도록 Pageable을 받는 새로운 repository 메소드

public interface ScheduleRepository extends JpaRepository<Schedule, Long> {

    @Query("select s from Schedule s where s.owner.id = :accountId")
    List<Schedule> findAllByOwner(Long accountId);

    @Query("select s from Schedule s where s.owner.id = :accountId")
    List<Schedule> findAllByOwner(Long accountId, Pageable pageable);

    @Query("select s from Schedule s where s.owner.id = :accountId " +
            "or s.owner.id = (select a.lover.id from Account a where a.id = :accountId) order by s.dateTime")
    List<Schedule> findAllByCouple(Long accountId);

    @Query("select s from Schedule s where s.owner.id = :accountId " +
            "or s.owner.id = (select a.lover.id from Account a where a.id = :accountId) order by s.dateTime")
    List<Schedule> findAllByCouple(Long accountId, Pageable pageable);
}

기본적으로 한 페이지에서 10개의 데이터 받아옴.

전체 페이지 수 계산을 위해서 컨트롤러를 추가했는데, 좋은 방법은 아닌거 같다.

    @GetMapping
    @ResponseBody
    public List<Schedule> getSchedules(@AuthenticationPrincipal UserAccount userAccount,
                                       @PageableDefault(size = 10) Pageable pageable) {
        List<Schedule> schedules = scheduleService.getSchedules(userAccount, pageable);
        return schedules;
    }

    @GetMapping("/page")
    @ResponseBody
    public int getSchedulePageInfo(@AuthenticationPrincipal UserAccount userAccount) {
        List<Schedule> schedules = scheduleService.getSchedules(userAccount);
        return schedules.size();
    }

HTML 일정 테이블 아래에 부트스트랩에서 제공하는 page navi 추가

    <nav aria-label="Page navigation">
      <ul id="pageTab" class="pagination justify-content-end"></ul>
    </nav>

처음 일정탭을 누르면 printSchedules()를 호출하여 전체 일정 수 와 기본적으로 1페이지의 10개 데이터를 가져온다.

가져온 일정 수로 각 page 버튼을 만든다. 각 page 버튼은 다시 loadSchedules() 함수로 데이터를 가져온다.

function printSchedules(pageNum) {
    $.ajax({
        url: "http://localhost:8080/schedule/page",
        type: "GET",
        dataType: "json",
        success: function(data) {
            printPageTab(data);
            currentPage(pageNum);
        },
        error: function(e) {
            alert(e.responseText);
        }
    });
    loadSchedules(pageNum);
}

function printPageTab(size) {
    var pageNum = Math.round(size/10);
    for (let i = 1; i <= pageNum; i++) {
        $('ul#pageTab').append('<li class="page-item"><a class="page-link" onclick="loadSchedules('+i+')">'+i+'</a></li>');
    }
}

function loadSchedules(pageNum) {
    $.ajax({
        url: "http://localhost:8080/schedule",
        type: "GET",
        dataType: "json",
        data: { page: pageNum-1 },
        success: function(data) {
            addScheduleRow(data);
        },
        error: function(e) {
            alert(e.responseText);
        }
    });
}

현재 페이지 버튼 활성화를 위해 함수 추가/변경

function printPageTab(size) {
      $('ul#pageTab').empty();
    var pageNum = Math.round(size/10);
    for (let i = 1; i <= pageNum; i++) {
        $('ul#pageTab').append('<li class="page-item" value="'+i+'">' +
            '<a class="page-link" onclick="loadSchedules('+i+');currentPage('+i+');">'+i+'</a></li>');
    }
}

function currentPage(num) {
    $('li.page-item').removeClass("active");
    $('li.page-item[value='+num+']').addClass("active");
}

일정 추가/변경/제거 할때 처음 페이지로 돌아오는 버그 수정

loadSchedules() -> printSchedules(현재 페이지);

function modifyScheduleForm(){
    $.ajax({
        type: "POST",
        url: "http://localhost:8080/schedule/modify",
        contentType: "application/json",
        cache:false,
        data: JSON.stringify($("form#modifyScheduleForm").serializeObject()),
        success: function(response){
            $("#modifyScheduleModal").modal('hide');
            var pageNum = $('li.page-item.active').val();
            printSchedules(pageNum);
        },
        error: function(e){
            alert(e.responseText);
        }
    });
}

function modifyScheduleForm(){
    $.ajax({
        type: "POST",
        url: "http://localhost:8080/schedule/modify",
        contentType: "application/json",
        cache:false,
        data: JSON.stringify($("form#modifyScheduleForm").serializeObject()),
        success: function(response){
            $("#modifyScheduleModal").modal('hide');
            var pageNum = $('li.page-item.active').val();
            printSchedules(pageNum);
        },
        error: function(e){
            alert(e.responseText);
        }
    });
}

function removeScheduleForm(){
    $.ajax({
        type: "DELETE",
        url: "http://localhost:8080/schedule",
        contentType: "application/json",
        cache:false,
        data: JSON.stringify(checkedSchedules()),
        success: function(response){
            var pageNum = $('li.page-item.active').val();
            printSchedules(pageNum);
        },
        error: function(e){
            alert(e.responseText);
        }
    });
}

일정 변경 기본값

일정 변경 Modal 출력 전에 기본값으로 기존의 데이터가 입력되도록 변경

function showModifyScheduleModal(scheduleId) {
    $.ajax({
        url: "http://localhost:8080/schedule/"+scheduleId,
        type: "GET",
        dataType: "json",
        success: function(data) {
            modifyScheduleModalDefaultValue(data)
        },
        error: function(request) {
            alert(request.responseText);
        }
    });
    $('#modifyScheduleModal').modal('show');
}

function modifyScheduleModalDefaultValue(schedule) {
    $('input#m-title').val(schedule.title);
    $('input#m-place').val(schedule.place);
    $('input#m-dateTime').val(schedule.dateTime.substr(0, 16));
    $('input#m-personal').prop('checked',schedule.personal);
    $('#modifyScheduleForm > .modal-body').append('<input type="hidden" name="id" value="'+schedule.id+'">');
}

짝꿍 취소하기

컨트롤러 테스트

    @Test
    @WithUserDetails(value = "jilee@example.com", setupBefore = TestExecutionEvent.TEST_EXECUTION)
    public void cancelLover() throws Exception {
        Account account = accountService.getUserByEmail("jilee@example.com");
        Account lover = accountService.getUserByEmail("sjlee@example.com");
        account.setLover(lover);
        lover.setLover(account);
        account.setLoverState(LoverState.COUPLED);
        lover.setLoverState(LoverState.COUPLED);

        mvc.perform(post("/account/lover/cancel"))
                .andDo(print())
                .andExpect(status().isOk());

        assertThat(lover.getLoverState()).isEqualTo(LoverState.NOTHING);
        assertThat(account.getLoverState()).isEqualTo(LoverState.NOTHING);
    }

핸들러

    @PostMapping("/lover/cancel")
    @ResponseBody
    public ResponseEntity<?> loverCancel(@AuthenticationPrincipal UserAccount userAccount) {
        accountService.cancelLover(userAccount);
        return ResponseEntity.ok("{}");
    }

서비스

    public void cancelLover(UserAccount userAccount) {
        Account account = getUserById(userAccount.getAccountId());
        Account lover = account.getLover();
        account.setLover(null);
        account.setLoverState(LoverState.NOTHING);
        lover.setLover(null);
        lover.setLoverState(LoverState.NOTHING);
    }

버튼 추가 및 ajax 기능 추가

function matchedComment(data) {
    $("div#loverStateResult").append('<p class="text-sm-start">'+data.name.toString()+'('+data.email.toString()+')님과 짝꿍입니다!</p>');
    $("div#loverStateResult").append('<a class="btn btn-outline-secondary btn-sm" type="button" id="cancelLover" onclick="cancelLover();">취소하기</a>');
}

function cancelLover() {
    $.ajax({
        url: "http://localhost:8080/account/lover/cancel",
        type: "POST",
        dataType: "json",
        success: function(data) {
            alert("취소되었습니다.");
            loadCoupleState();
        },
        error: function(request) {
            alert(request.responseText);
        }
    });
}

Comments