I found this post. However, writing a plugin in currently not an option, and I'd like to do this with Vue.js since I'm already using it for form validation & other stuff. Also, this involves toggling radio buttons across entries/forms.
Scenario:
I have a Structure section called Competitions that allows users to submit one or more (usually three maximum) entries to judged photo competitions. However, they can only submit one or two if they wish.
Once the user has submitted entries, let's say three, they can view them and edit them as follows:
Choose which (only one allowed) of the three images they would like to have critiqued by the judges. This is currently a lightswitch radio button. If they select one image it gets set to
1
and the other two to0
. I have this working ATM, but if there's a better way, let me know.Choose the order in which they would like to have their images judged (1, 2, or 3. Must be unique per image). This is currently a radio button group per entry. So if entry A is set to 1, B set to 2, and C set to 3, if they set A to 2, then B would be "reset" (no option selected). I don't know how many entries a user will submit, so I'm looping through entries and assigning loop index to buttons.
Delete an entry. Not implemented yet, but I think I can manage.
Once the user has made the above changes, I'd like them to be able to click one button called "Save changes" to save all of the entries at once.
However, I'm not sure if the forms should be saved immediately when toggling radio buttons, and then the "Save changes" button is the last step that sends everything to server & refreshes the template to display the changes made to the image order (template orders entries by this field).
Below is my code so far. You'll notice that I've created one Vue instance per form. Not sure if this is necessary or even desirable.
Also, I don't know if I'm going about this the right way. My skills in this area are limited. Please let me know if there's a better way.
Sreenshot of what code looks like in browser: https://cl.ly/ee7cbf47954a
Thanks for your help!
Form:
{# One form per entry #}
{% for entry in submissions %}
<form class="uk-form-horizontal" id="edit{{ entry.id }}" method="post" accept-charset="UTF-8" enctype="multipart/form-data" novalidate @submit.prevent="submitForm">
{{ csrfInput() }}
<input type="hidden" name="action" value="entries/save-entry">
{{ redirectInput('/') }}
<input type="hidden" name="sectionId" value="{{ section.id }}">
{# Set ID of Competition that will be submitting to #}
<input type="hidden" name="parentId" value="{{ competition.id }}">
{# ID of section's entry type #}
<input type="hidden" name="typeId" value="10">
<input type="hidden" name="enabled" value="1">
{# Required to edit entry #}
<input type="hidden" name="entryId" value="{{ entry.id }}">
{# Set hidden field to 0 #}
{# https://craftcms.stackexchange.com/questions/5146/updating-a-lightswitch-value-not-working-via-front-end #}
<input type="hidden" name="fields[competitionSubmissionCritique]" value="0">
{# Redacted errors macro for brevity #}
{# Image submission order #}
{# Allows users to chose in which order they want their submissions judged #}
{% set competitionSubmissionImageOrder = craft.app.fields.getFieldByHandle('competitionSubmissionImageOrder') %}
<div class="uk-margin">
<div class="uk-form-label">Image order</div>
<div class="uk-form-controls">
{# Loop through entries to get image order numbers for radio buttons #}
{# Use submission instead of entry so don't conflict with entry.id #}
{% for submission in currentUserSubmissions %}
<label>
<input class="uk-radio" type="radio" id="{{ loop.index }}" name="fields[{{ competitionSubmissionImageOrder.handle }}]" {% if entry is defined %}value="{{ entry.competitionSubmissionImageOrder }}"{% endif %} v-model="entry.competitionSubmissionImageOrder" v-bind:value="'{{ loop.index }}'" @change="setImageOrder{{ entry.id }}">
{{ loop.index }}
</label>
{% endfor %}
</div>
</div>
{# /Image submission order #}
{# Image critique #}
{# Allows users to chose which image they want judges to critique. Can only be one image. #}
<div class="uk-margin">
<div class="uk-form-label">Critique image?</div>
<div class="uk-form-controls">
<div>
<label>
<input class="uk-radio" type="radio" name="fields[competitionSubmissionCritique]" value="1" {% if entry is defined and entry.competitionSubmissionCritique == 1 %} checked{% endif %} v-model="entry.competitionSubmissionCritique" @change="isCritiqued{{ entry.id }}">
</label>
</div>
</div>
</div>
{# /Image critique #}
{# Save changes button #}
<div class="uk-margin">
<button class="uk-button uk-button-primary uk-button-large uk-width-1-1" type="submit" value="Submit" @click="submitForm"><span :data-uk-icon="'icon: upload'"></span> Save changes</button>
</div>
{# Vue.js debugging #}
<p>Vue.js debug:<br>${entry}</p>
</form>
{% endfor %}
Javascript/Vue:
<script>
// Required for Vue Resource
Vue.http.options.emulateJSON = true;
Vue.http.options.emulateHTTP = true;
// Craft CSRF token
window.csrfTokenName = "{{ craft.app.config.general.csrfTokenName }}";
window.csrfToken = "{{ craft.app.request.csrfToken }}";
//Vue.use(VeeValidate);
{# Vue instance for each form. Necessary? #}
{% for entry in currentUserSubmissions %}
var vm{{ entry.id }} = new Vue({
el: '#edit{{ entry.id }}',
delimiters: ['${', '}'],
data: {
entry: {
action: 'entries/save-entry',
entryId: '{{ entry.id }}',
competitionSubmissionImageOrder: '{{ entry.competitionSubmissionImageOrder }}',
competitionSubmissionCritique: '{{ entry.competitionSubmissionCritique }}',
},
},
methods: {
setImageOrder{{ entry.id }}: function() {
// Not working!!
// Get related entries
{% set relatedEntries = currentUserSubmissions.id('not ' ~ entry.id).all() %}
// Loop through related entries & set them to OFF
{% for relatedEntry in relatedEntries %}
if (vm{{ relatedEntry.id }}.entry.competitionSubmissionImageOrder === this.entry.competitionSubmissionImageOrder) {
vm{{ relatedEntry.id }}.entry.competitionSubmissionImageOrder = '0';
}
{% endfor %}
},
isCritiqued{{ entry.id }}: function() {
// Works!!
// Get related entries
{% set relatedEntries = currentUserSubmissions.id('not ' ~ entry.id).all() %}
// Loop through related entries & set them to OFF
{% for relatedEntry in relatedEntries %}
vm{{ relatedEntry.id }}.entry.competitionSubmissionCritique = 0;
{% endfor %}
},
submitForm: function () {
var form = document.getElementById('edit{{ entry.id }}');
var formData = new FormData(form);
//var data = this.entry;
formData[window.csrfTokenName] = window.csrfTokenValue; // Append CSRF Token
//data[window.csrfTokenName] = window.csrfTokenValue; // Append CSRF Token
//data[csrfTokenName] = csrfToken;
this.$http.post('/', formData)
//this.$http.post('/', data)
.then(function(response) {
console.log('saved', response);
});
},
}
});
{% endfor %}
</script>
UPDATE 1:
It turns out that the setImageOrder
function actually works. I had confused myself by the image order change when refreshing template. Calling the submitForm
function from it also seems to work:
setImageOrder{{ entry.id }}: function() {
// Works
// Get related entries
{% set relatedEntries = currentUserSubmissions.id('not ' ~ entry.id).all() %}
// Loop through related entries & 'reset' if match current entry
{% for relatedEntry in relatedEntries %}
if (vm{{ relatedEntry.id }}.entry.competitionSubmissionImageOrder === this.entry.competitionSubmissionImageOrder) {
vm{{ relatedEntry.id }}.entry.competitionSubmissionImageOrder = '';
}
{% endfor %}
// Also works
this.submitForm();
},
UPDATE 2:
My isCritiqued
is working as follows with setTimeout
:
isCritiqued{{ entry.id }}: function() {
// Works 2018-10-08
// Get related entries
{% set relatedEntries = currentUserSubmissions.id('not ' ~ entry.id).all() %}
{% for relatedEntry in relatedEntries %}
if (vm{{ relatedEntry.id }}.entry.competitionSubmissionCritique === this.entry.competitionSubmissionCritique) {
vm{{ relatedEntry.id }}.entry.competitionSubmissionCritique = '0';
setTimeout(vm{{ relatedEntry.id }}.submitForm, 2000);
}
{% endfor %}
this.submitForm();
},