How to access a method from vuejs component? - javascript

I am building a Tic Tac Too game with vue.js framework. I have declared a vue component called grid-item, when this item is clicked I want it to call the handleClick method.
when I run the code bellow it logs to the console that the handleClick method is not defined.
How to fix the problem and get access to this method from the component ?
// vue components
Vue.component("grid-item", {
template: "#grid-item",
data: function() {
return {
sign: "X",
owner: ""
}
}
})
// vue instance
new Vue({
el: "#app",
data: {
matriceSize: 3,
},
methods: {
handleClick: function() {
alert("checked");
}
}
})
* {
box-sizing: border-box;
}
#game-box {
width: 150px;
display: block;
margin: 0px auto;
padding: 0px;
background: green;
}
.grid-item {
display: inline-block;
width: 33.333%;
height: 50px;
background: yellow;
margin: 0px;
text-align: center;
line-height: 50px;
border: 1px solid
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div id="game-box">
<grid-item v-for="n in 9"></grid-item>
</div>
</div>
<template id="grid-item">
<div class="grid-item" #click="handleClick"></div>
</template>

You are getting this error as you have defined handleClick method in component : app but you are using this in the template of grid-item, where it is not defined.
Scope of vue methods is limited to the instance they have been defined.
// vue components
Vue.component("grid-item", {
template: "#grid-item",
data: function() {
return {
sign: "X",
owner: ""
}
},
methods: {
handleClick: function() {
alert("checked");
}
}
})
// vue instance
new Vue({
el: "#app",
data: {
matriceSize: 3,
}
})
* {
box-sizing: border-box;
}
#game-box {
width: 150px;
display: block;
margin: 0px auto;
padding: 0px;
background: green;
}
.grid-item {
display: inline-block;
width: 33.333%;
height: 50px;
background: yellow;
margin: 0px;
text-align: center;
line-height: 50px;
border: 1px solid
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div id="game-box">
<grid-item v-for="n in 9"></grid-item>
</div>
</div>
<template id="grid-item">
<div class="grid-item" #click="handleClick"></div>
</template>

Related

How can I fix this audit warn usage error of vue

>>>app vue code and addtask code:
<template>
<div class="container">
<Header title="Task Tracker" />
<AddTask #add-task="addTask" />
<Tasks #toggle-reminder="toggleReminder" #delete-task="deleteTask" :tasks="tasks" />
</div>
</template>
<script>
import Header from './components/Header'
import Tasks from './components/Tasks'
import AddTask from './AddTask'
export default {
name: 'App',
components: {
Header,
Tasks,
AddTask
},
data() {
return {
tasks : []
}
},
methods: {
addTask(task) {
this.tasks = [...this.tasks, task]
},
deleteTask(id) {
if(confirm('Are you sure?')) {
this.tasks = this.tasks.filter((task) =>task.id !== id)
}
},
toggleReminder(id) {
this.tasks = this.tasks.map((task) => task.id === id ? {...task,reminder: !task.reminder } : task
)
},
},
created() {
this.tasks = [
{
id: 1,
text: 'Doctors Appointement',
day: 'March 1st at 2:30pm',
reminder: true,
},
{
id: 2,
text:'Meeting at School',
day: 'March 3rd at 1:30pm',
reminder: true,
},
{
id: 3,
text: 'Food Shopping',
day: 'March 3rd at 11:00am',
reminder: false,
},
]
}
}
</script>
<style>
#import url('https://fonts.googleapis.com/css2?family=Poppins:wght#300;400&display=swap');
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Poppins', sans-serif;
}
.container {
max-width: 500px;
margin: 30px auto;
overflow: auto;
min-height: 300px;
border: 10px solid steelblue;
padding: 30px;
border-radius: 5px;
}
.btn {
display: inline-block;
background: #000;
color: #fff;
border: none;
padding: 10px 20px;
margin: 5px;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
font-size: 15px;
font-family: inherit;
}
.btn:focus {
outline: none;
}
.btn:active {
transform: scale(0.98);
}
.btn-block {
display: block;
width: 100%;
}
</style>
header and tasks code:
<template>
<header>
<h1>{{title}}</h1>
<Button #toggle-add-task="$emit('toggle-add-task')" text="Add Task" color="green" />
</header>
</template>
<script>
import Button from './Button'
export default {
name:'Header',
props: {
title: String,
showAddTask: Boolean
},
components :{
Button
},
}
</script>
<style scoped>
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
</style>
<template>
<div :key="task.id" v-for="task in tasks">
<Task #toggle-reminder="$emit('toggle-reminder',task.id)" #delete-task="$emit('delete-task', task.id)" :task='task' />
</div>
</template>
<script>
import Task from './Task'
export default {
name: 'Tasks',
props: {
tasks: Array
},
components: {
Task,
},
emits: ['delete-task','toggle-reminder'],
}
</script>
Hello I got a problem with a task adder when I try and create a new task, and I click save task it doesn't work and I get an audit to warn. The new task that I add should save and appear but instead, I get a warning and it doesn't work.
Can you help me and tell me where is the problem, I checked the code and did it after a tutorial it should work but instead I just get the warning.
This is the warning that I get when I click save task: Audit usage of navigator.userAgent, navigator.appVersion, and navigator. platform
Any way to solve this?

