Passing props to element attributes in Vue components - javascript

When I try passing props to attributes of HTML elements in .vue files, they just stop rendering. What am I doing wrong?
script.js
import hInput from './components/hInput.vue'
Vue.component('h-input', hInput);
const app = new Vue({
el: '#head',
});
index.php
<div id="head">
<h1>{{config('app.name')}}</h1>
<h-input placeholder="Hi" name="hello"></h-input>
</div>
hInput.vue
<template>
<div>
<input type="text" placeholder="{{placeholder}}">
</div>
</template>
<script>
export default {
props: ['placeholder', 'name']
};
</script>

Use the binding syntax, not text interpolation.
<template>
<div>
<input type="text" v-bind:placeholder="placeholder">
</div>
</template>
There is also a shorthand.
<template>
<div>
<input type="text" :placeholder="placeholder">
</div>
</template>

Related

How to hide slotted element before rendering the vue template in vueJs 3?

See this images
i dont want to display here those elements
See the Below code sample
sample.blade.php
<create-form id= "create_form" title="Sample Form" >
<div class="col-md-6">
<label class="form-label" for="multicol-username">Username</label>
<input type="text" id="multicol-username" class="form-control" placeholder="john.doe" name="ma_user_id" data-type="varchar" required>
</div>
<div class="col-md-6">
<label class="form-label" for="multicol-email">Email</label>
<div class="input-group input-group-merge">
<input type="text" id="multicol-email" class="form-control" placeholder="john.doe" aria-label="john.doe" aria-describedby="multicol-email2" name="password" data-editmode="false" data-editname="email">
<span class="input-group-text" id="multicol-email2">#example.com</span>
</div>
</div>
</create-form>
createForm.vue
<template>
<div class="card mb-4" v-show="showForm">
<h5 class="card-header">{{title}}</h5>
<form class="card-body" id="createForm" enctype="multipart/form-data" ref="createForm">
<div class="row g-3">
<slot></slot>
<div class="pt-4">
<button type="submit" class="btn btn-primary me-sm-3 me-1" id="save_return">Submit</button>
<button type="reset" class="btn btn-label-secondary" #click="hideForm">Cancel</button>
</div>
</div>
</form>
</div>
</template>
<script>
export default {
props: ['title'],
setup() {},
data() {
return {
}
},
created() {
},
mounted() {
},
methods: {
},
}
</script>
app.js
require('./bootstrap')
import { createApp } from 'vue'
import createForm from './components/createForm';
const app = createApp({})
app.component('create-form', createForm);
app.mount('#app')
It sounds like you want the template that's rendered by the php backend to not be visible until vue is able to handle it, though I'm not 100% sure that's the case.
If that is the case, you could use the v-cloak directive
This directive is only needed in no-build-step setups.
When using in-DOM templates, there can be a "flash of un-compiled templates": the user may see raw mustache tags until the mounted component replaces them with rendered content.
v-cloak will remain on the element until the associated component instance is mounted. Combined with CSS rules such as [v-cloak] { display: none }, it can be used to hide the raw templates until the component is ready.
<create-form id="create_form" title="Sample Form" v-cloak>
...
<style>
[v-cloak] { display: none }
</style>

vue.js: create component from string

I have two components named component-1 and component-2, along with this vue.js code:
<template>
<div id="app">
<input class="input" #keyup="update()">
<div class="render"></div>
</div>
</template>
<script>
export default {
methods:{
update () {
document.querySelector(".render").innerHTML=`<component-${
document.querySelector(".input").value
} />`
}
}
}
</script>
Whenever I run the code, the component doesn't render. Is there a way to get this component to render?
This isn't the right way to render components dynamically in Vue - instead you should use :is
<template>
<div id="app">
<input class="input" #keyup="update($event.target.value)">
<component :is="myComponent"></component>
</div>
</template>
<script>
export default {
data() {
return {
myComponent: undefined
}
},
methods:{
update (component) {
this.myComponent = component
}
}
}
</script>
A sandbox demo is here

What's the correct ways of two-way binding props in Vue?

