[TIL # 40] Vue 14일차

Yejin Yang·2022년 6월 15일
0

[TIL]

목록 보기
39/69
post-thumbnail

노션 프로젝트 (3)

워크스페이스 페이지 클릭 시 상세정보


workspaces 뒤로 아이디 값이 부여되는데, 워크스페이스 페이지 클릭하면 해당 id인 워크스페이스로 들어가서 그에 맞는 상세정보가 출력되어야 한다.

라우터 index.js에 동적파라미터 추가

routes: [
    {
      path: '/',
      component: Home,
    },
    {
      // 내가 가지고 있는 워크스페이스중 첫번째 목록에 존재하는 워크스페이스로 이동하라
      // id는 이름을 지정한 것이다. 내장된 옵션이 아니다.
      // :id 는 동적 파라미터
      path: '/workspaces/:id',
      component: Workspace,
    },
  ]

Workspace.vue


// 매칭하기

<script>
import { mapStores } from 'pinia'
import { useWorkspaceStore } from '~/store/workspace'

export default {
  computed: {
    ...mapStores(useWorkspaceStore)
  },
  created() {
    this.$route.params.id
  }
}
</script>

$route는 페이지 정보를 의미한다.
created()로 연결해야 해당 컴포넌트가 태어나면(created) 상세 정보 조회 ($route) 할 수 있다.

워크스페이스 상세 내용 조회

단일 워크스페이스의 상세 내용을 가져온다.

state() {
    return {
      workspace: {},
      // 기본은 빈배열, 목록을 불러오면 갱신
      workspaces: []
    }
  },



async readWorkspace(id) {
      const res = await fetch(`URL${id}`, {
        method: 'GET',
        headers: {
          'content-type': 'application/json',
          'apikey': '',
          'username': ''
        },
        })
        const workspace = await res.json()
        console.log(workspace)

				this.workspace = workspace
    },

workspace.vue

// workspace.vue

<template>
 <h1>Workspace!</h1>
 <button @click="workspaceStore.createWorkspace">
   워크스페이스 생성!
 </button>
 <section :key="$route.params.id">
   <h1
     ref="title"
     contenteditable>
     {{ workspaceStore.workspace.title }}
   </h1>
   <p
     ref="content"
     contenteditable>
     {{ workspaceStore.workspace.content }}
   </p>
 </section>

 <input
   type="text"
   @change="onInput" />
</template>

<script>
import { mapStores } from 'pinia'
import { useWorkspaceStore } from '~/store/workspace'

export default {
 computed: {
   ...mapStores(useWorkspaceStore)
 },
 watch: {
   // 페이지가 바뀔 때 마다 감시
   $route() {
     this.workspaceStore.readWorkspace(this.$route.params.id)
   }
 },
 created() {
   // 주소에 들어있을 id값을 추출해서 이 함수의 값으로 사용
   this.workspaceStore.readWorkspace(this.$route.params.id)
 },
 methods: {
   // 동시에 알아내서 두개의 요소 한번에 전송하게 로직 짜기 
   onInput() {
     // const title = 요소.textContent (글자만 필요해서 div태그 빼려고. 엔터누르면 content영역으로 가게)
     // const content = 요소.innderHTML (div 포함, 내용은 줄바꿈요소도 필요하니까 innderHTML 사용)
     const title = this.$refs.title.textContent
     const content = this.$refs.content.innderHTML
   }
 }
}
</script>
// LNB.vue
<template>
  <ul>
    <li
      v-for="workspace in workspaceStore.workspaces"
      :key="workspace.id">
      <RouterLink :to="`/workspaces/${workspace.id}`">
        {{ workspace.title }}
      </RouterLink>
     
			 <button @click="workspaceStore.deleteWorkspace(workspace.id)">
        삭제!
      </button>
    </li>
  </ul>
</template>

워크스페이스 수정

워크스페이스 수정하기
워크스페이스의 내용(content)은 <div>, <br> 태그만 허용
타이틀, 내용이 동시에 수정되는 하나의 API이다.

curl -X 'PUT' \ 
@param {String} parentId - 부모 워크스페이스 ID, 부모 워크스페이스를 삭제하는 경우 '-1' 
@param {title} title - 워크스페이스 제목
@param {content} content - 워크스페이스 내용
@param {content} poster - 워크스페이스 대표 이미지(Base64), 이미지 삭제하는 경우 '-1'
@return {Object} - 수정된 워크스페이스 객체

수정 시 body에 담을 내용

@param {title} title - 워크스페이스 제목
@param {content} content - 워크스페이스 내용
@param {content} poster - 워크스페이스 대표 이미지(Base64)
async updateWorkspace(payload) {
      const { id, title, content } = payload
      await fetch(`URL${id}`, {
        method: 'PUT',
        headers: {
          'content-type': 'application/json',
          'apikey': '',
          'username': ''
        },
        // 데이터 받아줄 body
        body: JSON.stringify({
          title,
          content
        })
      })
      // 제목 수정하면 목록도 바뀌어지게 갱신
      this.readWorkspaces()
    },

workspace.vue

onInput이 실행되면 updateWorkspace action이 실행 되게 수정한다.

methods: {
    // 동시에 알아내서 두개의 요소 한번에 전송하게 로직 짜기 
    onInput() {
      // const title = 요소.textContent (글자만 필요해서 div태그 빼려고. 엔터누르면 content영역으로 가게)
      // const content = 요소.innderHTML (div 포함, 내용은 줄바꿈요소도 필요하니까 innderHTML 사용)
      const title = this.$refs.title.textContent
      const content = this.$refs.content.innderHTML

     // payload 매개변수가 받을 수 있게 객체형태
      this.workspaceStore.updateWorkspace({
        id: this.$route.params.id,
        title,
        content
      })
    }
  }

템플릿도 수정
contenteditable 에서는 수정이 끝나면 블러이벤트가 발생한다.
따라서 제목, 내용 부분에 @blur="onInput"를 입력한다.

<h1>Workspace!</h1>
  <button @click="workspaceStore.createWorkspace">
    워크스페이스 생성!
  </button>
  <section :key="$route.params.id">
    <h1
      ref="title"
      contenteditable
      @blur="onInput">
      {{ workspaceStore.workspace.title }}
    </h1>
    <p
      ref="content"
      contenteditable
      @blur="onInput"
      v-html="workspaceStore.workspace.content">
    </p>
  </section>

v-html로 연결한 이유는 수정 텍스트만 보이는것이 아니라 div같은 태그들이 그대로 노출되어서 v-html로 연결한 것이다.

제목 - 엔터키 누르면 포커스가 내용으로 넘어가게하기 그리고 엔터키 줄바꿈 안되게 하기는 아래 코드 추가하면 된다.

@keydown.prevent.enter="$refs.content.focus()

contenteditable의 플레이스홀더

템플릿 영역
placeholder="제목 없음”

placeholder="내용을 입력하세요!”

스타일 영역

<style scoped lang="scss">
/* contenteditabl 속성선택자
 - empty: 요소가 가지는 content가 비워져있는지 확인
 - 가상요소선택자 사용했으니 content 써야한다.
 - attr() CSS 함수 
 - contenteditable이라는 속성을가지고있는 요소에 내용이비어져있으면 
내부 앞쪽에(before) content로 placeholder라는 값을 알아내서 content로 채워라. */
[contenteditable] {
  &:empty::before {
    content: attr(placeholder);
    color: lightgray;
  }
}
</style>

근데 br태그가 남아있으면 placeholder 가 안될수도있다. 그래서 onInput 메소드 if문 추가한다.

methods: {
    // 동시에 알아내서 두개의 요소 한번에 전송하게 로직 짜기 
    onInput() {
      // const title = 요소.textContent (글자만 필요해서 div태그 빼려고. 엔터누르면 content영역으로 가게)
      // const content = 요소.innderHTML (div 포함, 내용은 줄바꿈요소도 필요하니까 innderHTML 사용)
      const title = this.$refs.title.textContent
      const content = this.$refs.content.innerHTML

      if (!title.trim()) {
        this.$refs.title.innerHTML = ''
      }
      if (!this.$refs.content.textContent.trim()) {
        this.$refs.content.innerHTML = ''
      }

     // payload 매개변수가 받을 수 있게 객체형태
      this.workspaceStore.updateWorkspace({
        id: this.$route.params.id,
        title,
        content
      })
    }
  }
}
profile
Frontend developer

0개의 댓글