LoginSignup
3
1

More than 1 year has passed since last update.

の続きです。

タスクの完了

タスクのチェックボックスのイベントに応じて親のtaskStoreを更新するためにonChangeに以下の関数をセットして更新させます。
ReactではuseStateを使用すれば、セッターのような関数がついてきますがQwikでは見つからなかったので、強引にセットしています。
(やり方あったら教えてください:bow:

src/components/todo/index.tsx
<input 
  id={task.id} 
  type="checkbox" 
  checked={task.completed} 
  onChange$={() => {
    const updatedTasks = taskStore.tasks.map((task: any) => {
      if (id === task.id) {
        return {...task, completed: !task.completed}
      }
      return task
    })
    taskStore.tasks = updatedTasks;
  }}
/>

タスクの削除

Deleteボタンにもクリックイベントに対する処理を追記していきます。
こちらもタスクの完了の関数とどうようにtaskStoreを更新することでデータの管理をしています。

src/components/todo/index.tsx
<button 
  type="button"
  className="btn btn__danger"
  onClick$={() => {
    const remainingTasks = taskStore.tasks.filter((task: any) => id !== task.id)
    taskStore.tasks = remainingTasks; 
  }}
>
  Delete <span className="visually-hidden">Eat</span>
</button>

ビューテンプレートとエディットテンプレートの切り替え

editingTemplateとviewTemplateに分けます。
useSignalを使用して、isEditing を定義しisEditing.valueのbool値によってテンプレートの出し分けをします。

src/components/todo/index.tsx
import { component$, useSignal } from "@builder.io/qwik";

type Todo = {
  taskStore: any
  id: string
}

export default component$(({ taskStore, id }: Todo) => {
  const isEditing = useSignal(false);

  const task = taskStore.tasks.filter((item: any) => item.id === id)[0];

  const editingTemplate = (
    <>
      <form className="stack-small">
        <div className="form-group">
          <label className="todo-label" for={task.id}>
            New name for {task.name}
          </label>
          <input id={task.id} className="todo-text" type="text" />
        </div>
        <div className="btn-group">
          <button 
            type="button" 
            className="btn todo-cancel"
            onClick$={() => {
              isEditing.value = false;
            }}
          >
            Cancel
            <span className="visually-hidden">renaming {task.name}</span>
          </button>
          <button type="submit" className="btn btn__primary todo-edit">
            Save
            <span className="visually-hidden">new name for {task.name}</span>
          </button>
        </div>
      </form>
    </>
  );

  const viewTemplate = (
    <>
      <div className="stack-small">
        <div className="c-cb">
          <input 
            id={task.id} 
            type="checkbox" 
            checked={task.completed} 
            onChange$={() => {
              const updatedTasks = taskStore.tasks.map((task: any) => {
                if (id === task.id) {
                  return {...task, completed: !task.completed}
                }
                return task
              })
              taskStore.tasks = updatedTasks;
            }}
          />
          <label className="todo-label" for="todo-0">
            {task.name}
          </label>
        </div>
        <div className="btn-group">
          <button 
            type="button"
            className="btn"
            onClick$={() => {
              isEditing.value = true
            }}
          >
            Edit <span className="visually-hidden">Eat</span>
          </button>
          <button 
            type="button"
            className="btn btn__danger"
            onClick$={() => {
              const remainingTasks = taskStore.tasks.filter((task: any) => id !== task.id)
              taskStore.tasks = remainingTasks; 
            }}
          >
            Delete <span className="visually-hidden">Eat</span>
          </button>
        </div>
      </div>
    </>
  );

  return (
    <li className="todo">{isEditing.value ? editingTemplate : viewTemplate}</li>
  )
})

このようにEditボタンをクリックすると、編集用のテキストボックスに切り替わるはずです。
スクリーンショット 2022-12-04 18.15.21.png

エディットページを完成させる

エディットページのinputの入力イベントに対するStateの更新処理を追記(onChangeの箇所)して、Saveボタンが押されるとそのStateでnameを更新するようにします。
ここでも preventdefault:click をタグに追記してあげないとボタンを押すたびにStateがリセットされてしまうので気をつけてください。

src/components/todo/index.tsx
const editingTemplate = (
    <>
      <form className="stack-small">
        <div className="form-group">
          <label className="todo-label" for={task.id}>
            New name for {task.name}
          </label>
          <input 
            id={task.id} 
            className="todo-text" 
            type="text"
            onChange$={(e) => {
              nameStore.name = e.target.value
            }}
          />
        </div>
        <div className="btn-group">
          <button 
            type="button" 
            preventdefault:click
            className="btn todo-cancel"
            onClick$={() => {
              isEditing.value = false;
            }}
          >
            Cancel
            <span className="visually-hidden">renaming {task.name}</span>
          </button>
          <button 
            type="submit"
            preventdefault:click
            className="btn btn__primary todo-edit"
            onClick$={() => {
              const updatedTasks = taskStore.tasks.map((task: any) => {
                if (id === task.id) {
                  return {...task, name: nameStore.name}
                }
                return task
              })
              taskStore.tasks = updatedTasks;
              isEditing.value = false;
            }}
          >
            Save
            <span className="visually-hidden">new name for {task.name}</span>
          </button>
        </div>
      </form>
    </>
  );

フィルターを完成させる

filterButtonコンポーネントで、選択しているフィルターをStateで管理できるようにします。
filterStoreは親コンポーネントのroutes/index.tsxでuseStoreを新しく作成します。

src/components/filterButton/index.tsx
import { component$ } from "@builder.io/qwik";

type FilterButton = {
  category: string
  filterStore: any
}

export default component$(({ category, filterStore }: FilterButton) => {
  return (
    <button 
      type="button" 
      className="btn 
      toggle-btn" 
      aria-pressed="true"
      preventdefault:click
      onClick$={() => {
        filterStore.filter = category
      }}
    >
      <span className="visually-hidden">Show </span>
      <span>{category}</span>
      <span className="visually-hidden"> tasks</span>
    </button>
  )
})

デフォルトは全部選択中のAllにします。

src/routes/index.tsx
  const filterStore = useStore({
    filter: "All"
  })

次に、選択されたフィルターに応じてタスクを絞り込むための関数を用意し、taskListに適応させていきます。

src/routes/index.tsx
  const FILTER_MAP = {
    All: () => true,
    Active: (task) => !task.completed,
    Completed: (task) => task.completed
  };

 const taskList =taskStore.tasks
   .filter(FILTER_MAP[filterStore.filter])
   .map((task) => (
     <Todo taskStore={taskStore} id={task.id} />
   ));

絞り込みも上手く動きました!

All Active Completed
スクリーンショット 2022-12-04 18.43.35.png スクリーンショット 2022-12-04 18.43.42.png スクリーンショット 2022-12-04 18.43.47.png

完成です!

ここまで、QwikでTODOリストを作ってきました。ReactよりAPIが整っていないのと思想が違うので関数の書き方等少し冗長なものにしてしまいました。(これが正しいかもよくわかっていない。)

Builder.ioのCTOである mhevery - sanのTODOリストのデモ実装があったので併せて載せておきます。自分の記事よりも正しいものだと思うのでこちらも参考にしてみてください。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1