How do I insert object created with document.createElement into template? - vuejs2

I like to know how I can insert a Element that has already been created with the document.createElement method into the template. I am not sure how to proceed here because eventually I would also like to bind the contents of that particular textBox. Here is the (non working) code that I have up till now to illustrate what I like to do:
<template>
<div>
<p id="status">{{ statusMessage }}</p>
<div id="output" v-html="textBox"></div>
</div>
</template>
<script>
export default {
name: 'Result',
data() {
return {
statusMessage: "Status",
textBox: Object,
}
},
mounted() {
this.textBox = this.$someModule.createTextBox()
console.log('textBox should become: '+this.textBox)
// Shows: textbox should become: [object HTMLTextAreaElement]
},
...
}

First of all, v-html is a directive that allow you to use raw html text.
ref: https://v2.vuejs.org/v2/guide/syntax.html#Raw-HTML
Second of all, you can you a ref directive to create a link to you element (ref="someName"):
<div id="output" ref="textBox"></div>
Then:
const el = this.$refs.textBox;
el.appendChild('entity which you want to append');

Related

Vue v-model does not select value on checkbox

I'm fairly new to Vue and I've researched as much as I could, but cannot find a solution to this strange issue. I'm building a filter function for an online shop, and one section allows filtering based on values with a checkbox.
My vue template is as following:
<template>
<div>
<h3>{{data.filterLabel}}</h3>
<ul>
<li v-for="(item, index) in data.options" :key="index">
<input v-model="values" type="checkbox" :id="item" :value="item" :index="index" />
<label class="products__label products__capitalize" :for="item">{{ item }}</label>
</li>
</ul>
</div>
</template>
I am getting the options from a database, and loop through the data.options array with v-for. I have created a new empty array in
data() {
return {
values: []
};
},
as in the form-bindings example on the vue.js website here: https://v2.vuejs.org/v2/guide/forms.html#Checkbox
My script is as following:
<script>
export default {
name: "CheckBoxFilter",
data() {
return {
values: []
};
},
props: {
data: Object,
filterCheckBox: Function
},
watch: {
values: function(value) {
const optionRange = JSON.parse(JSON.stringify(this.values));
this.$emit("filterCheckBox", this.data.filterValue, optionRange);
}
}
};
</script>
For some strange reason, the $emit function works perfectly fine, and the array of products is filtered correctly in the UI. But when I check a value in the checkbox, the checkbox is not ticked. How is it possible that the checkbox is not ticked, while at the same time it is clearly correctly filtering the values?
I even looked at the :checked value with $event.target.checked which also correctly returns true or false, but the checkbox is still not ticked in the UI.
I have the same issue with radio buttons.
There are no issues with the <input type="text"> and also no issues with a <select>.
Has anyone experienced this before and if so what is the solution?
Thanks!
I tested and the UI displays the checked/unchecked checkboxes properly. Which version of Vue do you use? I'm not sure of what you want to do, but I think it would be cleaner to expose your values through a computed property:
export default {
name: "CheckBoxFilter",
props: {
data: Object,
},
data() {
return {
internalValues: [],
};
},
computed: {
values: {
get() {
return this.internalValues;
},
set(newVal) {
this.internalValues = newVal;
this.$emit("filterCheckBox", this.data.filterValue, [...newVal]);
},
},
},
};
</script>
With your current implementation, the values change are not observable and the filterCheckBox event is never emitted.
EDIT: I also don't understand why you set a filterCheckBox prop, it is not React ;)

passing object to component using v-for

I am trying to send a series of objects that are in an array to a child component using v-for, but when I try to access them from the child component, it tells me that the props are not defined.
Im using Quasar Framework actually
This is how I pass the data:
<div class="row justify-center">
<foo
v-for="brand in brands"
:key="brand.id"
:brand="brand"
></foo>
</div>
<script>
import foo from "src/components/foo.vue";
export default {
components: {
foo
},
data() {
return {
brands: []
};
},
methods: {
async getData() {
let x = await get.getData();
this.brands = x.data;
console.log(this.brands);
}
},
mounted() {
this.getData();
}
};
</script>
brands is an array that obtains two objects from a request made to a local database, which I have already verified that it receives the data correctly
And this is the component file and how I try to get the properties:
<q-card class="my-card" flat bordered>
<q-img
:src="require(`../assets/${brand.img}`)"
:alt="brand.img + ' Logo'"
/>
<div class="text-h5 q-mt-sm q-mb-xs">{{ brand.name }}</div>
<div class="text-caption text-grey">
<p>
{{ brand.price }}
</p>
</div>
<script>
export default {
name: "foo",
props: ["brand"],
data() {
return {
expanded: false
};
},
};
</script>
but when I try to execute the code it gives me the following error:
Error in render: "Error: Cannot find module './undefined'
I know one way to make it work, and it is by creating a property for each of the object's values, for example:
<component
v-for="brand in brands"
:key="brand.id"
:name="brand.name"
:price="brand.price"
></component>
But I dont think thats the correct way to do this....
try to change
import component from "src/components/component.vue";
to
import foo from "src/components/component.vue";
on your components section you just call foo instead of foo:component
I am not sure, but:
Looks like ${brand} is empty. Your function GetData() is async, so the <foo> is created before the GetData() has its data set/returned.
You can change
<foo v-for="brand in brands" :key="brand.id" :brand="brand"></foo>
To
<foo v-if="brands.length> 0" v-for="brand in brands" :key="brand.id" :brand="brand"></foo>
To make sure that the element is renderd after the data if set.
Note: v-if is when the html is rendered, v-show is just a css display hide, but the html is always renderd