How do I make an element scroll to the center of the scroll bar upon entering the page

I am making an Angular project which has a "timeline" component, which has a overflow-x: scroll element:
.container {
width: 30vw;
height: 100vh;
background-color: aquamarine;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
}
.timeline-box {
width: 90%;
position: relative;
}
.timeline-strip {
background-color: yellow;
height: 200px;
display: flex;
overflow-x: scroll;
}
.timeline-box:before {
content: '';
position: absolute;
top: 50%;
border-top: 2px solid black;
width: 100%;
height: 0px;
}
.point {
margin: 0 40px;
align-self:center;
}
.point:before {
content: '';
position: relative;
left: 50%;
border-left: 2px solid black;
top: 20px;
}
<div class="container">
<div class="timeline-box">
<div class="timeline-strip">
<div class="point">1</div>
<div class="point">2</div>
<div class="point">3</div>
<div class="point">4</div>
<div class="point">5</div>
<div class="point">6</div>
<div class="point">7</div>
<div class="point">8</div>
<div class="point">9</div>
<div class="point">10</div>
</div>
</div>
</div>
I want the scroll bar of the .timeline-strip element (not the scrollbar of the page itself) to automatically be in the middle when the user enters the page. How can I achieve this? (I am Using Angular if that helps)
You can do this by by taking the scrollWidth property minus the clientWidth property and divide the result of this by 2. This will be the scroll position halfway so you can set this value to the scrollLeft property.
If your div contains images then you want to do this on the window load event. On the document DOMContentLoaded event the images are not loaded yet (unless cashed) and therefore the size of your div may change when the images will be loaded.
//Can be document.addEventListener('DOMContentLoaded', () => {}) when you don't care about images and stuff
window.addEventListener('load', () => {
let scrollElement = document.querySelector('.timeline-strip');
scrollElement.scrollLeft = (scrollElement.scrollWidth - scrollElement.clientWidth ) / 2;
});
.container {
width: 30vw;
height: 100vh;
background-color: aquamarine;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
}
.timeline-box {
width: 90%;
position: relative;
}
.timeline-strip {
background-color: yellow;
height: 200px;
display: flex;
overflow-x: scroll;
}
.timeline-box:before {
content: '';
position: absolute;
top: 50%;
border-top: 2px solid black;
width: 100%;
height: 0px;
}
.point {
margin: 0 40px;
align-self:center;
}
.point:before {
content: '';
position: relative;
left: 50%;
border-left: 2px solid black;
top: 20px;
}
<div class="container">
<div class="timeline-box">
<div class="timeline-strip">
<div class="point">1</div>
<div class="point">2</div>
<div class="point">3</div>
<div class="point">4</div>
<div class="point">5</div>
<div class="point">6</div>
<div class="point">7</div>
<div class="point">8</div>
<div class="point">9</div>
<div class="point">10</div>
</div>
</div>
</div>
personally I do not like to use addEventListener, so #ViewChild is better. This depends which angular is used
modify the answer from #ng-hobby:
add a id to the timeline strip div:
<div class="timeline-strip" #timelineStrip >
modify the viewChild and rename if you want:
#ViewChild('timelineStrip', {static: false}) timelineStripRef: ElementRef;
you can use scrollTo on the timelineStripRef element or timeline-strip:
const width = this.timelineStripRef.nativeElement.clientWidth;
this.timelineStripRef.nativeElement.scrollTo(width/2, 0);
I created a sample for you here on Stackblitz
So, Try something like this using #viewChild & ngAfterViewInit with window.scrollTo():
app.component.html
<hello name="{{ name }}"></hello>
<p class="img" #getHeight>
Start editing to see some magic happen :)
</p>
app.component.ts
import { Component, VERSION, ElementRef, AfterViewInit, ViewChild } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
name = 'Angular ' + VERSION.major;
#ViewChild('getHeight', {static: false}) getHeight: ElementRef;
constructor(private el: ElementRef){}
ngAfterViewInit() {
let pageheight = this.getHeight.nativeElement.offsetHeight;
console.log(pageheight/2)
window.scrollTo(0, pageheight/2);
}
}

