7. Make tasks editable

Let the service update tasks

class TaskService extends Service<TaskService> {
    /* ... */
    public updateTask(task: ITask): void {
        this._tasks = this._tasks.map((t) => {
            return t.id === task.id ? task : t
        });
        AppEventBus.emit('task-event', { taskId: task.id })
    }
}

Generate the component

npm run generate component task-inputs
# Creates src/app/components/task-inputs/task-inputs.component.ts|html|scss

Give the component a task

const templateFn = (_task: ITask) => `__TEMPLATE_PLACEHOLDER__`;

export class TaskInputsComponent extends Component {
  private _task: ITask;

  constructor(mountTarget: HTMLElement, config: ITask) {
    super({templateFn, mountTarget, config});
    this._task = config;
  }

  private _editTask(field: keyof ITask, value: string): void { 
    this._task = { ...this._task, [field]: value };
    taskService.updateTask(this._task);
  }
}

Handle changes

<div class="task-inputs">
    <form>
        <input
            type="text"
            id="name"
            placeholder="Name"
            value="${_task.name}"
            data-event="change:_editTask:name,@value"
        />
        <textarea
            id="content"
            placeholder="Task details..."
            data-event="change:_editTask:content,@value"
        >${_task.content ?? ''}</textarea>
    </form>
</div>

Add style

.task-inputs {
    padding: 2rem;
    input {
        font-size: 1.5rem;
        font-weight: bold;
        padding: .2rem .5rem;
        margin-bottom: .5rem;
        width: 100%;
        background: #F1F1EF;
        border-radius: 8px;
    }
    textarea {
        width: 100%;
        font-size: 1.2rem;
        padding: .5rem 1rem;
        background: #E5E5E2;
        border-radius: 8px;
    }
}

Use dynamic routing for your tasks

Register a dynamic route

// src/app/routes.ts
export const appRoutes: Routes = {
    /* ... */
    '/tasks/:id?': () => new TaskCatalogView(),
    /* ... */
}

Handle routeParams in the view

export class TaskCatalogView extends View {
    /* ... */

    registerChildren(): ComponentConfig[] {
        const taskCatalogChildren: ComponentConfig[] = [ /* add-task, task-cards */ ];

        if (this.hasRouteParam('id')) {
            taskCatalogChildren.push(...this.getTaskInputsChild());
        }

        return taskCatalogChildren;
    }
  
    private getTaskInputsChild(): ComponentConfig[] {
        const taskId = this.getRouteParam('id') || 0;
        const task = taskService.getTask(+taskId);
        if (!task) return [];

        return [
            { 
                selector: `task-inputs`,
                factory: (el) => new TaskInputsComponent(el, task)
            }
        ]  
    }
}
<div class="task-catalog__body">
    <div data-catalog="task-cards" class="task-catalog__task-cards"></div>
    <div data-component="task-inputs"></div>
</div>
import { Navigation } from '../../../core/index.js';

export class TaskCardComponent extends Component {
    /* ... */

    registerChildren(): ComponentConfig[] {
        return [
            /* ... */
            {
                selector: 'edit',
                factory: (el) => new TaskActionComponent(el, {
                    callback: () => this._goToEdit(),
                    textContent: 'Edit',
                })
            },
        ]
    }

    private _goToEdit(): void {
        Navigation.navigateTo(`/tasks/${this._task.id}`)
    }
}
<div class="task-card">
    <!-- ... -->
    <div class="task-card__actions">
        <div data-component="edit"></div>
        <div data-component="delete"></div>
    </div>
</div>
.task-card__actions {
    padding-top: .5rem;
    display: flex;
    justify-content: flex-end;
}

Next step →