Super small Vue button - vue.js

I'm learning Vue, and even with the simplest examples there is something wrong. For example, buttons. I have a defined component, myButton, responds to clicks, but it doesn't look like it should, is super small and dont have any label. What am I doing wrong?
Part of index.js:
Vue.component('mybutton', {
props: {
buttonLabel: String,
},
template: '<button #click="onClick()" class="btn">{{ buttonLabel }}</button>',
methods: {
onClick(){
console.log('Click');
}
},
})
Part of index.html:
<div id="app">
<mybutton text="From Vue"></mybutton>
<button class="btn">Test</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="index.js"></script>
And CSS:
.btn {
display: inline-block;
background: #000;
color: #fff;
border: none;
padding: 10px,20px;
border-radius: 5px;
text-decoration: none;
font-size: 15px;
font-family: inherit;
}

Your prop is called buttonLabel, while you pass a property called text inside your index.html. Therefore, the button doesn't get any text and then it's rendered without any inner content (and therefore slim, since you didn't give it fixed width and height).
You need to change the part of index.html and replace text with button-label (Vue automatically maps buttonLabel to it, and it is the better option. Using buttonLabel might not work in this case, since you are not using single file components.

Call it like
<mybutton mylabel="hI"></mybutton>
Vue.component('mybutton', {
props: ['mylabel'],
template: '<button>{{ mylabel }}</button>'
})
https://codepen.io/flakerimi/pen/wvgGqVb
https://v2.vuejs.org/v2/guide/components.html

Related

In Vue SFC link click is not triggered first time

I am using Vue 3 to show a set of links for which I am assigning event handlers dynamically(based on link id).
The issues is: The first time when any link is clicked, the corresponding event is not triggered. But subsequently clicks are perfectly working.
The updated code is below:
<script setup>
const makeSizer = ([...sizes]) => {
sizes.map((size) =>{
console.log('size-' + size);
document.getElementById('size-' + size).style.display = "";
document.getElementById('size-' + size).onclick = ((e) =>{
e.preventDefault();
document.body.style.fontSize = e.target.text + 'px';
e.target.style.display = "none";
});
});
};
function zoomIt(){
return {
zoom: makeSizer([12,14,16,18])
}
}
</script>
<template>
<div class="greeting"> {{zoom}}
<p>Some paragraph text</p>
<h1>some heading 1 text</h1>
<h2>some heading 2 text</h2>
<div class="link">
12
</div>
<div class="link">
14
</div>
<div class="link">
16
</div>
<div class="link">
18
</div>
</div>
</template>
<style>
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
.link{
padding:5px; display:inline-table;
}
.greeting {
color: red;
font-weight: bold;
}
.greeting a{
border:2px solid blue;
padding:3px;
color:white;
background-color:blue;
}
#size-12{ font-size:12px;}
#size-14{ font-size:14px;}
#size-16{ font-size:16px;}
#size-18{ font-size:18px;}
</style>
The bad news is, the way you approach it is an anti-pattern in Vue. The good news is, with some small changes you will end up with code that is much more simple to read and maintain!
You are doubling your event listeners by calling onclick() inside makeSizer() and defining click events via #click.
However, let us not just fix the bug by altering the existing code. What we want to do is to get rid of the anti-patern. So instead, we try passing the desired value of 'zoom' to the handler directly and avoid the beforementioned duplications altogether.
// Script
// We define a function that adjusts zoom value using only the value that is being passed to it as an argument
setZoom(size) {*code*}
// Template
<button #click.prevent="setZoomTo(12)">
This is a general idea. I modified your code a bit more to make it more maintainable and added comments where changes were made. I hope this helps.
Script
<script setup>
import { ref } from "vue";
const currentZoom = ref(12); // Let us set default zoom to 12
const zoomOptions = [12, 14, 16, 18]; // We define zoom options as an array to dynamically generate buttons
function setZoomTo(size) {
currentZoom.value = size; // Set current zoom value
document.body.style.fontSize = currentZoom.value + "px"; // Adjust fontSize on body
}
</script>
Template
<div class="links">
<button // We use button tag for semantic correctness
v-for="zoom in zoomOptions" // For every value in zoomOptions a button is created
:key="zoom"
:disabled="zoom === currentZoom" // If zoom value represented by the button is also currentZoom value => add disabled attribute to the button
#click.prevent="setZoomTo(zoom)" // Adjust currentZoom value according to the zoom value represented by the button
>
{{ zoom }} // Button's zoom value
</button>
</div>
Style
.links {
display: flex;
gap: 16px;
}
.links button {
border: 2px solid blue;
padding: 3px;
color: white;
background-color: blue;
cursor: pointer;
}
.links button:disabled {
opacity: 0.7; // For better UX we change button's opacity instead of hiding it
}

Can I create iframes tab menu using Nuxt?

I'm going to make Nuxt function like following sample code.
The work is being carried out without a good understanding of Nuxt.
It is not even using the tag <nuxt/>. Because it has to be made with iframe.
The reason why we want to use iframe is that we want existing information to remain even if the new content generated is tabbed.
The way I want to work doesn't seem to fit Nuxt's characteristics, but... I can't think of any other way.
The question I want is as follows.
Create tabMenu using Nuxt, and each tab content must maintain existing data even if the tab moves.
Is this possible with Nuxt?
// it just sample code, Not my question
$(function(){
function setPage(name,src){
const tabs = `<button role="button">${name}</button>`
const iframes = `<div>here is ifame area of ${name} page</div>`
$('.page-tab').append(tabs)
$('.page-frame').append(iframes)
}
function setView(number){
$('.page-frame > div').eq(number).removeClass('hide').siblings().addClass('hide')
}
$('.tab-content > li').click(function(){
const $this = $(this);
const index = $this.index()
const name = $this.text()
const src = $this.data('src')
setPage(name+index,src)
setView(index)
$('.page-tab button').click(function(){
setView($(this).index())
})
});
})
html,body,#sample {
height: 100%;
}
#sample {
display :flex;
}
aside {
flex: 0 0 auto;
height: 100%;
background-color:#eee;
}
.tab-content li {
text-align: center;
cursor: pointer;
padding: 10px 8px;
border-bottom: 1px solid #ddd;
}
main {
flex: 1 0 auto;
display: flex;
flex-direction:column;
}
.hide {
display: none !important;
}
<link href="https://cdn.jsdelivr.net/npm/reset-css#5.0.1/reset.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="sample">
<aside>
<ul class="tab-content">
<li role="button" data-src="https://www.reddit.com/">Apple</li>
<li role="button" data-src="https://finance.yahoo.com/quote/TSLA/">Orange</li>
<li role="button" data-src="https://github.com/dogecoin/dogecoin">Water</li>
</ul>
</aside>
<main>
<div class="page-tab"></div>
<div class="page-frame"></div>
</main>
</div>
You should really not try to do this in jQuery but in pure VueJS (or Nuxt, it's the same). Mixing declarative and imperative code is not a good idea.
For a tab functionality, you can use dynamic components to keep up the state while still toggling tabs.
I'm not sure if you're using SFC components or not, but here is a JSfiddle that may show you how to make tabs in VueJS: https://jsfiddle.net/chrisvfritz/Lp20op9o/

Append child to $slot.default

I have a component that I need display some custom modal on screen. I don't know where I should put this dialog content, so I did something like that:
<template>
<div class="ComponentItself">
<div v-show="false" ref="ModalContent">
Hello!
</div>
<button v-on:click="showModal">Show modal</button>
</div>
</template>
[...]
Note: I could not set the tag name of [ref=ModalContent] to template because the vue reserves this tag to another feature.
My idea is when I click on "show modal" it open creates an instance of another component (v-dialog) that I have created with the [ref=ModalContent] content (it should be compiled to support nested vue components).
import Dialog from './Dialog';
const DialogCtor = Vue.extend(Dialog);
const dialog = new DialogCtor({ propsData: {...} });
dialog['$slots'].default = [ this.$refs['templateNewFolder'].innerHTML ];
{something like document.body.appendChild(dialog.$el)}
This another component have a slot that could receives the HTML content to be displayed inside of that. And it just not works. The modal is displayed, but the slot content is undefined or the HTML content not parsed.
<div class="Dialog">
[...]
<slot></slot>
[...]
</div>
The current result is something like:
What I need:
I need to know if I am on the right way. I have about the component feature, but I could not identify or understand if it is/could resolve my problem;
What I could do to make it work;
Some similar project could help it, but I could not found anyone;
Maybe I could resolve my problem if is possible I just .appendChild() directly to $slot.default, but it is not possible;
It seems to me this might be a case of an XY problem.
What probably happens is that you do not need to manually fill $slot.default, but use your Dialog component a more standard way. Since there is little detail about the latter in your question, that component might also need some refactoring to fit this "standard way".
So a more standard approach would be to directly use your <custom-dialog> component in the template of your parent, instead of using a placeholder (the one you reference as ModalContent) that you have to hide. That way, whatever HTML you pass within that <custom-dialog> will be fed into your Dialog's <slot> (designed beaviour of slot).
That way you also save the hassle of having to manually instantiate your Dialog component.
Then you can toggle your <custom-dialog> visibility (with v-if or v-show) or even manipulate its position in the DOM as you mention in your code; you can access its DOM node as $el: this.$refs.ModalContent.$el when ModalContent is a Vue instance.
You could also factorize the showModal method by delegating it to the Dialog component.
Code example:
Vue.component('modal-dialog', {
template: '#modal-dialog',
data() {
return {
modalShown: false,
};
},
methods: {
showModal() {
this.modalShown = true;
},
hideModal() {
this.modalShown = false;
},
},
});
new Vue({
el: '#app',
methods: {
showModal() {
this.$refs.ModalContent.showModal();
},
},
});
/*
https://sabe.io/tutorials/how-to-create-modal-popup-box
MIT License https://sabe.io/terms#Licensing
*/
.modal {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
visibility: hidden;
transform: scale(1.1);
transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s;
}
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 1rem 1.5rem;
width: 24rem;
border-radius: 0.5rem;
}
.close-button {
float: right;
width: 1.5rem;
line-height: 1.5rem;
text-align: center;
cursor: pointer;
border-radius: 0.25rem;
background-color: lightgray;
}
.close-button:hover {
background-color: darkgray;
}
.show-modal {
opacity: 1;
visibility: visible;
transform: scale(1.0);
transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<modal-dialog ref="ModalContent">
Hello!
</modal-dialog>
<h1>Hello World</h1>
<button v-on:click="showModal">Show modal</button>
</div>
<template id="modal-dialog">
<div class="modal" :class="{'show-modal': modalShown}" #click="hideModal">
<div class="modal-content">
<span class="close-button" ref="closeButton" #click="hideModal">×</span>
<slot></slot>
</div>
</div>
</template>
Now if you really want to fiddle with $slot, #Sphinx's linked answer in the question comments is an acceptable approach. Note that the accepted answer there also favours the standard usage. It seems to me this is also what #Sphinx implies in their 2nd comment.

Why should I use v-bind for style

I just started learning Vue and I was wondering, why should I use v-bind for style and not write it regularly in html/css file
Let's say you need to create a progress bar that is not static. You will then need to update the style attribute width for-example.
To accomplish this, we need to programatically edit the width of the element. We 'cannot' to this in plain css, therefore the :style attribute comes in handy.
Let's create an example:
Codepen
HTML
<div id="vue">
<div class="progress-bar">
<div :style="{'width':progress + '%'}" class="progress" />
</div>
<button #click="fakeProgress">Init fake progress</button>
</div>
Css;
.progress-bar, .progress {
border-radius: 20px;
height: 20px;
}
.progress-bar {
width: 250px;
background-color: gray;
}
.progress {
background-color: blue;
width: 0;
transition: all 1s ease;
}
Javascript
new Vue({
el: '#vue',
data: {
progress: 0
},
methods: {
fakeProgress() {
let progress = setInterval(() => {
if(this.progress == 100) {
clearInterval(progress)
} else {
this.progress += 1;
}
}, 50)
}
}
})
As you see here, we bind the progress data attribute to the width value on the fake progress bar. This is just a simple example, but I hope this makes you see its potential. (You could achieve this same effect using the <progress> tag, but that would ruin the explanation.
EDIT; Also want to point out that you are supposed to write all your css as normal as you point out in your question. However, :style is used in cases that you cannot normally use css for. Like the example above where we need css to change from a variable.

Vue.js Modifiers Chain

I am new in Vue.js. I am reading docs, and can't understand one moment...
https://v2.vuejs.org/v2/guide/events.html
Order matters when using modifiers because the relevant code is
generated in the same order. Therefore using #click.prevent.self will
prevent all clicks while #click.self.prevent will only prevent clicks
on the element itself.
I can't understand what's mean'Therefore using #click.prevent.self will prevent all clicks while #click.self.prevent will only prevent clicks on the element itself.' Can anybody give an example with prevent default actions and show difference...
for example with links (#click.prevent.self="fn" or #click.self.prevent="fn" difference)
I know about event phases (capture, target and bubbling).
For example it can be useful in Bubbling phase:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
.root {
width: 300px;
height: 300px;
background: green;
text-align: center;
color: white;
}
.parent {
width: 200px;
height: 200px;
background: red;
text-align: center;
color: white;
margin: 50px
}
.child {
width: 100px;
height: 100px;
background: blue;
margin: 50px;
text-align: center;
color: white;
}
<div id="app">
<div class="root" v-on:click="log('root')">root
<div class="parent" v-on:click.self.stop="log('parent')">Parent
<div class="child" v-on:click="log('child')">Child
</div>
</div>
</div>
</div>
<script type="text/javasctipt">
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
log(message) {
alert(message);
console.log(message)
}
}
})
</script>
If i click on child i'll get output:
child
root
Bubblig doesn't stopped! But if i click on parent bubbling stopped! It's helpful!
But can anybody show me examples and difference if i'll use prevent and self modifiers (with links, checkboxes or may be with another elements) ?
Thanks for advance!
You can check this example:
https://jsfiddle.net/50wL7mdz/39994/
If you use #click.prevent.self, you can't click on anything. It prevents all click.
If you use #click.self.prevent, it only prevents when you click on <a> element, we can still click on choose file to upload file
Example illustrates difference in chain order
<div id="app">
<a href="https://stackoverflow.com" #click.self.prevent target="_blank">Stackofervlow!
<span :style="{ background: 'yellow', width: '100px', height: '100px' }">span</span>
</a>
</div>
new Vue({
el: '#app'
});
#click.self.prevent
If you click on span, stackoverflow will open.
If you click on a, stackoverflow will not open.
#click.prevent
If you click on span, stackoverflow will not open.
If you click on a, stackoverflow will not open.