Viewing and Updating ==================== With that in place, we will now implement the viewing and updating of modules. Viewing a Module ---------------- First create a new file ``src/js/routes/modules/view.jsx`` and add the following code: .. code-block:: jsx import m from "mithril"; import Athena from "../../athena.jsx"; import api from "../../api.js"; export default function View() { let module = null; let teacher = null; return { oninit: (vnode) => { m.request({ method: "GET", url: `${api.base}/modules/${vnode.attrs.mid}`, }).then((data) => { module = data; m.request({ method: "GET", url: `${api.base}/users/${data.data.relationships.teacher.data.id}`, }).then((data) => { teacher = data; }); }); }, view: (vnode) => { if (module && teacher) { return (

{module.data.attributes.code} {module.data.attributes.name}

Semester
{module.data.attributes.semester}
Contact
{teacher.data.attributes.email}
Edit
); } else { return
Loading...
; } }, }; } The code again demonstrates the high level of similarity between React and Mithril, with the only difference in the data acquisition. Most of that you are already familiar with, but as we are now accessing a single module, we will want to have a dynamic route segment with the module id, just as we did in Ember and React. This time, we stored our data inside two variables, however, thus we can access the ``vnode`` properties through these variables. In Mithril these dynamic route segments are available via the ``vnode.attrs`` property: .. code-block:: js mithril.request({ method: 'GET', url: `${api.base}/modules/${vnode.attrs.mid}` }) Here we have used a ``mid`` key to access the module id, so when we define the dynamic route in the ``src/js/index.js``, we need to use the same name: .. code-block:: js import m from "mithril"; import Athena from "./athena.jsx"; import ModulesIndex from "./routes/modules/index.jsx"; import New from "./routes/modules/new.jsx"; import View from "./routes/modules/view.jsx"; import '../styles/app.scss' const root = document.getElementById("root"); m.route(root, "/", { "/": Athena, "/modules": ModulesIndex, "/modules/new": New, "/modules/:mid": View }); As you can see, all frameworks use the same colon + identifier pattern (``':mid'``) to define dynamic route segments. One difference between Mithril and React Class Components is that routes here are always matched exactly, thus unlike in these Class Components there is no need for an ``exact`` keyword. Functional Components did not need this keyword as well. Before you can navigate, we need to link the view from the ``src/js/routes/modules/index.jsx``, updating the following lines: .. code-block:: jsx ... if(vnode.state.modules) { modules = vnode.state.modules.data.map((module) => { let key = `module-${module.id}`; return ( {module.attributes.code} {module.attributes.name} ... You can now try out navigating to the individual modules. Updating a Module ----------------- If you look at the ``view.jsx`` JSX code, you will see that there is already a link to the edit functionality route, so let us now implement that in a new file ``src/js/routes/modules/edit.jsx``: .. code-block:: jsx import m from "mithril"; import Athena from "../../athena.jsx"; import api from "../../api.js"; export default function Edit() { let module = null; let teacher = null; function setCode(ev) { module.data.attributes.code = ev.target.value; } function setName(ev) { module.data.attributes.name = ev.target.value; } function setSemester(ev) { module.data.attributes.semester = ev.target.value; } function updateModule(ev) { ev.preventDefault(); m.request({ method: "PATCH", url: `${api.base}/modules/${module.data.id}`, body: module, }).then((data) => { m.route.set(`/modules/${data.data.id}`); }); } return { oninit: (vnode) => { m.request({ method: "GET", url: `${api.base}/modules/${vnode.attrs.mid}`, }).then((data) => { module = data; m.request({ method: "GET", url: `${api.base}/users/${data.data.relationships.teacher.data.id}`, }).then((data) => { teacher = data; }); }); }, /** * Render the Component */ view: (vnode) => { if (module && teacher) { return (

Edit {module.data.attributes.name}

); } else { return
Loading...
; } }, }; } The edit functionality demonstrates a few differences from the equivalent React code. First of all, we don't have to work with ``setState``, we can simply modify the value of the ``module`` object that we fetched from the remote API. The advantage of that is that when we actually send the update request, we can simply pass ``module`` as the ``data`` and a correctly formated module object will be sent to the server. Also, it means that we do not have to deal with default and changed data, as the ``module`` object immediately contains both. At the same time, because we are dealing with raw JSONAPI data, the paths to access individual objects are much longer (``{module.data.attributes.name}``). One thing to note here is just a JSONAPI specific aspect, namely that if you want to update a single object, you use the same URL as for accessing that single object, but this time you use the ``'PATCH'`` request method. We still need to link the new component into our routes, so update the ``src/js/index.js`` .. code-block:: js import m from 'mithril'; import Athena from './athena.jsx'; import ModulesIndex from './routes/modules/index.jsx'; import New from './routes/modules/new.jsx'; import View from './routes/modules/view.jsx'; import Edit from './routes/modules/edit.jsx'; const root = document.getElementById('app-entry-point) m.route(root, '/', { '/': Athena, '/modules': ModulesIndex, '/modules/new': New, '/modules/:mid': View, '/modules/:mid/edit': Edit }) and then you will be able to view and edit your modules.