Pass variable to component template - vue.js

I am trying to create a Vue.js component existing of some input fields. This means that the component template must accept name for the inputs.
Let's say I have the template:
<template>
<input type="text" name="VARIABLE">
</template>
and I call that component with
<component-input></component-input>
How do my component-input define the value of VARIABLE?

You can do like this
Vue.component('input-component', {
template: '<input type="text" :name="inputName">',
props: {
inputName: String
}
})
<input-component input-name="someName"></input-component>
The point to your question is to use props. Hope to help you.

I got it:
<template>
<input type="text" name="{{name}}">
</template>
-
<component-input name="demo"></component-input>
-
var component = Vue.extend({
props: {
name: {
type: String
}
}
});

Related

Binding v-model from of child component to its parent

In my parent component I have a simple input as
<input v-model="value" />
export default class ParentComponent extends Vue {
value = "" as string;
async check() {
try {
console.log(this.value) // this works, this get the actual value of the input
…
}
}
Now I want to create a child component with that input, so I try:
<template>
<input type="text" v-model="inputValue" />
</template>
<script lang="ts">
export default {
name: “BaseTextBox",
props: {
value: {
type: String,
default: "",
},
},
};
</script>
And in the parent component:
<BaseTextBox v-model="value" />
import BaseTextBox from "#/components/_base/BaseTextBox.vue";
The component display correctly, but when I try to access to this.value in method it display as empty string because did not get modified with v-model? How can I bind it correctly?
You are using wrong value "inputValue" in v-model. It would be "value". So your input will take the prop "value" as v-model and modify that directly.
Your child component would be:
<template>
<input type="text" v-model="value" />
</template>
<script lang="ts">
export default {
name: “BaseTextBox",
props: {
value: {
type: String,
default: "",
},
},
};
</script>

How to pass class attribute to child component element

how can I pass class attribute from parent to child component element?
Look here:
https://codesandbox.io/s/pedantic-shamir-1wuuv
I'm adding class to the Component "Input Field"
And my goal is that the 'custom-class' will be implemented on the 'input' element in the child component
But just still using the class attribute, and not setting a new prop like "customClass" and accept it in the props of the component
Thanks!
This depends on the template structure of your ChildComponent
Your Parent Component can look like this:
<div id="app">
<InputField class="anyClass" />
</div>
If your Child looks like this:
<template>
<input ... />
</template
Because if you have only 1 root Element in your template this will inherit the classes given automatically.
if your Template e.g. looks like this: (only available in vue3)
<template>
<input v-bind="$attrs" />
<span> hi </span>
</template
You will need the v-bind="$attrs" so Vue knows where to put the attributes to. Another Solution would be giving classes as props and assigning it to the element with :class="classes"
The pattern for the customs form component in Vue 2 where the props go to a child element looks something like this.
<template>
<div class="input-field">
<label v-if="label">{{ label }}</label>
<input
:value="value"
:class="inputClass"
#input="updateValue"
v-bind="$attrs"
v-on="listeners"
/>
</div>
</template>
<script>
export default {
inheritAttrs: false,
props: {
label: {
type: String,
default: "",
},
value: [String, Number],
inputClass: {
type: String,
default: "",
},
},
computed: {
listeners() {
return {
...this.$listeners,
input: this.updateValue,
};
},
},
methods: {
updateValue(event) {
this.$emit("input", event.target.value);
},
},
};
</script>
The usage of those components could look something like this.
```html
<template>
<div id="app">
<InputField
v-model="name"
label="What is your name?"
type="text"
class="custom"
inputClass="input-custom"
/>
<p>{{ name }}</p>
</div>
</template>
<script>
import InputField from "./components/InputField";
export default {
name: "App",
components: {
InputField,
},
data() {
return {
name: "",
};
},
};
</script>
A demo is available here
https://codesandbox.io/s/vue-2-custom-input-field-4vldv?file=/src/components/InputField.vue
You need to use vue props . Like this:
child component:
<template>
<div>
<input :class="className" type="text" value="Test" v-bind="$attrs" />
</div>
</template>
<script>
export default {
props:{
className:{
type:String,
default:'',
}
}
};
</script>
Parent component:
<div id="app">
<InputField :class-name="'custom-class'" />
</div>
Since you're using vue2 the v-bind=$attrs is being hooked to the root element of your component the <div> tag. Check the docs. You can put this wrapper on the parent element if you need it or just get rid of it.
Here is a working example
Another approach
There is also the idea of taking the classes from the parent and passing it to the child component with a ref after the component is mounted
Parent Element:
<template>
<div id="app">
<InputField class="custom-class" ref="inputField" />
</div>
</template>
<script>
import InputField from "./components/InputField";
export default {
name: "App",
components: {
InputField,
},
mounted() {
const inputField = this.$refs.inputField;
const classes = inputField.$el.getAttribute("class");
inputField.setClasses(classes);
},
};
</script>
Child Element:
<template>
<div>
<input type="text" value="Test" :class="classes" />
</div>
</template>
<script>
export default {
data() {
return {
classes: "",
};
},
methods: {
setClasses: function (classes) {
this.classes = classes;
},
},
};
</script>
Here a working example
In Vue2, the child's root element receives the classes.
If you need to pass them to a specific element of your child component, you can read those classes from the root and set them to a specific element.
Example of a vue child component:
<template>
<div ref="root">
<img ref="img" v-bind="$attrs" v-on="$listeners" :class="classes"/>
</div>
</template>
export default {
data() {
return {
classes: null,
}
},
mounted() {
this.classes = this.$refs.root.getAttribute("class")
this.$refs.root.removeAttribute("class")
},
}
Pretty much it.

Problem with watching prop changes Vue.js

What's the problem
I wanted to assign a local component variable to prop. I constantly get Vue alert Invalid watch handler specified by key "undefined". Maybe the case is that the prop is passed from another component, where I use v-model, but I don't really know. I would really appreciate your help, because my small exercise project really depends on this mechanic.
Parent Component
Here I have some HTML select, this is where I actually model my state.selectedPhysicsModule
<template>
<div>
<div>
<h1>Some header</h1>
</div>
<form class="choosePhysicsModule">
<label for="selectedPhysicsModule"></label>
<select class="select_Module" id="selectedPhysicsModule" v-model="state.selectedPhysicsModule">
<option :value="option.value" v-for="(option, index) in importedListToSelect" :key="index">
{{option.name}}
</option>
</select>
</form>
<list_of_exercises v-if="state.selectedPhysicsModule" :what_exercises="state.selectedPhysicsModule"/>
</div>
</template>
<script>
export default {
name: 'ChoosePhysicsModule',
components: {list_of_exercises},
setup() {
const state = reactive({
selectedPhysicsModule: null,
})
return {
state,
importedListToSelect
}
}
}
Child Component
</script>
export default {
name: "list_of_exercises",
props: {
whatExercises: {
type: String,
required: true
}
},
data() {
return {
exercises: this.what_exercises,
}
},
watch: {
whatExercises: function () {
this.exercises = this.whatExercises
}
}
In the parent component where you are passing the prop you need to add a setter for the prop passed. Here is an example:
<template>
<div id="app">
<label>
<input name="whatExercises" v-model="whatExercises">
</label>
<ListOfExercises v-if="whatExercises" :what_exercises="whatExercises" />
</div>
</template>
<script>
export default {
data() {
return {
whatExercises: null,
}
}
}
</script>
P.S: as a side note, I recommend using camelCase for prop names. It's more in-line with the rest of the community. If you have time feel free to check out the style guide on the official website.

How to get component data? How to access the components from the outside?

I derive the component:
<my-component> </my-component>
The question is how to get the properties of this particular component, knowing only its tag "my-component"?
A more detailed description of the problem:
Using the vue-cli-service build --target wc --name my-component my-component.vue command, I compile in js file
Then I connect this script in the header of the site.
Then I use the component
But how to get the properties of this component now and generally how to access this component? How to get the input field value in this component?
Sorry, I probably didn’t explain it well.
I need something like this:
<my-component id="my-component"> </my-component>
<script>
let inputValue = $("#my-component").find("input").val();//but this not work
</script>
Or
<script>
let props = <my-component></my-component>// this component props
</script>
In the my-component.vue file, this code:
<template>
<input :placeholder="test" type="text" name="test" value="">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
}
},
data () {
return {
}
},
}
</script>
usage
<template>
<input :placeholder="placeholder" type="text" #input="emitInputValue">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
}
},
methods: {
emitInputValue ($event) {
this.$emit('input', $event.target.value)
}
}
}
</script>
get value
<my-component #input"getValue"> </my-component>
methods: {
getValue (payload) {
console.log(payload)
}
}
<template>
<input :placeholder="test" :id="id" type="text" name="test" value="">
</template>
<script>
export default {
name: 'MyInputComponent',
props: {
placeholder: {
type: String,
default: "00"
},
id: {
type: String
}
},
data () {
return {
}
},
}
</script>
Now your id would be available and you can do whatever you want.
Only one suggestion i would like to give is: when you are using vue.js then try avoiding jquery for getting input value. there are many ways in vue.js itself you can get input text value.
Let me know if you need something else

v-model inside a renderless component

CodeSandbox: https://codesandbox.io/s/61my3w7xrw?fontsize=14
I have this renderless component that uses a scoped slot:
name: "BlockElement",
props: {
element: {
type: Object,
required: true
}
},
data() {
return {
inputValue: this.element.value
};
},
render() {
return this.$scopedSlots.default({
inputName: this.inputName,
inputValue: this.inputValue
});
}
Using it like so:
<block-element :element="element" v-slot="{ inputName, inputValue }">
<div>
<input type="text" :name="inputName" v-model="inputValue">
<p>inputValue: {{ inputValue }}</p>
</div>
</block-element>
... so the value is not updated on change. What am I doing wrong?
In the following part of the template
<input type="text" :name="inputName" v-model="inputValue">
inputValue is the variable obtained from the v-slot and not the inputValue computed property on the <block-element> component; so if you assign to it (which is what v-model does) it won't be calling the setter, it's just setting the value of a local variable in the template code.
You could "fix" it like this:
<block-element :element="element" v-slot="{ inputName }" ref="block">
<div>
<input type="text" :name="inputName" v-model="$refs.block.inputValue">
<p>inputValue: {{ $refs.block.inputValue }}</p>
</div>
</block-element>
but this is just messy and breaks the abstraction you tried to create.
Another way is to have a inputValue setter property on the scope object that will correctly delegate the update to the component:
render() {
const self = this;
return this.$scopedSlots.default({
inputName: this.inputName,
get inputValue() { return self.inputValue },
set inputValue(value) { self.inputValue = value; },
});
}
<block-element :element="element" v-slot="scope">
<div>
<input type="text" :name="scope.inputName" v-model="scope.inputValue">
<p>inputValue: {{ scope.inputValue }}</p>
</div>
</block-element>
but this isn't ideal either because the scope object isn't typically writable, and this particular implementation detail would need to be documented.
In a situation like this where you want a scoped slot to pass data back to the parent component, you would implement this by passing a callback function to the slot. You can provide a function for setting inputValue but then you can't use v-model:
render() {
return this.$scopedSlots.default({
inputName: this.inputName,
inputValue: this.inputValue,
setInputValue: value => this.inputValue = value,
});
}
<block-element :element="element" v-slot="{ inputName, inputValue, setInputValue }">
<div>
<input type="text" :name="inputName" :value="inputValue" #input="setInputValue($event.target.value)">
<p>inputValue: {{ inputValue }}</p>
</div>
</block-element>
Now there's no confusion about what to do.