Render form after method completion in VueJS - vue.js

I am facing a problem with my page with VueJS. It's a page for different translations of the website. It has a dropdown on the top for the language selection that once switched will update the fields with the current language.
The problem starts when it loads, because my form is like this:
<form id="trForm">
...
<input type="text" name="header_title" class="form-control" v-model="translations.header.header_title" />
...
</form>
It's trying to access these attributes before the method returns any data, but somehow it will still show the data once it is complete, but it becomes troublesome when I try to switch the language, it won't because of this problem and also, if I do the following:
<form id="trForm">
...
<input type="text" name="header_title" v-if="translations.header" class="form-control" v-model="translations.header.header_title" />
...
</form>
on each field, those that aren't populated will display no field at all for a new input value. I tried something like translations.features || '', but no success.
I also tried to put on the parent block a condition that if the loading is false will display the form, but since the page is loaded first than the method is executed, it will always be false for the first microsecond.
methods: {
fetchTranslations(e) {
let vm = this;
vm.loaded = false;
$.get('/ajax/admin/translations', { 'locale': e }).done((data) => {
if (data.success) {
vm.translations = JSON.parse(data.translations.translation);
vm.loaded = true;
} else {
toastr.error('Something went wrong');
}
});
},
Please, what do I do? It'd be good to show the form after there is data.

Introduce a new variable, e.g. loaded that defaults to false
Use this variable as a v-if condition on the form
In the callback of your data fetch, set loaded to true.

Related

handle errors with HTMX

<form
class="" id="form" hx-post="/add/" hx-swap="afterbegin" hx-target="#big_list" hx-trigger="submit">
<input type="text" name="langue1" >
<input type="text" name="langue2">
<div id="errors"></div>
<button type="submit">GO</button>
</form>
<div id="big_list">
.....
</div>
I have a big list in #big_list, and I want my #form appends only one row when submitted.
How with htmx, can I handle errors and show message in #errors ?
I created this solution so you can use hx-target-error = to define which HTML will be displayed after a failed request
document.body.addEventListener('htmx:afterRequest', function (evt) {
const targetError = evt.target.attributes.getNamedItem('hx-target-error')
if (evt.detail.failed && targetError) {
document.getElementById(targetError.value).style.display = "inline";
}
});
document.body.addEventListener('htmx:beforeRequest', function (evt) {
const targetError = evt.target.attributes.getNamedItem('hx-target-error')
if (targetError) {
document.getElementById(targetError.value).style.display = "none";
}
});
If your code raises the errors (validation?), you can change target and swap behavior with response headers.
Response.Headers.Add("HX-Retarget", "#errors");
Response.Headers.Add("HX-Reswap", "innerHTML");
If you want to return a status other than 200, you have to tell htmx to accept it.
4xx would normally not do a swap in htmx. In case of validation errors you could use 422.
document.body.addEventListener('htmx:beforeOnLoad', function (evt) {
if (evt.detail.xhr.status === 422) {
evt.detail.shouldSwap = true;
evt.detail.isError = false;
}
});
It works in htmx 1.8.
If you want to remove the error message on then next sucessfull request, you could use hx-swap-oob. Out of band elements must be in the top level of the response.
So the response could look like this:
<div>
your new row data...
</div>
<div id="errors" hx-swap-oob="true"></div>
Update
You can now use the new powerful extension multi-swap to swap multiple elements arbitrarily placed and nested in the DOM tree.
See https://htmx.org/extensions/multi-swap/
Although it doesn't follow REST principles, you might consider using an swap-oob to report your error back to your user. For example, your request might return a (slightly misleading) status 200, but include content like this:
<div id="errors" hx-swap-oob="true">
There was an error processing your request...
</div>
If it's important to follow REST more precisely, then you'll want to listen to the htmx:responseError event, as mentioned by #guettli in his previous answer.

In Vue, how to get the content of a textarea?

I want to keep the value of a variable identical with the content of a textarea.
I don't want to use v-bind or v-model, because I have already bound the textarea with another value.
This is a notebook app, and the textarea is used to display the content of a note, so it has been bound using v-bind with a note object, like
<textarea cols="30" rows="3" v-bind:value="note"></textarea>
Now, I want to add the "edit note" functionality. So when the content of the textarea changes, I want to store its value into a variable, and when the "submit" button is clicked, I pass the value of the variable, which contains the new content of the note, to backend to update the note.
My question is, how to store the textarea's content into the variable after each time the content changes?
I think I cannot use v-model because this way the note will be changed right after the content of the textarea is modified (though not sent to backend), but this is not what I want. What I want is the note to be changed only after the "submit" button is clicked. Thus, I cannot use v-model
Should I use v-on:change? If so, how to get the content of the textarea?
Like,
<textarea v-on:change="updateTheVariable(I need to get the content of the textarea here)"> ... </textarea>
methods: {
updateTheVariable(content of the textarea) {
this.variable = content of the textarea
}
}
Thanks
I'm assuming this thing only shows up when you click some kind of edit button which is why you don't want to alter note so try something like this instead
<button type="button" v-if="!editMode" #click="editNote">Edit</button>
<form v-if="editMode" #submit="handleSubmit">
<fieldset :disabled="saving">
<textarea v-model="editingNote"></textarea>
<button type="submit">Edit</button>
</fieldset>
</form>
export default {
data: () => ({
note: 'whatever', // maybe it's a prop, maybe assigned later, doesn't matter
editMode: false,
editingNote: null, // this will be used to bind the edited value
saving: false
}),
methods: {
editNote () {
this.editingNote = this.note
this.editMode = true
this.saving = false
},
async handleSubmit () {
this.saving = true // disables form inputs and buttons
await axios.post('/notes/update', { note: this.editingNote}) // just an example
this.note = this.editingNote // or maybe use data from the response ¯\_(ツ)_/¯
// or if it's a prop, this.$emit('updated', this.editingNote)
this.editMode = false
}
}
}
As #Phil indicated in a deleted post, the right way to do it is
<textarea #input="updateTheVariable($event.target.value)"></textarea>
.
.
.
methods:{
updateTheVariable(value){
this.variable = value
}
}

How to prevent reactivity in form placeholder

I have an input field set up as
<input #input="e => machineCIDR = e.target.value" type="text" name="machine" class="form-control form-control-lg" :placeholder="machineCIDR">
The problem is, whenever someone fills out the form and then deletes it all, the placeholder is left with the last character that was filled out.
How can I setup :placeholder="machineCIDR" so that it shows the initial value, and then never gets updated again??
Assuming machineCIDR is set up already (as a prop or in data), you could create an object for storing all of your initial values:
data() {
return {
...
initial: {}
}
}
And set up the values in created:
created() {
this.initial['machineCIDR'] = this.machineCIDR;
}
Then bind to that instead in the placeholder:
:placeholder="initial['machineCIDR']"

Vue.js: binding select boxes, but don't want to ajax all the options

Good day. I'm using Vue.js to render an arbitrary number of select elements from the data in a component.
Here's sample JSON data that indicates there are two select elements, each with one or more options.
{
"dropdowns":[
{
"cd":"UG9ydGZvbGlv",
"formname":"sp_filter_UG9ydGZvbGlv",
"nm":"Portfolio",
"selected":"1a",
"options":[
{
"cd":"1a",
"val":"Option 1A"
}
]
},
{
"cd":"UHJvZHVjdCBOYW1l",
"formname":"sp_filter_UHJvZHVjdCBOYW1l",
"nm":"Product Name",
"selected":"2b",
"options":[
{
"cd":"2a",
"val":"Option 2A"
},
{
"cd":"2b",
"val":"Option 2B"
}
]
}
]
}
Here's the template HTML:
<form>
<div v-for="dropdown in dropdowns">
<div v-if="dropdown.availableToView">
<h4>{{dropdown.nm}}</h4>
<select v-model="dropdown.selected" v-on:change="triggerUpdate">
<option value="">(Make a selection)</option>
<option v-for="option in dropdown.options" :value="option.cd">{{option.val}}</option>
</select>
</div>
</div>
</form>
So far so good.
I've got the data loading and Vue is building the dropdowns.
When the user changes any select box (remember there can be an arbitrary number of them), the trigger action needs to submit ALL of the elements in the form via ajax. It sounds like the most correct option is to bind the form fields to the underlying component data, as I've done.
My triggerUpdate looks like this:
methods: {
triggerUpdate: function() {
axios({
method: "post",
url: actionURL,
data: this.dropdowns
})
.then(response => (this.data = response));
}
}
...but this submits the entire dropdowns data element, including all of the options in each select box. It's unnecessary to send all of the options in. I just want to send each field name along with its selected option (i.e. the "value").
I know i could serialize the whole form and make that my ajax payload. But that seems to be making an "end run" around Vue.js. Everyone talks about having your form fields bound to the Vue model...is it then correct to basically ignore the model when making an ajax request whose purpose is to then update the model?
I'm relatively new to Vue.js so I'd appreciate help with what I'm overlooking here. How should I go about sending in the data from the form (a) while using proper Vue.js binding and (b) without sending extraneous data?
Thanks for your time.
If you need to post only the selected values, and you store those in each dropdown's selected property, the sensible approach seems to be just mapping it to a simple array of name/value objects.
Try this (it assumes the name of each field is the formname property, if it isn't you can just replace it):
var submitData = this.dropdowns.map((dropdown) => {
return { name: dropdown.formname, value: dropdown.selected };
});
Then you send submitData in your ajax request.

simple html value for nested form

I'm using rails, and backbone with handlebars, and I have a nested form.
I am trying to create a user, and the user can supply a url.
So I have a user model and a url model
I thought I could do something like this
<form id="new_user">
<label name="username" >username</label>
<input type="text" name="username" />
<label name="url" >url</label>
<input type="text" class="nested urls_attributes" name="url" />
</form>
----------------------update -----------------------------------------
as this is something that will be seldomly used, I've updated my backbone view to create the correct json when I submit the form, so now on submit form, I create the first json, then add the nested object.
var form_json = MyApp.Helpers.Serialize_Form($('form#user_form inputs').not('.nested'));
form_json['urls_attributes']=MyApp.Helpers.Serialze_Form($('form#user_form inputs.nested'));
this creates the json in the format of
{username: "test", urls_attributes: {url:"http://test"}}
So when I pass that to my controller, I expected that the url would be created when the user is created.
Unfortunately, that doesn't seem to be happening, and I have no errors. The user gets created, but not the nested model of urls.
Any idea why this is happening?
-----------how I make the json----------------------
I convert my form into a json object with
serialize_objects: function(inputs){
//this takes form inputs and creates a json string of them
var o = {};
$.map(inputs, function(t) {
if (o[t.name] !== undefined) {
if (!o[t.name].push) {
o[t.name] = [o[t.name]];
}
o[t.name].push(t.value || '');
} else {
o[t.name] = t.value || '';
}
});
return o;
}
I can't use jquery serializeArray because I'm using jqMobi which doesn't implement serializeArray. But that shouldn't matter for this question.