My function js just show color border required, and if input any place has something change color.
in javascript work good but in vue no work.
I need to use this function in vue. anyplace, any component of vue
app.js
$('.req' ).on('keyup', function() {
let val = this.value
val = $.trim(val)
if(val==""){
$(this).removeClass('valid');
}else{
$(this).addClass('valid');
}
});
$('.req').change(function() {
let val = this.value
val = $.trim(val)
if(val==""){
$(this).removeClass('valid');
}else{
$(this).addClass('valid');
}
})
Anyplace.blade.php
<form>
<input type="text" class="req form-control " name="last_name">
</form>
app.css
.valid{
border-left: 2px solid #9cff2b !important;
}
.req{
border-left: 2px solid red;
}
component-example.vue
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">papa</div>
<div class="card-body">
papa. esto viene del hijo {{ messaje }}
<br>
<input type="text" v-model="modelo" class="req ">
</div>
</div>
<hijo :numero=modelo #verInfo="recibirpapa" > </hijo>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component papa.')
},
data(){
return{
modelo:"",
messaje:''
}
},methods:{
recibirpapa(valor){
this.messaje = valor;
}
},
}
</script>
You can try this code. include <req-input></req-input> anywhere you want.
Vue.component('req-input', {
template: '#req-input-template',
props: {
value: {
type: [String, Number],
default: ""
}
},
data: function() {
return {
currentValue: "",
valid: false
}
},
watch: {
value: {
handler(value) {
const string = value.trim()
if (string=="") {
this.valid = false
} else {
this.valid = true
}
this.currentValue = value
},
immediate: true
}
},
methods: {
handleInput() {
this.$emit("input", this.currentValue);
}
}
})
new Vue({
el: "#app",
data: {
modelo: ""
},
})
.valid {
border-left: 2px solid #9cff2b !important;
}
.req{
border-left: 2px solid red;
}
.form-control:focus {
outline: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<req-input v-model="modelo"></req-input>
modelo: {{ modelo }}
<script type="text/x-template" id="req-input-template">
<input
type="text"
v-model="currentValue"
#input="handleInput"
:class="['form-control req', { 'valid': valid }]" />
</script>
</div>
Related
i have created a component which works perfect apart from binding the a string rather than an array. The event's fire correctly.
So on the multi-select version if I select and deselect items the multiSelected variable is updated correctly. However when selecting another value within the single select the singleSelected variable isn't being changed, however the same event is firing.
Dummed down the code here so you can just see the logic and issue:
Vue.component('dropdown', {
template: `<div class="dropdown">
<label v-for="item in items" :key="item.value" :class="{selected:selected.indexOf(item.value) > -1}">
{{ item.label }}
<input type="checkbox" :value="item.value" :checked="selected.indexOf(item.value) > -1" #change="selected = $event" />
</label>
</div>`,
props: [ 'value', 'items', 'multiSelect' ],
computed: {
selected: {
get: function() {
if (this.value === undefined) {
return [];
}
if (!Array.isArray(this.value)) {
return [ this.value ];
}
return this.value;
},
set: function($event) {
let current = this.selected;
if (!this.multiSelect) {
current = $event.target.value;
}
if (this.multiSelect && !$event.target.checked) {
const index = current.indexOf($event.target.value);
if (index > -1) {
current.splice(index, 1)
}
}
if (this.multiSelect && $event.target.checked) {
current.push($event.target.value);
}
console.log(current);
this.$emit('value', current);
}
}
}
});
Vue.component('wrapper', {
template: `
<div>
Single
<dropdown :items="items" v-model="singleSelected" :multi-select="false" name="single" />
<br />
Multi
<dropdown :items="items" v-model="multiSelected" :multi-select="true" name="multi" />
<p>Models</p>
<p>singleSelected: {{ singleSelected }}</p>
<p>multiSelected: {{ multiSelected }}</p>
</div>
`,
data() {
return {
items: [{value:'bmw',label:'BMW',count:1},{value:'audi',label:'Audi',count:1},{value:'kia',label:'KIA',count:1}],
multiSelected: ['kia'],
singleSelected: 'kia',
}
}
});
new Vue().$mount('#app');
.dropdown {
border: 1px solid black;
padding: 10px;
display: block;
}
label {
margin: 5px;
}
.selected {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<wrapper>
</wrapper>
</div>
v-model on custom component expects that the component will emit input event, not value
Only reason why your component is (sort of) working in multi-select mode is because you are directly mutating the input array passed to you by a value prop (in other words the emitted event is useless). Don't do that. Create a new array instead before emitting
Also, there is one more issue with single-select mode - after deselecting only checked box, you still emitting the value so the model is set to a value even none of the checkboxes is checked
Personally I find your design strange for several reasons:
Using different v-model "type" (array or string) feels unnatural
In single-select mode it behaves as radio so why not use radio
What will happen if you switch multiSelect prop at runtime?
Vue.component('dropdown', {
template: `<div class="dropdown">
<label v-for="item in items" :key="item.value" :class="{selected:selected.indexOf(item.value) > -1}">
{{ item.label }}
<input type="checkbox" :value="item.value" :checked="selected.indexOf(item.value) > -1" #change="selected = $event" />
</label>
</div>`,
props: [ 'value', 'items', 'multiSelect' ],
computed: {
selected: {
get: function() {
if (this.value === undefined) {
return [];
}
if (!Array.isArray(this.value)) {
return [ this.value ];
}
return this.value;
},
set: function($event) {
if(this.multiSelect) {
if (!$event.target.checked) {
this.$emit('input', this.selected.filter(v => v !== $event.target.value))
} else {
this.$emit('input', [...this.selected, $event.target.value])
}
} else {
this.$emit('input', $event.target.checked ? $event.target.value : "")
}
}
}
}
});
Vue.component('wrapper', {
template: `
<div>
Single
<dropdown :items="items" v-model="singleSelected" :multi-select="false" name="single" />
<br />
Multi
<dropdown :items="items" v-model="multiSelected" :multi-select="true" name="multi" />
<p>Models</p>
<p>singleSelected: {{ singleSelected }}</p>
<p>multiSelected: {{ multiSelected }}</p>
</div>
`,
data() {
return {
items: [{value:'bmw',label:'BMW',count:1},{value:'audi',label:'Audi',count:1},{value:'kia',label:'KIA',count:1}],
multiSelected: ['kia'],
singleSelected: 'kia',
}
}
});
new Vue().$mount('#app');
.dropdown {
border: 1px solid black;
padding: 10px;
display: block;
}
label {
margin: 5px;
}
.selected {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<wrapper>
</wrapper>
</div>
I am a bit confused about how to pass a value defined inside a button to a child component. Basically, the value of a clicked button displaying a percentage should be the prop value. This prop value should change based on which value I click. Should I use a v-model? If so, how? Here's what I have so far...
ButtonGroup.vue
<button class="button" v-on:click="percentageValue(0.05)">5%</button>
<button class="button" v-on:click="percentageValue(.10)">10%</button>
<button class="button" v-on:click="percentageValue(.15)">15%</button>
<button class="button" v-on:click="percentageValue(.25)">25%</button>
<button class="button" v-on:click="percentageValue(.50)">50%</button>
<script>
export default {
name: 'ButtonGroup',
data(){
return{
percentage: null
}
},
methods:{
percentageValue(value){
return this.percentage = value;
}
},
props:['percentage']
}
</script>
Calculator.vue
<ButtonGroup :percentage="percentage"/>
Try like this:
Vue.component('button-group', {
template: `
<div class="">
<ul>
<li v-for="(percent, i) in percentages" :key="i">
<button class="button"
#click="setPercent(percent)"
:class="selPercent === percent && 'selected'">
{{ percent }}%
</button>
</li>
</ul>
</div>
`,
props:['percentage'],
data(){
return{
selPercent: this.percentage,
percentages: [5, 10, 15, 25, 50 ]
}
},
methods:{
setPercent(value){
this.selPercent = value;
this.$emit('percent-seted', value);
}
},
})
new Vue({
el: '#demo',
data() {
return {
percent: 5
}
},
methods: {
percentSeted(val) {
this.percent = val
}
}
})
Vue.config.productionTip = false
Vue.config.devtools = false
ul {
list-style: none;
display: flex;
}
.selected {
background: violet;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<h3>{{ percent }}%</h3>
<button-group :percetntage="percent" #percent-seted="percentSeted" />
</div>
There is a product page where product list component and action buttons are there. This is the parent component. That product list component consists of list table and edit/add modal. Now, problem is Add action event is in parent component. But, add modal related data is available in child component.
So, how can i open that model based on click event from parent? Here i am doing like this approach.
Parent Component(Product Component snippets )
<template>
..... other code ....
<div class="action-buttons">
<vu-button class="add-action" #click="onAddAction">
<svg-icon
fill="#0071E3"
name="add"
height="20"
width="28"
/>
</vu-button>
</div>
<ChildComponent :open-add-modal="isAddModal" />
</template>
Methods in Parent component
onAddAction() {
this.editable = false;
this.isAddModal = true;
},
Now, in child component i passing boolean props openAddModal but i am checking condition into created hook to show Add modal.
Problem is in initial rendering or page load add modal is showing up not in click event. How can i solve this issue?
Child component(Created hook)
created() {
if(this.openAddModal) {
this.showModal = true;
this.formType = 'add';
this.editId = null;
}
},
I want to show add modal based on click event from parent not in initial page load.
You can try using a watcher instead of checking the value of open-add-modal in the created hook. This way, when the prop open-add-modal in the child component changes, you can check the new value there and emit the needed data to the parent to then open the modal.
Example:
Parent component code
<template>
<div>
<p>Parent component</p>
<button #click="changeOpenAddModal">Clic to get child data</button>
<button #click="resetParent">Reset data in parent</button>
<p>Data from child: {{ childData }}</p>
<br />
<br />
<Child
:openAddModal="this.openAddModal"
#child-component-data-emit="this.setChildData"
/>
</div>
</template>
<script>
import Child from "./Child";
export default {
name: "Parent",
components: { Child },
data() {
return {
childData: null,
openAddModal: false,
};
},
methods: {
changeOpenAddModal() {
this.openAddModal = !this.openAddModal;
console.log(
"changing openAddModal data. New value is ",
this.openAddModal
);
},
setChildData(data) {
console.log("setting child data", data);
this.childData = data;
},
resetParent() {
this.childData = null;
this.changeOpenAddModal();
},
},
};
</script>
Child component code
<template>
<div>
<p>Child component</p>
</div>
</template>
<script>
export default {
name: "Child",
props: {
openAddModal: {
type: Boolean,
default: false,
},
},
data() {
return {
childData: {
prop1: "lorem",
prop2: "ipsum",
prop3: "dolor",
},
};
},
watch: {
openAddModal: function (newValue, oldValue) {
console.log("child watcher with newValue", newValue);
if (newValue) {
this.$emit("child-component-data-emit", this.childData);
}
},
},
mounted: function () {
console.log("prop openAddModal value on mounted:", this.openAddModal);
},
};
</script>
I have built a few modals with Vue 2 and Vue CLI, and use an alternate approach for showing, hiding, and determined if add or edit mode. No watch or separate add/edit mode boolean is necessary.
The 'product' processing is somewhat contrived since no database or AJAX are used in this example, but you should get be able to evaluate the functionality.
Parent.vue
<template>
<div class="parent">
<h4>Parent of Form Modal</h4>
<div class="row">
<div class="col-md-6">
<button class="btn btn-secondary" #click="showAddModal">Show Add Modal</button>
<button class="btn btn-secondary btn-edit" #click="showEditModal">Show Edit Modal</button>
</div>
</div>
<form-modal v-if="displayModal"
:parentProduct="product"
#save-product-event="saveProduct"
#close-modal-event="hideModal"
/>
</div>
</template>
<script>
import FormModal from './FormModal.vue'
export default {
components: {
FormModal
},
data() {
return {
product: {
id: 0,
name: '',
description: ''
},
displayModal: false
}
},
methods: {
showAddModal() {
this.resetProduct();
this.displayModal = true;
},
showEditModal() {
this.product.id = 1;
this.product.name = 'productEdit';
this.product.description = 'productEditDescription';
this.displayModal = true;
},
hideModal() {
this.displayModal = false;
},
saveProduct(modalProduct) {
this.product = modalProduct;
this.hideModal();
console.log(this.product);
},
resetProduct() {
this.product.id = 0;
this.product.name = '';
this.product.description = '';
}
}
}
</script>
<style scoped>
.btn-edit {
margin-left: 0.5rem;
}
</style>
FormModal.vue
<template>
<!-- The Modal -->
<div id="form-modal" class="modal-dialog-container">
<div class="modal-dialog-content">
<div class="modal-dialog-header">
<h4>{{ modalTitle }}</h4>
</div>
<div class="modal-dialog-body">
<form #submit.prevent="saveProduct">
<div class="form-group">
<label for="product-name">Name</label>
<input type="text" class="form-control" id="product-name" v-model="product.name">
</div>
<div class="form-group">
<label for="product-description">Description</label>
<input type="text" class="form-control" id="product-description" v-model="product.description">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-secondary btn-close" #click="closeModal">Cancel</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
parentProduct: {
type: Object,
required: true
}
},
data() {
return {
product: this.parentProduct
}
},
computed: {
modalTitle() {
return this.product.id === 0 ? 'Add Product' : 'Edit Product';
}
},
methods: {
closeModal() {
this.$emit('close-modal-event');
},
saveProduct() {
// Add product
if (this.product.id === 0) {
this.product.id = 2;
}
this.$emit('save-product-event', this.product);
}
}
}
</script>
<style scoped>
.modal-dialog-container {
/* display: none; Hidden by default */
position: fixed;
/* Stay in place */
z-index: 1;
/* Sit on top */
left: 0;
top: 0;
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
/* Black w/ opacity */
}
.modal-dialog-content {
background-color: #fefefe;
margin: 10% auto;
padding: 20px;
border: 1px solid #888;
border-radius: 0.3rem;
width: 30%;
}
.btn-close {
margin-left: 0.5rem;
}
</style>
What I want to happen, is when you click on the radio button on the left, then it filter the content on the right to show only has the same location.
I don't know how to emit an event when someone click on the radio buttons.
I'm using the x-template in this vue code.
const locations = {
options: [
{
title: 'A',
value: 'a'
},
{
title: 'B',
value: 'b'
},
{
title: 'C',
value: 'c'
}]
};
const team = {
options: [
{
name: 'Team A',
location: 'a'
},
{
name: 'Team B',
location: 'b'
},
{
name: 'Team C',
location: 'c'
}]
};
Vue.component('location-filter', {
template: '#location-filter',
props: {
location: {
type: Array,
required: true
}
}
});
new Vue({
el: '#location-finder',
data: {
location: locations.options,
teams: team.options
}
});
.page {
padding:5px;
border:1px solid #cccccc;
display:flex;
flex-flow:row;
}
.sidebar {
flex-grow:0.6;
border: 1px solid red;
padding:10px;
}
.body {
padding:10px;
border:1px solid blue;
flex-grow:1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="page" id="location-finder">
<div class="sidebar">
<location-filter :location="location"></location-filter>
</div>
<div class="body">
<div v-for="team in teams">
<div>{{ team.name }}</div>
</div>
</div>
</div>
<script type="text/x-template" id="location-filter">
<div>
<div v-for="place in location">
<label>
<input type="radio" name="place" value="place.value">{{ place.title }}
</label>
</div>
</div>
</script>
Hopefully, it's good enough my explanation and the code to give you an picture of what i'm trying to achieve.
You can emit a custom event from inside of a component, such as when the user clicks on the radio label, like this:
<label #click="$emit('location', place.value)">
I named the event location. Now you can listen for an event named location in the parent's template (locationFinder) like this:
<location-filter :location="location" #location="changeLocation">
Now, locationFinder will try to call a method in its instance called changeLocation every time that location event is emitted. Here's what that method looks like:
changeLocation(location) {
this.selected = location;
}
You can see it sets a data property called selected to the selected location. So now you'll need your teams loop to check that selected location, and only show a team if its location matches the selected one. The best way to do this is to create a computed property which will always contain a filtered version of the teams list. If there is no selected team, it returns all teams:
filtered() {
if (this.selected) {
return this.teams.filter(team => team.location == this.selected);
} else {
return this.teams;
}
}
Finally, update the template to loop over this filtered teams list instead of the unfiltered one:
<div v-for="team in filtered">
I included an updated snippet.
const locations = {
options: [
{
title: 'A',
value: 'a'
},
{
title: 'B',
value: 'b'
},
{
title: 'C',
value: 'c'
}]
};
const team = {
options: [
{
name: 'Team A',
location: 'a'
},
{
name: 'Team B',
location: 'b'
},
{
name: 'Team C',
location: 'c'
}]
};
Vue.component('location-filter', {
template: '#location-filter',
props: {
location: {
type: Array,
required: true
}
}
});
new Vue({
el: '#location-finder',
data: {
location: locations.options,
teams: team.options,
selected: null
},
computed: {
filtered() {
if (this.selected) {
return this.teams.filter(team => team.location == this.selected);
} else {
return this.teams;
}
}
},
methods: {
changeLocation(location) {
this.selected = location;
}
}
});
.page {
padding:5px;
border:1px solid #cccccc;
display:flex;
flex-flow:row;
}
.sidebar {
flex-grow:0.6;
border: 1px solid red;
padding:10px;
}
.body {
padding:10px;
border:1px solid blue;
flex-grow:1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="page" id="location-finder">
<div class="sidebar">
<location-filter :location="location" #location="changeLocation"></location-filter>
</div>
<div class="body">
<div v-for="team in filtered">
<div>{{ team.name }}</div>
</div>
</div>
</div>
<script type="text/x-template" id="location-filter">
<div>
<div v-for="place in location">
<label #click="$emit('location', place.value)">
<input type="radio" name="place" value="place.value">{{ place.title }}
</label>
</div>
</div>
</script>
I have a simple project where i am trying to learn the concepts of vue.js using componenetes, comunication between components(i use eventBus) i am using the webkit-simple template to approach this, basicly what happens, is that i have 1 component that consists in a simple textarea where i add some text, that text should be displayed in my second component, that is a template where i render a array with all my texts that i inserted, like a list of quotes.
component addQuote
<template>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<div class="col-md-offset-3 col-md-6">
<label>Quote:</label>
<textarea v-model="quote.text" class="form-control" rows="5"></textarea>
<div class="text-center">
<button #click="addQuote" class="btn btn-primary center">Add Quote</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { quoteBus } from '../main.js';
export default {
methods: {
addQuote() {
if (this.counter < 10) {
this.counter++;
this.quote.key =+ new Date();
quoteBus.$emit('saveQuote', this.quote);
}
}
},
data: function () {
return {
quote: {},
counter: 0
}
},
created(){
quoteBus.$on('decreaseCounter', () => {
this.counter--
});
}
}
</script>
<style scoped>
.row {
margin-top: 40px;
}
.center {
margin: 0 auto;
}
div .text-center {
margin-top: 20px;
}
</style>
component quotes
<template>
<div class="row">
<div class="col-md-3" v-for="(quote,$index) in quotes" #click="deleteQuote($index)" :key="quote.key">
<div class="spacing">
<h2>{{quote.text}}</h2>
</div>
</div>
</div>
</template>
<script>
import { quoteBus } from '../main.js';
export default {
data: function () {
return {
quotes: []
}
},
methods: {
deleteQuote(i){
this.quotes.splice(i,1);
quoteBus.$emit('decreaseCounter');
}
},
created() {
quoteBus.$on('saveQuote', quote => {
this.quotes.unshift(quote);
console.log(JSON.stringify(this.quotes));
});
}
}
</script>
<style scoped>
h2 {
font-family: 'Niconne', cursive;
}
div .col-md-3 {
border: 1px solid darkgray;
padding: 10px;
}
div .row {
margin-top: 40px;
}
.spacing {
margin: 10px;
padding: 10px;
}
</style>
the problem is, everytime i add a quote the text replace all the elements before.
Example:
9th entry: text: "abcdef", all the entries in the array has this value in text, all my divs has the value of abcdef, what is happening :S
const quoteBus = new Vue();
Vue.component('addQuote', {
template: '#addQuote-template',
methods: {
addQuote() {
if (this.counter < 10) {
this.counter++;
this.quote.key = +new Date();
quoteBus.$emit('saveQuote', Object.assign({}, this.quote));
}
}
},
data: function() {
return {
quote: {},
counter: 0
}
},
created() {
quoteBus.$on('decreaseCounter', () => {
this.counter--
});
}
});
Vue.component('quotes', {
template: '#quotes-template',
data: function() {
return {
quotes: []
}
},
methods: {
deleteQuote(i) {
this.quotes.splice(i, 1);
quoteBus.$emit('decreaseCounter');
}
},
created() {
quoteBus.$on('saveQuote', quote => {
this.quotes.unshift(quote);
console.log(JSON.stringify(this.quotes));
});
}
});
new Vue({
el: '#app'
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<template id="addQuote-template">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<div class="col-md-offset-3 col-md-6">
<label>Quote:</label>
<textarea v-model="quote.text" class="form-control" rows="5"></textarea>
<div class="text-center">
<button #click="addQuote" class="btn btn-primary center">Add Quote</button>
</div>
</div>
</div>
</div>
</div>
</template>
<template id="quotes-template">
<div class="row">
<div class="col-md-3" v-for="(quote,$index) in quotes" #click="deleteQuote($index)" :key="quote.key">
<div class="spacing">
<h2>{{quote.text}}</h2>
</div>
</div>
</div>
</template>
<div id="app">
<add-quote></add-quote>
<quotes></quotes>
</div>
The problem is that there is only one instance of this.quote in your addQuote component. You pass that particular object to quotes to be put into the array every time. When an object is put into an array, it is by-reference. If you put the same object into an array multiple times, you just have multiple references to the object's contents. Every element of your array is a reference to the same set of contents.
You need to send a copy of the object instead:
quoteBus.$emit('saveQuote', Object.assign({}, this.quote));