I'm doing a Vue school course and I'm trying to do a little exercise with components and props. I'm trying to buid a simple button and paragraph component that takes the content of a paragraph as the prop's value:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Click-Counter</title>
</head>
<body>
<div id="app">
<h1>Vue.js Components Fundamentals</h1>
<click-counter click-title="The first counter is:"></click-counter>
<click-counter click-title="The second counter is:"></click-counter>
<click-counter click-title="The Third counter is:"></click-counter>
</div>
<script type="text/x-template" id="click-counter-template">
<div>
<p>{{ click-title }}</p>
<button v-on:click="count++"> {{ count }} </button>
</div>
</script>
<script src="https://unpkg.com/vue"></script>
<script src="app.js"></script>
</body>
</html>
app.js
Vue.component('click-counter', {
props: {
'click-title': {
type: String,
required: true
}
},
data () {
return {
count: 0
}
},
template: '#click-counter-template',
})
new Vue({
el: '#app'
})
The thing is that the content of the click-title prop is being evaluated but not rendered.
Screenshot of the Vue extension
Screenshot of the page's content
The problem was that, for Vue to understand that the prop in the app.js file and the index.html file are the same thing, you have to use camelCase in app.js (and when you define the component) and kebab-case in index.html (in the instances of the component).
In short, I changed this:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Some Head Info -->
</head>
<body>
<div id="app">
<h1>Vue.js Components Fundamentals</h1>
<!-- USE kebab-case HERE (click-title) -->
<click-counter click-title="The first counter is:"></click-counter>
<click-counter click-title="The second counter is:"></click-counter>
<click-counter click-title="The Third counter is:"></click-counter>
</div>
<script type="text/x-template" id="click-counter-template">
<div>
<!-- USE camelCase HERE (clickTitle) -->
<p>{{ clickTitle }}</p>
<button v-on:click="count++"> {{ count }} </button>
</div>
</script>
<!-- More stuff -->
</body>
</html>
app.js:
Vue.component('click-counter', {
props: {
// USE camelCase HERE (clickTitle)
'clickTitle': {
// Atributes
}
},
data () {
// Variables
},
// Template
})
// New Vue
Related
When I run the following code, I get the following results (Google Chrome running screenshot).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>17-Learning of Scoped Slots and Named Slots</title>
<style>
.h1Class{
color:red;
}
</style>
</head>
<body>
<div id="div_new">
<h1 class="h1Class">first set</h1>
<cpn></cpn>
<h1 class="h1Class">second set</h1>
<cpn>
<!-- first method -->
<template slot="slotName" slot-scope="planallScope">
<!-- second method after Vue2.6 -->
<!-- <template v-slot:slotName="planallScope" > -->
<h4>{{planallScope.planall[0]}}</h4>
<h4>{{planallScope.planall[1]}}</h4>
<h4>{{planallScope.planall[2]}}</h4>
</template>
</cpn>
</div>
<template id="template_div">
<div>
<slot v-bind:planall="plan" name="slotName">
<ul>
<li v-for="item in plan"> {{ item }}</li>
</ul>
</slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue#2.7.7/dist/vue.js"></script>
<script>
const templateDivText = {
template: '#template_div',
data() {
return {
plan: ['C#', 'Java', 'JavaScript']
}
},
}
const app_new = new Vue({
el: '#div_new',
components: {
'cpn': templateDivText,
},
})
</script>
</body>
</html>
The running result is as follows:
enter image description here
When I use v-slot, the code is as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>17-Learning of Scoped Slots and Named Slots</title>
<style>
.h1Class{
color:red;
}
</style>
</head>
<body>
<div id="div_new">
<h1 class="h1Class">first set</h1>
<cpn></cpn>
<h1 class="h1Class">second set</h1>
<cpn>
<!-- first method -->
<!-- <template slot="slotName" slot-scope="planallScope"> -->
<!-- second method after Vue2.6 -->
<template v-slot:slotName="planallScope" >
<h4>{{planallScope.planall[0]}}</h4>
<h4>{{planallScope.planall[1]}}</h4>
<h4>{{planallScope.planall[2]}}</h4>
</template>
</cpn>
</div>
<template id="template_div">
<div>
<slot v-bind:planall="plan" name="slotName">
<ul>
<li v-for="item in plan"> {{ item }}</li>
</ul>
</slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue#2.7.7/dist/vue.js"></script>
<script>
const templateDivText = {
template: '#template_div',
data() {
return {
plan: ['C#', 'Java', 'JavaScript']
}
},
}
const app_new = new Vue({
el: '#div_new',
components: {
'cpn': templateDivText,
},
})
</script>
</body>
</html>
The running result is as follows:
enter image description here
So in Vue2.7, v-slot is not available, how to solve the problem?
Your syntax is correct, except one little detail: you can't use camelCase for slot names.
To be fair, I don't precisely know why, it has to do with template compilation and with the fact the slot names get parsed as element attributes in <template v-slot:slot-name"scope">. Vue's styling guideline does strongly advise on using kebab-case for attributes, directives and the likes, when used in templates or JSX.
However, name="slotName" + <template #slot-name="scope"> doesn't seem to work for slots.
In short, name="slotName" (+ <template #slotName="scope") does not work, but name="slot-name" (+ <template #slot-name="scope") does.
See it working, in Vue 2.7.7:
const templateDivText = Vue.defineComponent({
template: '#template_div',
data() {
return {
plan: ['C#', 'Java', 'JavaScript']
}
},
})
const app_new = new Vue({
el: '#div_new',
components: {
'cpn': templateDivText,
},
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.7.7/dist/vue.min.js"></script>
<div id="div_new">
<cpn>
<template #slot-name="{planall}">
<h4>{{planall[0]}}</h4>
<h4>{{planall[1]}}</h4>
<h4>{{planall[2]}}</h4>
</template>
</cpn>
</div>
<template id="template_div">
<div>
<slot name="slot-name" :planall="plan">
<ul>
<li v-for="item in plan"> {{ item }}</li>
</ul>
</slot>
</div>
</template>
Notes:
:planAll="" is shorthand for v-bind:planAll=""
<template #slot-name=""> is shorthand for <template v-slot:slot-name="">
when you only have one slot, you can remove the slot name altogether (it defaults to name="default")
I'm working whit laravel and I have installed component vue. https://github.com/dalphyx/vue-top-progress
I will like to use this component into different pages and other components.
I don't know how to set global, for use other components or pages blade. laravel.
app.js
require('./bootstrap');
window.Vue = require('vue');
Vue.component('crud1', require('./components/crud1.vue').default);
Vue.component('crud2', require('./components/crud2.vue').default);
import { vueTopprogress } from 'vue-top-progress';
const app = new Vue({
el: '#app',
mounted(){
this.$refs.topProgress.start()
setTimeout(() => {
this.$refs.topProgress.done()
}, 2000)
},
components: {
vueTopprogress
}
});
home.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>home</title>
</head>
<body>
<div class="wrapper" id="app">
<vue-topprogress ref="topProgress"></vue-topprogress>
<crud1></crud1>
</div>
<script src="app.js"></script>
</body>
</html>
crud1.vue
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">crud 1 Component</div>
<div class="card-body">
I'm an example component.
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
this.$refs.topProgress.start()
setTimeout(() => {
this.$refs.topProgress.done()
}, 2000)
}
}
if I use "this.$refs.topProgress.start()" on crud1 I get this error
app.js:41686 [Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'start' of undefined"
found in
---> <crud1> at resources/js/components/crud1.vue
<Root>
I want to add an eventhandler on my component. When I click the button I want the component to show. I tried all kinds of solutions but I think my knowledge is lacking. Here is my code, I would be thankful for help. Feel free to explain what the best methods are for solving this problem and how I should think in scenarios like these. Appricitate your time.
HTML
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
<link rel="stylesheet" href="main.css">
<title>SneakPeak</title>
</head>
<body>
<div id="body-div">
<div id="app">
<modal-button></modal-button>
<modal-1 v-if="modalStatus == 2"></modal-1>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="app.js"></script>
</div>
</body>
</html>
JAVASCRIPT
// Define a new component called button-counter
Vue.component('modal-button', {
data: function () {
return {
count: 0,
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
// Defining a component called modal-1
Vue.component('modal-1', {
props: ["sModal"],
template: `
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header"
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
`
})
var shaba = new Vue({
el: '#app',
props: ["sModal"],
data: {
modalStatus: 0
},
})
// // register modal component
// Vue.component('modal', {
// template: modalTemplate
// })
// // start app
// new Vue({
// el: '#body-div',
// data: {
// showModal: false
// }
// })
Old Answer:
if you want to show/hide your component on click then use #click="val = !val" on the button component and use v-if or v-show="val" on the modal componant to enable/ disable Display.
set val: false in your data.
New Answer:
Looks Like you button is already capturing click so please follow this pen https://codepen.io/ashwinbande/pen/WPozgR
what we are doing is on button click executing a method which increments count as well as emits a custom event. In button component on that event, we change the value of val. then the val is used to show hide the modal component`!
I am trying to change my html title depending on the partial that I load for the body.
Parent html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>
{{title}}
</title>
</head>
<body>
{{>body}}
</body>
</html>
Partial body.hbs file:
<h1 id="brick-mason">Brick Mason</h1>
<h3 id="what-is-a-brick-mason-">What is a brick mason?</h3>
<p>Brick masons use various stones, concrete and bricks to build walls, walkways, fences, and other structures.</p>
What can I change so that my partial can determine what the title on my main html page displays?
you can do it easily with JavaScript,
just add in the body.hbs :
<script> document.title = 'the title here ' </script>
What I ended up doing was including a second partial in the handlebars file and then splitting them out in the grunt file config.
gruntfile.js
file_context: function(src) {
var fileName = path.basename(src).split(".")[0];
var obj = glob.route[fileName];
var fileString = fs.readFileSync("./" + src, "utf8").split("<split />");
return {
partials: {
header: fileString[0] || "",
body: fileString[1] || "",
},
src: 'src/base.hbs',
dest: path.join('static', obj, 'index.html')
};
}
base.hbs
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/index.min.css" />
{{>header}}
</head>
<body>
<div id="nav_background"></div>
<div id="main">
<div id="nav_bar">
<div class="nav left_nav">
BlueCollarJobs101
</div>
<div class="nav right_nav">
States
Jobs
Contact Us
</div>
</div>
<div class="markdown-body">
{{>body}}
</div>
</div>
</body>
</html>
In your Partial.hbs body add the script to manipulate the tags
<h1 id="brick-mason">Brick Mason</h1>
<h3 id="what-is-a-brick-mason-">What is a brick mason?</h3>
<p>Brick masons use various stones, concrete and bricks to build walls, walkways, fences, and other structures.</p>
<script>
const title = document.querySelector('title');
title.innerHTML = 'Partial';
</script>
how can I swap content of a div with Vue.js? I think I lack general lifecycle knowledge. Here is what I´ve got so far. After all I need to swap between two Vue-instances and ´d like to use only one lightbox container for them.
Here is what I´ve got so far: http://jsbin.com/qokerah/edit?html,js,output
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script type="x-template" id="foo-template">
<div class="text">{{ fooText }}</div>
<div class="confirm" v-if="fooConfirm">confirmed</div>
</script>
<script type="x-template" id="bar-template">
<div class="heading">{{ barHeading }}</div>
<div class="info">{{ barInfo }}</div>
</script>
<div class="foo">
<div class="btn btn-primary" v-on:click="swapLightboxContent">Open foo template</div>
</div>
<div class="bar">
<div class="btn btn-primary" v-on:click="swapLightboxContent">Open bar template</div>
</div>
<div class="lightbox">
<div class="lightbox-content">Show foo- or bar-template</div>
</div>
</body>
</html>
JS
var foo = new Vue({
el: '.foo',
data: {
fooText: 'text',
fooConfirm: false
},
methods: {
swapLightboxContent: function () {
console.log('Show foo-template');
}
}
});
var bar = new Vue({
el: '.bar',
data: {
barHeading: 'text',
barInfo: 'text'
},
methods: {
swapLightboxContent: function () {
console.log('Show bar-template');
}
}
});
Ok, my main misconception where:
I Used var foo and bar as new Vue() instances without having a parent "intersection" instance
so this is now working:
http://jsbin.com/tiwoco/edit?html,js,output
Still need to figure out if accessing the currentView item through vmFoo and vmBar instances shouldn´t be done in foo and bar components :)