Ember Patterns ************** - :doc:`dev-ember` - :doc:`dev-ember-addons-kb` - :doc:`dev-ember-addons` - :doc:`dev-ember-auth` - :doc:`dev-ember-data` Forms ===== Move your form to it's own component e.g:: {{#if @model.schedule.isRunning}} {{else}} {{#with @model.schedule.value as |schedule|}} {{/with}} {{/if}} Using ``ember-changeset`` and validations:: import Component from "@glimmer/component" import lookupValidator from "ember-changeset-validations" import ScheduleValidations from "../validations/schedule" import { action } from "@ember/object" import { Changeset } from "ember-changeset" import { inject as service } from "@ember/service" import { task } from "ember-concurrency" import { tracked } from "@glimmer/tracking" export default class WorkflowDeleteFormComponent extends Component { @service kbMessages @tracked changeset = null constructor(owner, args) { super(owner, args) console.log(this.args.schedule) this.changeset = new Changeset( this.args.schedule, lookupValidator(ScheduleValidations), ScheduleValidations ) } @action submitForm() { this.saveTask.perform() } @task *saveTask() { let process_id = this.changeset.get("id") this.changeset.validate() if (this.changeset.isValid) { try { yield this.changeset.save() if (this.args.routeTransition) { this.args.routeTransition(process_id) } else { console.log( "WorkflowDeleteFormComponent ('components/workflow-delete-form.js') no 'routeTransition'" ) } } catch (e) { this.kbMessages.addError("Cannot delete the workflow process", e) } } } } To allow empty dates, use the following code:: if (this.changeset.isValid) { try { if (this.changeset.get("startDate") == "") { this.changeset.set("startDate", null) } yield this.changeset.save() In the form, highlight ``changeset`` and *server side* errors:: .. tip:: To return server side errors, check out the ``_deleted_comment`` code from ``ScheduledWorkflowUserViewSet`` (``work/api.py``). 08/04/2021, Not sure if this works with serializer errors or our ``custom_exception_handler`` (``api/models.py``). Notifications ============= Use the ``KbMessages`` service: https://gitlab.com/kb/kb-base-ember-addons/-/blob/tailwind-2-update-ember/addon/services/kb-messages.js :: import { inject as service } from "@ember/service" @service kbMessages try { // do something } catch (e) { this.kbMessages.addError("Cannot delete the workflow process", e) } Page ==== :: import { inject as service } from "@ember/service" @service kbPage export default class WorkflowRoute extends Route { beforeModel(transition) { this.kbPage.setTitle("Workflow") } .. _dev-ember-patterns-pagination: Pagination (JSON API) ===================== Example ``pagination`` (``meta``) returned from a JSON API view:: {page: 5, pages: 191, count: 3817} Pagination ========== Configure ``KbRestPagination`` as the ``pagination_class`` for the viewset:: from api.api_utils import SoftDeleteViewSet class IssueViewSet(SoftDeleteViewSet): authentication_classes = (TokenAuthentication,) permission_classes = (IsAuthenticated,) serializer_class = IssueSerializer In your controller:: queryParams = ["page"] @tracked page = 1 get recordsPerPage() { /** * Number of records per page (``perPage``). * * This is a fixed value in Django and Ember code. */ return 20 } @action setPage(page) { this.page = page } In your route, refresh the ``page`` and add ``perPage`` / ``totalPagesParam`` parameters:: queryParams = { page: { refreshModel: true, }, sort: { refreshModel: true, }, company_name: { refreshModel: true, }, }; contactTask = task(async (params) => { let filter = {}; if (params.company_name) { filter = { company_name: params.company_name }; } try { let contact = await this.store.query('contact', { page: { number: params.page, }, sort: params.sort, filter: filter, }); return await contact; } catch (e) { this.kbMessages.addError('Cannot load contact', e); } }); Add the ``KbPagination`` component in your template (just before the table):: {{#if @model.tickets.isRunning}}{{else}} {{/if}} .. tip:: If the ``KbPagination`` is inside a ``SlideOver``, then add ``@isSlideOver={{true}}``. Slide Over ========== :: {{#if this.slideOverVisible}} // Content of Slide-over... {{/if}} .. _dev-ember-patterns-tables-data: Tables / Data ============= Checklist 1. Add ``reload: true`` to all ``findRecord`` and ``findAll`` methods For details, see :ref:`ember-error-handling` 2. Add a ``try``, ``catch`` to all ``store`` methods. Use ``kbMessages.addError`` in the ``catch`` (see example below). 3. Test the ``store`` methods with an exception on the server (500 error). 4. Check the user is notified if a ``store`` method returns no data. Route:: import { inject as service } from "@ember/service" @service kbMessages model(params) { return { processes: this.processesTask.perform(params) } } @task *processesTask(params) { try { let processes = yield this.store.query("process", params) return yield processes } catch (e) { this.kbMessages.addError("Cannot load workflow processes", e) } } The components for tables can be found here :ref:`dev-ember-addons-kb-table`. To add a spinner and *nothing found* to your table:: {{#if @model.processes.isRunning}} {{else}} {{#each @model.processes.value as |process|}} {{else}} {{#if this.workflow}} No workflow processes found ... {{else}} Please select a workflow... {{/if}} {{/each}} {{/if}} .. tip:: For pagination, see :ref:`dev-ember-patterns-pagination`.