Here I have two components:
msg.vue
<template>
<div>
<input type="text" v-model="msg" />
</div>
</template>
<script>
export default {
name: "msg",
props: ["msg"]
};
</script>
samples.vue
<template>
<div>
<h2>{{ msg }}</h2>
</div>
</template>
<script>
export default {
name: "samples",
props: ["msg"]
};
</script>
and finally,
App.vue
<template>
<div id="app">
<samples v-bind:msg="msg"></samples>
<msg :msg="msg"></msg>
</div>
</template>
<script>
import samples from "./components/samples.vue";
import msg from "./components/msg.vue";
export default {
name: "app",
components: {
samples,
msg
},
data: () => {
return {
msg: "Hello World"
};
}
};
</script>
<style>
#app {
font-family: "Poppins", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
margin-top: 60px;
}
</style>
The thing I want to do is with the msg component I want to change the msg component in the data of App.vue. But when I change the value, Vue warns this :
So I can't understand what to do next. Please help me in this.
1. What's v-model?
<input v-model="msg" />
basically will transpile to
<input :value="msg" #input="msg = $event.target.value" />
2. data vs props
2.1 data is supposed to be component self-contained, whereas props is passed from parents.
2.2 data should be mutable within the component, props shouldn't. Why? Because of 2.1.
3. mutating a prop locally is considered an anti-pattern in Vue 2
Result: Here's the solution that I changed only 2 lines of code from yours:
App.vue
<template>
<div id="app">
<samples v-bind:msg="msg"></samples>
<msg :msg="msg" #update:msg="msg = $event"></msg> // changed this line
</div>
</template>
msg.vue
<template>
<div>
<input type="text" :value="msg" #input="$emit('uddate:msg', $event.target.value)" /> // changed this line
</div>
</template>
You're simply not allowed to mutate props, you're only allowed to change data variables.
To fix your problem here's what you could do. Implement a v-model on the component, so the msg always stays in sync, also in the App
App.vue
<template>
<div id="app">
<samples v-bind:msg="msg"></samples>
<msg v-model="msg"></msg>
</div>
</template>
<script>
import samples from "./components/samples.vue";
import msg from "./components/msg.vue";
export default {
name: "app",
components: {
samples,
msg
},
data: () => {
return {
msg: "Hello World"
};
}
};
</script>
msg.vue
<template>
<div>
<input type="text" v-model="selected" />
</div>
</template>
<script>
export default {
name: "msg",
props: ["value"],
computed: {
selected: {
get() {
return this.value;
},
set(value) {
return this.$emit('input', value)
}
}
}
};
</script>
Do not use v-model instead use $emit
Msg Component
Vue.component('msg',{
props:["msg"],
template: `<div>
<input #keyup="changeMsg" :value="msg">
</div>`,
methods:{
changeMsg:function(e){
this.$emit("changed",e.target.value);
}
}
});
<msg #changed="msg = $event" :msg="msg"></msg>
Check Solution in CodePen
One simple solution is to use v-bind.sync. Follow the link to see the document and usage. By using this, you need to change 2 lines of code from yours to make it work:
In App.vue:
change
<msg :msg="msg"></msg>
to
<msg :msg.sync="msg"></msg>
In msg.vue:
change
<input type="text" v-model="msg" />
to
<input type="text" #input="$emit('update:msg',$event.target.value)" />
However, this one is not recommended by Vue

vue.js component is not showing?

