DOM ARRAY PROJECTS(ARRAY API 연습)

YEONGHUN KO·2021년 11월 14일
0
post-thumbnail

1. 쇼핑바구니 아이템 계산

쇼핑할 아이템 이름과 가격을 타이핑 한뒤에 가격을 두배로 만들기도 하고, 합치기도 하고, 필터하기도 한다.

언제나 그렇듯 데이터는 App에서 관리하고 하위 컴포넌트로 내려준다. 타이핑 될때 마다 App.state안에 아이템의 이름과 가격이 차곡차곡 저장된다.

array api를 연습할 수 있는 좋은 기회이다.

1-1. 위계

  • main.js
    • App.js
      • handleButtons.js
      • handleItemLists.js
  • utils폴더
    • constant.js
    • validator.js
    • domStorage.js
    • storage.js
    • template.js

코드를 확인하고 싶다면 요기로에 방문해볼것

2. 배운 점

- CSS

  1. 양쪽 마진을 동일하게 주고싶다면 일단 부모요소에 아래와 같이 설정.
.parent {
  display: flex; /* 부모를 flexible하게 다룰 준비를 시켜야함*/
  margin: 0 auto; /* 양쪽 마진을 일단 동일하게 줌 */
  .self:nth-child(1) {
    margin-right: auto; /* 이렇게 하면 해당요소의 오른쪽 마진이 채워질 수 있을때까지 채워져서 중간이 텅비는 효과를 낸다.*/
  }
}

- JS

  1. sort는 본래 데이터를 manipulate한다. 그래서 본래 데이터를 보존하고 싶으면 spread syntax로 복사해놓고 sort하라.

  2. item들이 화면에 출력되는 기능을 handleItemList.js에서 관리하도록 하게 하기 위해서 조금 애를 썼다.(이름에 맞게 그에 맞는 기능이 수행되게끔 일관성을 유지하기 위해서 신경 쓴 것이다.)

//handelItemList.js

this.setState = nextState => {
  this.state = nextState;
  this.render();
};

this.render = () => {
  const itemRowsTemplate = createItemRows(this.state);
  result.innerHTML = itemRowsTemplate;
};

//template.js

const createElement = tagType => {
  return document.createElement(tagType);
};

const createItemRows = itemLists => {
  const resultLists = itemLists
    .map(
      item => `
    <div class='item ${item.name}'>
      <strong>${item.name}</strong>
      <div>
        <span>${formatPrice(item.price)}</span>
        <button data-id='${item.name}'>❌</button>
      </div>
    </div>
  `
    )
    .join('');
  return resultLists;
};
  1. Buttons.js 에 들어가는 button들은 App에서 관리했다. 왜냐면 ItemLists.js 에 있는 state에 접근하기 위해서이다.

App.state에 있는 데이터를 handleItemLists에 복사를 해두고 데이터를 조작하게 만들었다. 왜냐면 App.state에 있는 데이터는 원본데이터를 다시 복구시키는 onShowOriginalItemBtn을 위해서 보존해야하기 때문이다.

//App.js


const handleItemLists = new HandleItemLists({initialState: this.state,})

new HandleButtons({
  onShowOriginalItemBtn,

  onDoubleBtn: () => {
    const doublePriceItems = handleItemLists.state.map(item => {
      return { name: item.name, price: item.price * 2 };
    });
    handleItemLists.setState(doublePriceItems);
  },

  onShowExpensiveBtn: () => {
    const expensiveItems = handleItemLists.state.filter(
      item => item.price >= 10000
    );
    handleItemLists.setState(expensiveItems);
  },

  onSortBtn: () => {
    const copiedItemsForSort = [...handleItemLists.state];
    copiedItemsForSort.sort((a, b) => a.price - b.price);
    handleItemLists.setState(copiedItemsForSort);
  },

  onCalculateEntirePriceBtn: () => {
    if (!result.querySelector('.total')) {
      const totalPrice = handleItemLists.state.reduce((a, c) => {
        return (a += parseInt(c.price));
      }, 0);
      const totalTemplateRow = totalTemplate(totalPrice);
      result.appendChild(totalTemplateRow);
    }
  },
}
  1. 천의 자리마다 콤마가 표시되고 통화심볼까지 표시되게 하기 위해서 new Intl을 이용해도 되지만 아래의 코드를 적용해도 된다.
// template.js
const formatPrice = price => {
  return (
    '₩' +
    parseInt(price)
      .toFixed(2)
      .replace(/\d(?=(\d{3})+\.)/g, '$&,')
  );
};
  1. 삭제기능에서 클릭된 item의 class이름을 App.state에서 filter해서 다시 display해도 되지만 클릭된 item element를 removeChild로 없애도 된다.
//App.js
const handleItemLists = new HandleItemLists({
  onDeleteBtnClick: e => {
      // 클릭된 x의 선조중에 <div class='item'>인 element가 있는지?
      const selectedItemEle = e.target.closest('div.item');
      const { nodeName } = e.target;

      if (nodeName === 'BUTTON') {
        if (selectedItemEle) {
          result.removeChild(selectedItemEle);
          const filteredItemLists = this.state.filter(
            item => item.name !== selectedItemEle.classList[1]
          );
          this.setState(filteredItemLists);
        }
      }
    }
})
profile
'과연 이게 최선일까?' 끊임없이 생각하기

0개의 댓글