ReactJS: Changing button hover property depending on the text inside the button

I am working on a React application and I am using Redux to store the state. I have the following code:
request.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loading from '../loading/loading.component';
import { changeRequestStatus } from '../../redux/requests/requests.actions';
import { RESOLVED, AWAITING_WAIT_STAFF } from '../../redux/requests/requests.status-types'
import './request.styles.scss';
class Request extends Component {
state = { isLoading: false }
render() {
const { _id, table_no, timestamp, description, status } = this.props.request;
const { user, changeRequestStatus } = this.props;
return (
<>
{this.state.isLoading ? <Loading /> : null}
<div className="request-box">
<div className="request-details">
<div>
<h1 style={{ color: status === AWAITING_WAIT_STAFF ? "#28bfa6" : "#f5a953" }}>Table {table_no}, {new Date(timestamp).toLocaleString()}</h1>
<h2>{description}</h2>
</div>
<div className="status-button">
<button
className="request-button"
onClick={async () => {
this.setState({ isLoading: true })
await changeRequestStatus(_id, status === AWAITING_WAIT_STAFF ? user.username : RESOLVED)
this.setState({ isLoading: false })
}} style={{ background: status === AWAITING_WAIT_STAFF ? "linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%)" : "linear-gradient(to right, rgba(255,213,94,1) 0%, rgba(246,170,123,1) 100%)" }}>
{status}
</button>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = (state) => {
return {
requests: state.requests.requests,
user: state.user.currentUser
}
}
export default connect(mapStateToProps, { changeRequestStatus })(Request);
request.styles.scss:
.request-box {
border: 1px solid #c3c9c8;
height: 200px;
max-width: 100%;
border-radius: 5px;
position: relative;
background-color: white;
font-family: Helvetica;
box-shadow: 0 10px 6px -6px #ededed;
margin: 10px;
}
.request-details {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0 30px;
height: 100%;
h1 {
font-size: 30px;
color: #28bfa6;
text-align: left;
}
h2 {
font-size: 22px;
text-align: left;
}
}
.status-button {
padding-bottom: 25px;
width: 100%;
#media (min-width: 1000px) {
width: auto;
padding-right: 20px;
padding-left: 100px;
}
}
.request-button {
height: 50px;
font-size: 19px;
font-weight: 600;
border: none;
border-radius: 5px;
padding: 10px 25px;
background-size: 150% auto;
background: linear-gradient(to right, rgba(141,227,227,1) 0%, rgba(114,240,218,1) 100%);
cursor: pointer;
&:hover {
background: #2de1c2;
}
}
In my Request component, I am changing the background property of my request-button div depending on the value of the status variable.
However, I would like to change the request-buttton:hover property depending on the value of status variable in my Request component.
I am not sure what the correct syntax would be to achieve this. Any insights are appreciated.
Create different CSS classes for each button color/status. Then use a ternary operator to apply the CSS class to the button when status changes. Check the status via Redux and then apply a CSS className like this.

Element UI Vue js is slow in render

I have over two hundred record looping through v-for when we change any value it takes nearly 2 sec to rerender the changed value below is the sample code. when I check the value it takes nearly 2 sec to the checkboxes. Is there any way to improve the rendering the speed this is really slow for 200 records. Thanks.
sandbox link https://codesandbox.io/s/focused-mahavira-9cc9s
<template>
<div class="selection">
<div class="check-group">
<el-checkbox
#change="handleCheckAllChange"
:indeterminate="isIndeterminate"
v-model="checkAll"
></el-checkbox>
</div>
<div v-for="row in selections" :key="row.id" class="check">
<el-checkbox v-model="row.checked" :disabled="row.disabled" :value="row.id" :key="row.id"></el-checkbox>
</div>
</div>
</template>
<script>
export default {
name: 'MultiCheckBox',
created() {
this.selections = this.rows
this.totalEnabledRows = this.rows.filter(v => !v.disabled).length
},
props: {
rows: {
type: Array,
required: true
}
},
data() {
return {
checkAll: false,
isIndeterminate: false,
selectedRows: [],
selections: [],
totalEnabledRows: 0
}
},
watch: {
rows(val) {
this.selections = val
this.totalEnabledRows = val.filter(v => !v.disabled).length
},
selections: {
handler(val) {
let selected = val.filter(v => v.checked).map(v => v.id)
this.isIndeterminate = this.totalEnabledRows > 0 && selected.length > 0 && this.totalEnabledRows > selected.length
this.checkAll = this.totalEnabledRows === selected.length
this.$emit('checked', selected)
},
deep: true
}
},
methods: {
handleCheckAllChange() {
this.selections.forEach(s => {
!s.disabled && (s.checked = this.checkAll)
})
}
}
}
</script>
<style lang="scss" scoped>
.selection {
position: sticky;
left: 0;
z-index: 103;
background: white;
}
.check-group {
height: 40px;
padding: 10px 10px 10px 25px;
width: 50px;
border-top: 1px solid #dde2eb;
border-bottom: 1px solid #dde2eb;
flex-wrap: nowrap;
background: white;
position: sticky;
top: 50px;
left: 0;
z-index: 103;
}
.check {
height: 48px;
padding: 10px 10px 10px 25px;
width: 50px;
}
</style>

Vue.js custom select component with v-model

I want to create a custom select component in Vue.js. Since I need specific options styling, I need to create 'select' made of div's etc that looks and acts like a real html select.
Currently I have something like this:
Vue.component('child', {
template: `<div class="component-container" #click="showOptions = !showOptions">
<div class="component__select">
<span class="component__select--name">Select Fruit</span>
<span class="c-arrow-down" v-if="!showOptions"></span>
<span class="c-arrow-up" v-if="showOptions"></span>
</div>
<ul class="component__select-options" v-if="showOptions" >
<li class="select--option" v-for="option in options">
<label> <input type="checkbox" :value="option"/> {{option.name}}</label>
</li>
</ul>
</div>`,
methods: {
selectOption(option) {
this.$emit('option', option)
}
},
data: () => ({
showOptions: false,
}),
props: ['options']
});
var vm = new Vue({
el: '#app',
data: () => ({
options: [
{id: 0, name: 'Apple'},
{id: 1, name: 'Banana'},
{id: 2, name: 'Orange'},
{id: 2, name: 'Strawberry'},
],
selectedFruit: ''
}),
})
.component__select {
height: 38px;
background-color: #F5F7FA;
border: 1px solid #dddddd;
line-height: 38px;
display: grid;
max-width: 500px;
grid-template-columns: 10fr 1fr;
}
.component__select--name {
font-size: 0.8rem;
padding: 0 0 0 25px;
cursor: pointer;
}
.c-arrow-down {
justify-self: end;
}
.component__select-options {
max-height: 180px;
border: 1px solid #dddddd;
border-top: none;
overflow: auto;
position: absolute;
z-index: 1500;
max-width: 500px;
width: 500px;
margin: 0;
padding: 0;
}
.select--option {
height: 35px;
display: grid;
align-content: center;
padding: 0 0 0 25px;
background-color: #f5f5fa;
border-bottom: 1px solid #dddddd;
}
.select--option:last-child {
border-bottom: none;
}
.select--option:nth-child(2n) {
background-color: #ffffff;
}
.select--option input{
display: none;
}
.single-option {
height: 55px;
background-color: #2595ec;
font-size: 0.8rem;
border: 1px solid red;
}
.cust-sel {
width: 200px;
height: 38px;
background-color: #f5f5fa;
border: 1px solid #dddddd;
}
.cust-sel:focus {
outline-width: 0;
}
<html>
<head>
<title>An example</title>
</head>
<body>
<div id="app">
<span> This is parent component</span>
<p>I want to have data from select here: "{{selectedFruit}}"</p>
<child :options="options" v-model="selectedFruit"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>
But my problem is now how to return data from child to parent component using v-model on child component.
(I know I could emit data from child component and do:
<custom-select :options="someOptions" #selected="setSelectedOption"/>
but I need it to be reusable and writing more and more methods to retrieve data from every select in parent component is not exactly how it should work I think.)
Also I need to have an entire object returned, not only ID. (that's why i've got :value="option")
Any ideas?
As Vue Guide said:
v-model is essentially syntax sugar for updating data on user input
events, plus special care for some edge cases.
The syntax sugar will be like:
the directive=v-model will bind value, then listen input event to make change like v-bind:value="val" v-on:input="val = $event.target.value"
So for your use case, you need to create one prop=value, then emit the selected option with event=input.
Like below demo (bind/emit the whole option object):
Vue.config.productionTip = false
Vue.component('child', {
template: `<div class="component-container" #click="showOptions = !showOptions">
<div class="component__select">
<span class="component__select--name">{{value ? value.name : 'Select Fruit'}}</span>
<span class="c-arrow-down" v-if="!showOptions"></span>
<span class="c-arrow-up" v-if="showOptions"></span>
</div>
<ul class="component__select-options" v-if="showOptions" >
<li class="select--option" v-for="option in options" #click="selectOption(option)">
<label> <input type="checkbox" :value="option"/> {{option.name}}</label>
</li>
</ul>
</div>`,
methods: {
selectOption(option) {
this.$emit('input', option)
}
},
data: () => ({
showOptions: false
}),
props: ['options', 'value']
});
var vm = new Vue({
el: '#app',
data: () => ({
options: [
{id: 0, name: 'Apple'},
{id: 1, name: 'Banana'},
{id: 2, name: 'Orange'},
{id: 2, name: 'Strawberry'},
],
selectedFruit: ''
}),
})
.component__select {
height: 38px;
background-color: #F5F7FA;
border: 1px solid #dddddd;
line-height: 38px;
display: grid;
max-width: 500px;
grid-template-columns: 10fr 1fr;
}
.component__select--name {
font-size: 0.8rem;
padding: 0 0 0 25px;
cursor: pointer;
}
.c-arrow-down {
justify-self: end;
}
.component__select-options {
max-height: 180px;
border: 1px solid #dddddd;
border-top: none;
overflow: auto;
position: absolute;
z-index: 1500;
max-width: 500px;
width: 500px;
margin: 0;
padding: 0;
}
.select--option {
height: 35px;
display: grid;
align-content: center;
padding: 0 0 0 25px;
background-color: #f5f5fa;
border-bottom: 1px solid #dddddd;
}
.select--option:last-child {
border-bottom: none;
}
.select--option:nth-child(2n) {
background-color: #ffffff;
}
.select--option input{
display: none;
}
.single-option {
height: 55px;
background-color: #2595ec;
font-size: 0.8rem;
border: 1px solid red;
}
.cust-sel {
width: 200px;
height: 38px;
background-color: #f5f5fa;
border: 1px solid #dddddd;
}
.cust-sel:focus {
outline-width: 0;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<span> This is parent component</span>
<p>I want to have data from select here: "{{selectedFruit}}"</p>
<child :options="options" v-model="selectedFruit"></child>
</div>
When using v-model on custom component all you need is to declare a prop named 'value' and when you need the component to chance it emit an 'input' event.
Something like this:
<template>
<form #submit.prevent="$emit('onSearch',val)" class="form-perfil">
<div class="form-group col-md-12">
<input v-model="val" #input="$emit('input',val)"
placeholder="filtrar resultados" class="form-control">
</div>
</form>
</template>
<script>
module.exports = {
name: "CaixaFiltro",
props: ["value"],
data: _ => ({ val: "" }),
created() {
this.val = this.value
}
}
</script>
Then you can use (after register the component) it like this:
<caixa-filtro v-model="textoBusca" #onSearch="listar"></caixa-filtro>
You can find more detailed info there:

Categories

Resources