I have created a file EmptyComponent.html to show a Vue.js component (myComponent.vue) both files are in the same folder. my component will not show and I am always getting an error in chrome developer tools console saying "Uncaught SyntaxError: Unexpected identifier EmptyComponent.html:8"
EmptyComponent.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<h1> id app div </h1>
<myComponent></myComponent>
</div>
<script>
import myComponent from 'myComponent.vue'
new Vue({
el: '#app',
components: {myComponent}
});
</script>
myComponent.vue
<template>
<div>
<h1> myComponent div</h1>
</div>
</template>
<script>
export default {
name: 'myComponent'
</script>
<style scoped>
</style>
In order to use single file components .vue you should use Vue cli 3, but your component does not contain a complex code you could use CDN and declare your component using Vue.component('my-component',{....}) :
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<h1> id app div </h1>
<my-component></my-component>
</div>
<script>
Vue.component('my-component', {
template: ` <div>
<h1> myComponent div</h1>
</div>`
})
new Vue({
el: '#app',
});
</script>

Is there a vue.js equivalent of ngTemplateOutlet?

Does vue.js have an equivalent of Angular's *ngTemplateOutlet directive? Let's say I have some components defined like this:
<template>
<div id="independentComponent">
Hello, {{firstName}}!
</div>
</template>
<script>
export default {
name: "independentComponent",
props: ['firstName']
}
</script>
...
<template>
<div id="someChildComponent">
<slot></slot>
<span>Let's get started.</span>
</div>
</template>
<script>
export default {
name: "someChildComponent"
}
</script>
I want to be able to do something like this:
<template>
<div id="parentComponent">
<template #indepdentInstance>
<independentComponent :firstName="firstName" />
</template>
<someChildComponent>
<template #indepdentInstance></template>
</someChildComponent>
</div>
</template>
<script>
export default {
name: "parentComponent",
components: {
someChildComponent,
independentComponent
},
data() {
return {
firstName: "Bob"
}
}
}
</script>
In Angular, I could accomplish this with
<div id="parentComponent">
<someChildComponent>
<ng-container *ngTemplateOutlet="independentInstance"></ng-container>
</someChildComponent>
<ng-template #independentInstance>
<independentComponent [firstName]="firstName"></independentComponent>
</ng-template>
</div>
But it looks like Vue requires the element to be written to the DOM exactly where it is in the template. Is there any way to reference an element inline and use that to pass to another component as a slot?
You cannot reuse templates like ngTemplateOutlet, but can combine idea of $refs, v-pre and runtime template compiling with v-runtime-template to achieve this.
First, create reusable template (<ng-template #independentInstance>):
<div ref="independentInstance" v-show="false">
<template v-pre> <!-- v-pre disable compiling content of template -->
<div> <!-- We need this div, because only one root element allowed in templates -->
<h2>Reusable template</h2>
<input type="text" v-model="testContext.readWriteVar">
<input type="text" v-model="readOnlyVar">
<progress-bar></progress-bar>
</div>
</template>
</div>
Now, you can reuse independentInstance template:
<v-runtime-template
:template="$refs.independentInstance.innerHTML"
v-if="$refs.independentInstance">
</v-runtime-template>
But keep in mind that you cannot modify readOnlyVar from inside independentInstancetemplate - vue will warn you with:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "readOnlyVar"
But you can wrap it in object and it will work:
#Component({
components: {
VRuntimeTemplate
}
})
export default class ObjectList extends Vue {
reusableContext = {
readWriteVar: '...'
};
readOnlyVar = '...';
}
You could try Portal vue written by LinusBorg a core Vue team member.
PortalVue is a set of two components that allow you to render a
component's template (or a part of it) anywhere in the document - even
outside the part controlled by your Vue App!
Sample code:
<template>
<div id="parentComponent">
<portal to="independentInstance">
<!-- This slot content will be rendered wherever the <portal-target>
with name 'independentInstance' is located. -->
<independent-component :first-name="firstName" />
</portal>
<some-child-component>
<portal-target name="independentInstance">
<!--
This component can be located anywhere in your App.
The slot content of the above portal component will be rendered here.
-->
</portal-target>
</some-child-component>
</div>
</template>
There is also a vue-simple-portal written by the same author that is smaller but that mounts the component to end of body element.
My answer from #NekitoSP gave me an idea for a solution. I have implemented the sample below. It worked for me. Perhaps you want to use it as a custom component with props.
keywords: #named #template #vue
<template>
<div class="container">
<div ref="templateRef" v-if="false">write here your template content and add v-if for hide in current place</div>
....some other contents goes here
<p v-html="getTemplate('templateRef')"></p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
Vue.extend({
methods:{
getTemplate(tempRef){
return this.$refs[tempRef].innerHTML
}
}
})
</script>
X-Templates
Use an x-template. Define a script tag inside the index.html file.
The x-template then can be referenced in multiple components within the template definition as #my-template.
Run the snippet for an example.
See the Vue.js doc more information about x-templates.
Vue.component('my-firstname', {
template: '#my-template',
data() {
return {
label: 'First name'
}
}
});
Vue.component('my-lastname', {
template: '#my-template',
data() {
return {
label: 'Last name'
}
}
});
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-firstname></my-firstname>
<my-lastname></my-lastname>
</div>
<script type="text/x-template" id="my-template">
<div>
<label>{{ label }}</label>
<input />
</div>
</script>
Not really sure i understand your problem here, but i'll try to give you something that i will opt to do if i want to add two components in one template.
HeaderSection.vue
<template>
<div id="header_id" :style="'background:'+my_color">
welcome to my blog
</div>
</template>
<script>
export default {
props: ['my_color']
}
</script>
BodySection.vue
<template>
<div id="body_id">
body section here
</div>
</template>
<script>
export default {
}
</script>
home.vue
<template>
<div id="parentComponent">
<header-section :color="my_color" />
<body-section />
</div>
</template>
<script>
import HeaderSection from "./components/HeaderSection.vue"
import BodySection from "./components/BodySection.vue"
export default {
name: "home",
components: {
HeaderSection,
BodySection
},
data() {
return {
my_color: "red"
}
}
}
</script>

Categories