Boostrap Vue & Vue - Load Form-Select with array

I'm working in a Laravel + Vue project. Initially I need to load a Vue component data from the view of Laravel. The idea is send by Prop parameter an array object with this data and from Vue component to load a Html Select element.
Unfortunately it's doesn't work. I'm new in Vue :(
From testing I want to load this Vue component send and static array: ['Customer1', 'Customer2'], I'm not send a variable array as for example {{ $customers }}. The goal will be send a variable from Laravel but for testing not.
I have made this html:
<div class="card-body">
<create-task-jobs v-bind:customers="['Customer1', 'Customer2']">
</create-task-jobs>
</div>
From bootstrap-vue I want to load a Select component(create-task-jobs) from array by Prop(v-bind:customers) parameter.
The code of my Vue component is:
<template>
<div>
<form #submit.prevent="agregar">
<h3>Afegir Tasques</h3>
<b-form-group id="input-group-3" label="Client" label-for="input-3">
<b-form-select
id="input-3"
v-model="form.customer"
:options="customers_load"
required
></b-form-select>
</b-form-group>
<button class="btn btn-primary" type="submit">Agregar</button>
</form>
</div>
</template>
<script>
export default {
props: {
customers: Array
},
data() {
return {
form: {
customer: null,
},
customers_load: [this.customers]
}
}
}
</script>
The current result is wrong because there is only an option separated by comma:
... but the correct behavior would be two option elements:
-> Customer1
-> Customer2
Please Could you help me with this issue?
Thanks.
You needs to use spread operator.
data() {
return {
form: {
customer: null,
},
customers_load: [...this.customers] 👈
}
}

Vue.js this.$refs empty due to v-if

I have a simple Vue component that displays an address, but converts into a form to edit the address if the user clicks a button. The address field is an autocomplete using Google Maps API. Because the field is hidden (actually nonexistent) half the time, I have to re-instantiate the autocomplete each time the field is shown.
<template>
<div>
<div v-if="editing">
<div><input ref="autocomplete" v-model="address"></div>
<button #click="save">Save</button>
</div>
<div v-else>
<p>{{ address }}</p>
<button #click="edit">Edit</button>
</div>
</div>
</template>
<script>
export default {
data() {
editing: false,
address: ""
},
methods: {
edit() {
this.editing = true;
this.initAutocomplete();
},
save() {
this.editing = false;
}
initAutocomplete() {
this.autocomplete = new google.maps.places.Autocomplete(this.$refs.autocomplete, {});
}
},
mounted() {
this.initAutocomplete();
}
}
I was getting errors that the autocomplete reference was not a valid HTMLInputElement, and when I did console.log(this.$refs) it only produced {} even though the input field was clearly present on screen. I then realized it was trying to reference a nonexistent field, so I then tried to confine the autocomplete init to only when the input field should be visible via v-if. Even with this, initAutocomplete() is still giving errors trying to reference a nonexistent field.
How can I ensure that the reference exists first?
Maybe a solution would be to use $nextTick which will wait for your DOM to rerender.
So your code would look like :
edit() {
this.editing = true;
this.$nextTick(() => { this.initAutocomplete(); });
},
Moreover if you try to use your this.initAutocomplete(); during mounting it cannot work since the $refs.autocomplete is not existing yet but I'm not sure you need it since your v-model is already empty.
I think it's because your "refs" is plural
<input refs="autocomplete" v-model="address">
It should be:
<input ref="autocomplete" v-model="address">

How to save an existing content?

I can't get how to use v-html to save an existing content. For example:
<div ref="content" v-html="content">Hello, World! A lot of divs</div>
How to make to div content was replace only when I will assign a some not null value with content? Or how to make it in another way? Or is the single way to request div content asynchronously?
The next way works, of course, but I lose a data binding.
this.$refs['content'].innerHTML = "New content";
P.S. I am migrating from jQuery and still can't think in Vue.js philosophy clearly.
Actualy, you must read vue documentation.
In your component you must declare content in data, and simply change it in oher places, i.e. in button's click handler or inside component's methods:
new Vue({
el: "#root",
data: function () {
return {
content: 'Hello, World! A <b>lot</b> of divs'
};
},
methods: {
changeText: function() {
this.content = 'This text from component';
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="root">
<div v-html="content"></div>
<button v-on:click="content = 'This text from button'">Click me</button>
<button v-on:click="changeText">And me</button>
</div>
You could assign a default value to content.
data () {
return {
content: 'Hello, World! A lot of divs'
}
}
When you'll assign a new value to content it will get rendered.
Another way would be to check if content is not null and have 2 different divs using v-if/v-else for conditional rendering.
<div v-if="content" v-html="content"></div>
<div v-else>Hello, World! A lot of divs</div>
and the script
export default {
name: 'customComponent',
data () {
return {
content: null
}
}
}