평점을 이용하였음.
와인 정보 게시판을 예시로 구현하였음.
4/4분면 값 저장을 위해
import com.std.sbb.domain.wine.entity.Wine;
import com.std.sbb.global.jpa.BaseEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.OneToOne;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.*;
import lombok.experimental.SuperBuilder;
@Entity
@Getter
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Taste extends BaseEntity {
@Min(value = 1)
@Max(value = 5)
private Integer sweet;
@Min(value = 1)
@Max(value = 5)
private Integer body;
@Min(value = 1)
@Max(value = 5)
private Integer acidity;
@Min(value = 1)
@Max(value = 5)
private Integer tannin;
@OneToOne(mappedBy = "taste")
private Wine wine;
}
BaseEntity 작성
import jakarta.persistence.*;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
import static lombok.AccessLevel.PROTECTED;
@Getter
@SuperBuilder
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = PROTECTED)
@ToString
@EqualsAndHashCode
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include
private Long id;
@CreatedDate
private LocalDateTime createDate;
@LastModifiedDate
private LocalDateTime modifyDate;
}
4/4분면 값을 저장하기 위한
import com.std.sbb.domain.taste.entity.Taste;
import com.std.sbb.domain.taste.repository.TasteRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class TasteService {
private final TasteRepository tasteRepository;
public Taste create(Integer sweet, Integer body, Integer acidity, Integer tannin) {
Taste taste = Taste.builder()
.sweet(sweet)
.body(body)
.acidity(acidity)
.tannin(tannin)
.build();
this.tasteRepository.save(taste);
return taste;
}
public Taste getTaste(Long id) {
Optional<Taste> ot = this.tasteRepository.findById(id);
if (ot.isPresent()) {
return ot.get();
} else {
throw new RuntimeException("맛이 존재하지 않습니다");
}
}
}
import com.std.sbb.domain.taste.entity.Taste;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TasteRepository extends JpaRepository<Taste, Long> {
}
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.Getter;
@Data
@Getter
public class TasteForm{
@NotNull(message = "Please provide a value for sweet")
private Integer sweet;
@NotNull(message = "Please provide a value for body")
private Integer body;
@NotNull(message = "Please provide a value for acidity")
private Integer acidity;
@NotNull(message = "Please provide a value for tannin")
private Integer tannin;
}
4/4분면 값을 wine 값에 종속시키기 위해 wineEntity에 코드 추가
@OneToOne
private Taste taste;
Taste taste = tasteService.create(tasteForm.getSweet(), tasteForm.getBody(), tasteForm.getAcidity(), tasteForm.getTannin());
this.wineService.create(wineForm.getWineName(), wineForm.getWineNameE(), wineForm.getCountry(),
wineForm.getList(), wineForm.getPrice(), wineForm.getKind(), wineForm.getFood(),
wineForm.getScore(), board, taste);
service에도 추가시켜줘야함
버튼 클릭 유지를 위해 스크립트 구문 추가
<script th:inline="javascript">
document.addEventListener('DOMContentLoaded', function () {
var dateInput = document.querySelector('[datepicker]');
flatpickr(dateInput, {
dateFormat: 'm/d/Y',
});
});
$(document).ready(function () {
// 페이지 로드 시 저장된 클릭 상태를 가져와 설정
$('[name^="score-"]').each(function () {
var tasteId = $(this).attr('id');
var savedValue = localStorage.getItem('selectedValue-' + tasteId);
if (savedValue === $(this).val()) {
$(this).prop('checked', true);
}
});
// 각각의 맛 구성 요소에 대한 독립적인 클릭 및 클릭 상태 유지
$('[name^="score-"]').on('change', function () {
// 클릭한 라디오 버튼이 이미 체크된 경우, 다시 체크하지 않도록 함
if (!$(this).prop('checked')) {
$(this).prop('checked', true);
}
var tasteId = $(this).attr('id');
var selectedValue = $(this).val();
// 클릭한 값 저장
localStorage.setItem('selectedValue-' + tasteId, selectedValue);
});
});
</script>
html구문 작성
<div th:fragment="tasteFragment(tasteId)">
<div class="mx-5 flex flex-row-reverse justify-end text-4xl">
<label th:for="${'score-' + tasteId}"></label>
<input type="radio" class="peer hidden" th:id="${'value5-' + tasteId}" th:value="5"
th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 5}"/>
<label th:for="${'value5-' + tasteId}"
class="cursor-pointer text-gray-400 peer-hover:text-pink-600 peer-checked:text-pink-600">●</label>
<input type="radio" class="peer hidden" th:id="${'value4-' + tasteId}" th:value="4"
th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 4}"/>
<label th:for="${'value4-' + tasteId}"
class="cursor-pointer text-gray-400 peer-hover:text-pink-500 peer-checked:text-pink-500">●</label>
<input type="radio" class="peer hidden" th:id="${'value3-' + tasteId}" th:value="3"
th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 3}"/>
<label th:for="${'value3-' + tasteId}"
class="cursor-pointer text-gray-400 peer-hover:text-pink-400 peer-checked:text-pink-400">●</label>
<input type="radio" class="peer hidden" th:id="${'value2-' + tasteId}" th:value="2"
th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 2}"/>
<label th:for="${'value2-' + tasteId}"
class="cursor-pointer text-gray-400 peer-hover:text-pink-300 peer-checked:text-pink-300">●</label>
<input type="radio" class="peer hidden" th:id="${'value1-' + tasteId}" th:value="1"
th:name="${fieldName}" th:checked="${tasteForm != null && tasteForm[fieldName] == 1}"/>
<label th:for="${'value1-' + tasteId}"
class="cursor-pointer peer text-gray-400 peer-hover:text-pink-200 peer-checked:text-pink-200">●</label>
</div>
</div>
<div class="flex items-center my-4" th:field="*{taste}">
<span class="flex items-center">당도</span>
<div th:replace="~{taste1 :: tasteFragment(tasteId='sweet', fieldName='sweet')}"></div>
<span class="flex items-center">산도</span>
<div th:replace="~{taste1 :: tasteFragment(tasteId='acidity', fieldName='acidity')}"></div>
<span class="flex items-center">바디</span>
<div th:replace="~{taste1 :: tasteFragment(tasteId='body', fieldName='body')}"></div>
<span class="flex items-center">타닌</span>
<div th:replace="~{taste1 :: tasteFragment(tasteId='tannin', fieldName='tannin')}"></div>
<button class="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 ml-20">
게시글 등록
</button>
</div>