Updating and Deleting ===================== The last two aspects of implementing the full model life-cycle are updating and deleting models. Updating -------- Updating is basically a combination of the display of a single module, together with the form functionality of creating a new module. Because you are already familiar with the majority of concepts, the explanations will be kept to a minimum. We will first implement updating, so create a new route and component-class: .. code-block:: console $ ember generate route modules/edit $ ember generate component -gc edit-module As with the individual view route, we need to specify an explicit path for this route, to include the dynamic segment. Update the route definition in ``app/router.js`` to the following: .. code-block:: js this.route('edit', {path: ':mid/edit'}); Now that we have the dynamic segment, we can use that in the ``app/routes/modules/edit.js`` to fetch the model to edit: .. code-block:: js import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; export default class ModulesEditRoute extends Route { @service store; model(params) { return this.store.findRecord('module', params.mid); } } Which we can then display in the ``app/templates/modules/edit.hbs``: .. code-block:: html+handlebars {{page-title "Edit"}} and in the actual component view ``app/components/edit-module``: .. code-block:: html+handlebars

Edit {{@model.name}}

As you can see, for each input element, we have directly bound the property of the model to that input field. Thus when the value is changed by typing, it is automatically updated in the model -- but not saved to the server. At this point the code will actually not work, because of this line: .. code-block:: html+handlebars As you can see we set the ``selected`` attribute by checking whether the ``semester`` property of the module we are editing is equal to the string ``"WS18/19"``. By default Ember does not provide this kind of functionality, so we need to install an additional package. Stop the Ember build server and then run the following: .. code-block:: console $ yarn ember install ember-truth-helpers Now re-start the build server and update the ``app/component/edit-module.js`` to handle the updating. .. code-block:: js import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class EditModuleComponent extends Component { /** * Services */ @service router; @service store; /** * Untracked Properties */ classSpan = 'invalid-feedback'; classInput = 'is-invalid'; classLabel = 'text-danger'; /** * Actions */ /** * * Handle update of the module, reset error messages * @param {*} event prevent default behavior */ @action updateModule(event) { event.preventDefault(); this.errorCode = ''; this.errorName = ''; this.errorSemester = ''; this.args.model .save() .then((model) => { this.router.transitionTo('modules.view', model.id); }) .catch((response) => { response.errors.forEach((error) => { if (error.source.pointer == '/data/attributes/code') { this.errorCode = error.detail; } else if (error.source.pointer == '/data/attributes/name') { this.errorName = error.detail; } else if (error.source.pointer == '/data/attributes/semester') { this.errorSemester = error.detail; } }); }); } /** * * Handle reset of module to previouse state * @param {*} event prevents default behavior */ @action resetModule(event) { event.preventDefault(); if (this.args.model.hasDirtyAttributes) { this.args.model.rollbackAttributes(); } this.router.transitionTo('modules.view', this.args.model.id); } /** * Sets the semester to the param's value * @param {String} semester value of the semester */ @action setSemester(semester) { this.args.model.semester = semester; } } As you can see the code is relatively minimal. This is because we directly bound the model properties to the input fields and all we need to do is call ``save()`` to save the update model to the server and then re-direct to the individual modules view. Should you wish to discard the changes, there is a second action, called ``resetModule``. This action checks if there are any unsaved changes via the ``hasDirtyAttributes`` property. Should there be any, they are discarded by calling the ``rollbackAttributes`` function. In any case the user is brought back to the view page of this specific module. All that remains to be done is to create an edit link in the ``app/components/view-module.hbs``: .. code-block:: html+handlebars

{{@module.code}} {{@module.name}}

Semester
{{@module.semester}}
Contact
{{@module.teacher.email}}
Edit
You can now try out updating a module. Unlike creating new modules, the individual module view is immediately updated after saving. This is because the ``findRecord`` call is smart enough to remember which models have been loaded and updating them, when they are changed. Deleting -------- The final step is to implement the "delete" action. As a reminder, last week, we added a list of actions to the model in ``app/models/module.js``: .. code-block:: js import Model, { attr, belongsTo} from '@ember-data/model'; export default class ModuleModel extends Model { @attr code; @attr name; @attr semester; @belongsTo('user') teacher; get sections() { return { dates: { title: 'Dates', icon: 'mdi mdi-calendar-clock', }, documents: { title: 'Documents', icon: 'mdi mdi-file-document-box-multiple-outline', }, exercises: { title: 'Exercises', icon: 'mdi mdi-test-tube', }, students: { title: 'TeilnehmerInnen', icon: 'mdi mdi-account-multiple', }, }; } get actions() { return { delete: { icon: 'mdi mdi-delete warning', title: 'Delete', action: 'delete' } } } } If you look at the list of modules, you will see that the delete action is shown, but nothing happens if you click on it. Thus update the ``app/components/modules/modules-list.hbs`` to call an action in the "actions" area: .. code-block:: html+handlebars As you can see, here we call a dynamic action where the name is taken from what we defined in the ``actions`` getter. We also pass the module as a parameter to the action, using the ``action``-helper that basically behaves like the ``on``-helper and listens to ``click`` events. All we need now is a controller to handle this action: .. code-block:: console $ yarn ember generate component-class modules/modules-list Then add the following code to handle the delete action in ``app/components/modules/modules-list.js``: .. code-block:: js import Component from '@glimmer/component'; import { action } from '@ember/object'; export default class ModulesModulesListComponent extends Component { @action delete(module) { module.deleteRecord(); module.save(); } } Because we provided the module as a parameter to the action, when handing it, we can simply call the ``deleteRecord()`` to delete that module. ``deleteRecord()`` does not actually delete the record on the server side, for that we need to call ``save()``, it just marks the record for deletion. You can now try this out to see that we have implemented the full model life-cycle.