Managing Records using AJAX (CRUD operations)
This guide will help you create, list, update, and delete Records using JavaScript and GraphQL.
Requirements
Steps
Managing Records using AJAX by implementing the full CRUD cycle is a five-step process:
Step 1: Define prerequisites: Table, Form, Page, javascript functions used in multiple places
Create required files for your implementation:
app/schema/feedback.yml
name: feedback
properties:
- name: message
type: string
- name: rate
type: string
app/forms/feedback.liquid
---
name: feedback_form
resource: feedback
resource_owner: anyone
fields:
properties:
rate:
validation: { presence: true }
message:
validation: { presence: false }
---
{% include 'modules/feedback/create' %}
{% include 'modules/feedback/read' %}
{% include 'modules/feedback/update' %}
{% include 'modules/feedback/delete' %}
Warning
For example purposes resource_owner
is set to anyone
, which means on live example page you will be able to edit any record. In real life scenario it is recommended to set Authorization Policy to prevent even not logged in user to submit a form.
app/assets/Feedback.js
const request = ({ url, form }) => {
fetch(url, {
credentials: "same-origin", // make sure safari understands what we are doing
method: "POST",
body: new FormData(form) // create FormData from form passed or empty if undefined
})
};
[...]
Step 2: Implement Create
First, create a simple form that works without JavaScript:
app/views/partials/create.liquid
{% form html-data-form: "create" %}
{% assign p = form.fields.properties %}
<h2>Create new feedback (record)</h2>
<label>
<input type="radio" name='{{ p.rate.name }}' value="Excellent">
Excellent
</label>
<label>
<input type="radio" name='{{ p.rate.name }}' value="Meh">
Meh
</label>
<label>
<input type="radio" name='{{ p.rate.name }}' value="Very bad">
Very bad
</label>
<label for="create_message">Message</label>
<textarea type='text' id="create_message" name='{{ p.message.name }}'></textarea>
<button>Create</button>
{% endform %}
Then add JavaScript to make it work in the background:
app/assets/Feedback.js
[...]
const Create = event => {
event.preventDefault();
request({
url: event.target.getAttribute("action"),
form: event.target
})
};
const createForm = document.querySelector('[data-form="create"]');
createForm.addEventListener("submit", Create);
Step 3: Implement Read
First, you'll need a GraphQL query to pull out the data:
app/graphql/feedback.graphql
query feedback($per_page: Int = 10) {
records(
filter: {
table: { value: "feedback" }
},
per_page: $per_page,
sort: { created_at: { order: DESC } }
) {
results {
id
created_at
updated_at
rate: property(name: "rate")
message: property(name: "message")
}
}
}
Then you need to create a page that returns JSON with the data:
app/views/pages/feedback_list.json.liquid
{%- graphql recent = "modules/feedback/feedback", per_page: 10 -%}
{{ recent.records.results }}
You also need some HTML that will contain the data, and a button that will trigger data download:
app/views/partials/read.liquid
<button type="button" data-button="refreshRead">Refresh content from the server</button>
<table>
<thead>
<th>ID</th>
<th>Created at</th>
<th>Updated at</th>
<th>Rating</th>
<th>Message</th>
</thead>
<tbody data-body="readTable"></tbody>
</table>
You also need JavaScript to get the data from the server and render it into the HTML:
app/assets/Feedback.js
const updateReadTable = data => {
const readBody = document.querySelector('[data-body="readTable"]');
const html = data.map(
feedback => `<tr>
<td>${feedback.id}</td>
<td>${feedback.created_at}</td>
<td>${feedback.updated_at}</td>
<td>${feedback.rate}</td>
<td>${escape(feedback.message)}</td>
</tr>`
).join('');
readBody.innerHTML = html;
};
const Read = () => {
fetch("/feedback_list.json")
.then(response => response.json())
.then(updateReadTable)
};
const refreshReadButton = document.querySelector('[data-button="refreshRead"]');
refreshReadButton.addEventListener("click", Read);
Step 4: Implement Update
The update form is similar to Create, with a couple of differences:
- Add hidden input with
_method
set to PUT - Send ID of Record you want to edit
app/views/partials/update.liquid
{% form html-data-form: "update" %}
{% comment %} Set method using hidden input to tell the server we are updating {% endcomment %}
<input type="hidden" name="_method" value="PUT" />
{% comment %} Let user decide which record to edit {% endcomment %}
<label for="record_id">Record ID</label>
<input type="text" name='record_id' value="" required>
<label>
<input type="radio" name='{{ p.rate.name }}' value="Excellent">
Excellent
</label>
<label>
<input type="radio" name='{{ p.rate.name }}' value="Meh">
Meh
</label>
<label>
<input type="radio" name='{{ p.rate.name }}' value="Very bad">
Very bad
</label>
<label for="update_message">Message</label>
<textarea type='text' id="update_message" name='{{ p.message.name }}'></textarea>
<button>Update</button>
{% endform %}
JavaScript is also very similar, but it takes ID from input defined above and puts it into the URL.
app/assets/Feedback.js
[...]
const Update = event => {
event.preventDefault();
const id = event.target.querySelector('[name="record_id"]').value;
request({
url: `${event.target.getAttribute("action")}/${id}`,
form: event.target
})
};
const updateForm = document.querySelector('[data-form="update"]');
updateForm.addEventListener("submit", Update);
Step 5: Implement Delete
The delete operation also needs method
and id
because underneath it all, it is just an update.
Soft and hard delete
Read Data Backup and Removal to get familiar with data retention and backups in platformOS.
app/views/partials/delete.liquid
{% form html-data-form: "delete" %}
<input type="hidden" name="_method" value="DELETE" />
<label for="record_id">Record ID</label>
<input type="text" name='record_id' value="" required>
<button>Delete</button>
{% endform %}
app/assets/Feedback.js
const Delete = event => {
event.preventDefault();
const id = event.target.querySelector('[name="record_id"]').value;
request({
url: `${event.target.getAttribute("action")}/${id}`,
form: event.target
})
};
const deleteForm = document.querySelector('[data-form="delete"]');
deleteForm.addEventListener("submit", Delete);
Live example and source code
To play with a live example (in much fuller form and with additional information) go to feedback example page.
Source code can be found on GitHub.
Next steps
Congratulations! You know how to implement CRUD operations for Records.