How to combine two values from child components with an event? - vue.js

I can't assign data from child component.
I am simply trying to implement emitting a specific value with an event
Child component is:
<template>
<StackLayout orientation="vertical" width="210" height="210"
backgroundColor="blue" color="white">
<Label text="Father" row="0" col="1"
class="h2 text-center p-t-5 text-primary" />
<ListPicker :items="maleName" v-model="maleIndex" row="1" col="1"
#selectedIndexChange="selectedIndexofFatherChanged" />
</StackLayout>
</template>
<script>
export default {
data() {
return {
maleName: [
"Rey",
"Antonia",
"Victor",
"Lincoln",
],
maleIndex: 0
};
},
methods: {
selectedIndexofFatherChanged() {
this.$emit("fatherChanged", this.fatherName);
// console.log("changed to: " + this.fatherName);
}
},
computed: {
fatherName() {
return this.maleName[this.maleIndex];
}
}
};
</script>
and Parent Component is:
<template>
<Page>
<ActionBar title="Choose Parent" />
<GridLayout columns="*, *" rows="*,90">
<SelectMother col="0" #motherChanged="onMotherChanged">
</SelectMother>
<SelectFather col="1" #fatherChanged="onFatherChanged">
</SelectFather>
<Button text="Show Result" #tap="onButtonTap" row="2"
colSpan="2" />
</GridLayout>
</Page>
</template>
<script>
import SelectMother from "./SelectMother";
import SelectFather from "./SelectFather";
export default {
components: {
SelectMother,
SelectFather
},
data() {
return {
motherName: null,
fatherName: null
};
},
methods: {
onButtonTap() {
alert("father: " + this.fatherName + " mother: " + this
.motherName);
},
},
watch: {
onFatherChanged(nameFromComponent) {
nameFromComponent = this.fatherName;
},
onMotherChanged(nameFromComponent) {
nameFromComponent
= this.motherName;
}
},
};
</script>
When I console log payload in onFatherChanged method it turns null
How to combine father and mother and show results.
Select Father from child component
Select Mother from second child component
Assign incoming payload and Show Results
Please check {N} Playground
https://play.nativescript.org/?template=play-vue&id=Dez2fV&v=26

Instead of
onFatherChanged(payload) {
payload = this.fatherName;
console.log(payload);
},
It should be;
onFatherChanged(payload) {
this.fatherName = payload;
console.log(payload);
},

Related

How to render a component in Vue editor as custom tag?

I have created a custom component called <Myh id="1" /> and I created a custom tag in vue2editor. This is code:
<div class="row pb-5 ">
<div id="toolbar">
<select class="ql-size">
<option value="small"></option>
<option selected></option>
<option value="large"></option>
<option value="huge"></option>
</select>
<button class="ql-bold"></button>
<button class="ql-italic"></button>
<button class="ql-underline"></button>
<button class="ql-strike"></button>
<button class="ql-color"></button>
<button class="ql-customBtn">Custom</button>
</div>
<vue-editor :editorOptions="editorSettings" v-model="article.content" class="w-100 h-100"> </vue-editor>
</div>
<button class="btn btn-primary" #click="updateArticle()"> Conferma </button>
</div>
</template>
<script>
import 'vue-select/dist/vue-select.css';
export default {
data: () => ({
editorSettings: {
modules: {
history: {
delay: 2000,
maxStack: 500,
userOnly: true,
},
toolbar: {
container: '#toolbar',
handlers: {
customBtn: () => {
const s = window.getSelection();
const n = s.anchorNode;
const o = s.anchorOffset;
const f = s.focusOffset;
n.textContent = n.textContent.substring(0,o)+'<Myh id="1" />'
+n.textContent.substring(f,n.textContent.length);
},
redo: () => {
console.log("redo button was clicked");
},
},
},
},
}
})
}
</script>
With another component I render the content saved in a mySQL database:
<script>
export default {
props: {
html: {
type: String,
default: ""
}
},
render(h) {
return h({
template: `<div> ${ this.replaceAll( this.html ) }</div>`
});
},
methods:{
htmlDecode(input) {
const doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
},
replaceAll(input){
return input.replace(/<Myh/g, '<Myh').replace(/>/g, '>');
}
}
};
</script>
The problem is that in textarea editor I see "<Myh id="1" />" as text. Would it be possible to render it also during the writing phase? Or what suggestion would you give me?
Thank you!

How to pass props to input value in Vuejs

I have a parent component as a Cart. Here I defined quantity and I want to pass this quantity to the child component's input value which is Counter. So here how I am passing it and here is my parent component, Cart:
<Counter quantity="item.quantity"/>
And here is my child component, Counter:
<template>
<div id="counter">
<button class="minus" #click="countDown"><i :class="quantity == 0 ? 'disabled' : ''" class="fas fa-minus"></i></button>
<div class="count-number"><input class="counter-content" type="number" v-model="quantity"></div>
<button class="plus" #click="countUp"><i class="fas fa-plus"></i></button>
</div>
</template>
<script>
export default {
props: {
quantity: Number
},
methods: {
countUp() {
this.quantity++;
},
countDown() {
if(this.quantity > 0) {
this.quantity--;
}
},
}
}
</script>
I am quite new in Vue, so maybe I am doing something wrong when I pass the props. So could you help me about this?
Try (with the : colon sign)
<Counter :quantity="item.quantity"/>
Before you were just passing the string "item.quanity"
I see you're modifying your prop directly:
countUp() {
this.quantity++;
},
countDown() {
if(this.quantity > 0) {
this.quantity--;
}
},
This is not how you do it in Vue. You need to use two way binding.
countUp() {
this.$emit('input', this.quantity+1)
}
countDown() {
this.$emit('input', this.quantity-1)
}
and in your parent component:
<Counter :quantity="item.quantity" #input="(payload) => {item.quantity = payload}"/>
By the way, the Vue styleguide recommends to use multi-word component names: https://v2.vuejs.org/v2/style-guide/#Multi-word-component-names-essential (Cart = bad, MyCart = good)
We cannot change the value that we get from props, so I created a variable and put props there when mounting
Try it
<Counter :quantity="item.quantity"/>
and
<template>
<div id="counter">
<button class="minus" #click="countDown"><i :class="sum == 0 ? 'disabled' : ''" class="fas fa-minus"></i></button>
<div class="count-number"><input class="counter-content" type="number" v-model="sum"></div>
<button class="plus" #click="countUp"><i class="fas fa-plus"></i></button>
</div>
</template>
<script>
export default {
props: {
quantity: Number
},
data: () => ({
sum: 0
}),
mounted() {
this.sum = this.quantity;
},
methods: {
countUp() {
this.sum++;
},
countDown() {
if(this.sum > 0) {
this.sum--;
}
},
}
}
</script>

Error: Do not mutate vuex store state outside mutation handlers

Scenario
I am using Vuex, to store some data in it, and in my case the ticket details.
Initially, I have a ticket which has an array of discounts, to be empty.
Once I hit the button "Add discount" I mount the component called "testDiscount" which in the mounted hook pushes the first object ({"code": "Foo", "value":"Boo"}) in the discounts array of a specific ticket in the store.
The problem arise when I try to type in the input boxes (changing its state) in this component where I get the error "do not mutate Vuex store state outside mutation handlers.". How could I best handle this?
Test.vue
<template>
<div>
<test-component v-for="(t, key) in tickets" :key="key" :ticket-key="key" :tid="t.id"></test-component>
</div>
</template>
<script>
import TestComponent from "~/components/testComponent.vue";
export default {
layout: "noFooter",
components: {
"test-component": TestComponent,
},
data() {
return {
tickets: this.$store.state.ticketDiscount.tickets,
};
},
mounted() {
if (this.tickets.length == 0) {
this.$store.commit("ticketDiscount/addTicket", {
id:
this.$store.state.ticketDiscount.tickets.length == 0
? 0
: this.$store.state.ticketDiscount.tickets[
this.$store.state.ticketDiscount.tickets.length - 1
].id + 1,
discount: [],
});
}
},
};
</script>
ticketDiscount.js
export const state = () => ({
tickets: []
});
export const mutations = {
addTicket(state, ticket) {
state.tickets.push(ticket);
},
addDiscount(state, property) {
state.tickets.find(ticket => ticket.id == property.id)[property.name].push(property.value);
}
testComponent.vue
<template>
<div>
<h3>Ticket number: {{ticketKey + 1}}</h3>
<button #click="showDiscount = true">Add discount</button>
<test-discount v-model="discount_" v-if="showDiscount" :tid="tid"></test-discount>
</div>
</template>
<script>
import testDiscount from "~/components/test-discount.vue";
export default {
components: {
testDiscount,
},
data() {
return {
showDiscount: false,
tid_: this.tid,
};
},
props: {
tickets: Array,
ticketKey: { type: Number },
tid: { type: Number, default: 0 },
},
methods: {
updateTicket() {
this.$emit("updateTicket", {
id: this.tid_,
value: {
discount: this.discount_,
},
});
},
},
mounted() {
this.$watch(
this.$watch((vm) => (vm.discount_, Date.now()), this.updateTicket)
);
},
computed: {
discount_: {
get() {
return this.$store.state.ticketDiscount.tickets.find(
(ticket) => ticket.id == this.tid
)["discount"];
},
set(value) {
// set discount
},
},
},
};
</script>
testDiscount.vue
<template>
<div class="container">
<div class="title">
<img src="~/assets/svgs/price_tag.svg" />
<span>Discount code</span>
{{ discounts }}
</div>
<div class="discount-container">
<div v-for="(c,idx) in discounts" class="discounts" :key="idx">
<div class="perc-input">
<input style="max-width: 50px;" v-model.number="c.discount" type="number" min="1" max="100" step="1" placeholder="10">
<div>%</div>
</div>
<input class="code-input" v-model="c.code" placeholder="Code">
<img src="~/assets/svgs/bin.svg" title="Delete code" #click="deleteCode(idx)" v-if="discounts.length > 1"/>
</div>
</div>
<span #click="newDiscount" class="add-another">+ Add another discount</span>
</div>
</template>
<script>
export default {
props: {
value: {
type: Array,
},
tid: { type: Number, default: 0 },
},
data() {
return {
discounts: this.value,
}
},
mounted() {
if (this.discounts.length == 0) {
this.newDiscount();
}
},
methods: {
newDiscount() {
this.$store.commit('ticketDiscount/addDiscount',
{
"id": this.tid,
"name": "discount",
"value": { code: null,discount: null }
});
},
deleteCode(index) {
this.discounts.splice(index, 1);
}
},
watch: {
discounts() {
this.$emit('input', this.discounts)
}
},
beforeDestroy() {
this.$emit('input', []);
}
}
</script>
you shouldn't use v-model in this case.
<input style="max-width: 50px;" v-model.number="c.discount" .../>
you could just set the value
<input style="max-width: 50px;" :value="c.discount" #change="handleValueChange" .../>
and then in handleValueChange function to commit the action to update just for that value.

Best way to do calculation on a property of a Prop(coming from the parent) object and put the result in the template

I have an object that is being passed to the child component and I want to display those values in the child component template. However, the object contains properties that are arrays and objects so I need to do some parsing/manipulation on them before I set the value.
object:
{
"id": "111",
"ip": "10.192.1.112",
"profiles": [
"Japan",
"Japan222"
],
"network": [
{
"plan_id": "PLAN-UUID",
"plan_name": "1BCT"
},
{
"plan_id": "PLAN-UUID",
"plan_name": "1BCT2"
}
],
"status": "LOCKED",
"last_downloaded": "1547672769000"
}
template code in child:
<label label="id" :value=this.objectFromParent.id />
<label label="ip" :value=this.objectFromParent.ip />
<label label="ip" :value= calculateProfiles />
where calculateProfiles is the method to calculate it and return the string value? I'm not sure what's a good way to handle this.
Try this
<template>
<div>
<label label="id" :value="objectFromParent.id" />
<label label="ip" :value="objectFromParent.ip" />
<label label="ip" :value="calculateProfiles" />
</div>
</template>
<script>
export default {
props: {
objectFromParent: {
type: Object,
default() {
return {
id: '111',
ip: '10.192.1.112',
profiles: ['Japan', 'Japan222'],
network: [
{
plan_id: 'PLAN-UUID',
plan_name: '1BCT'
},
{
plan_id: 'PLAN-UUID',
plan_name: '1BCT2'
}
],
status: 'LOCKED',
last_downloaded: '1547672769000'
}
}
}
},
computed: {
calculateProfiles() {
return this.profiles.join(',')
}
}
}
</script>
To help him on the comment:
<label label="ip">
<md-icon v-if="objectFromParent.status==='LOCKED'">lock</md-icon>
{{calculateProfiles}}
</label>

Can a Vue component behave like wordpress shortcode attributes

Can a Vue component behave like wordpress shortcode attributes? For example in [wp-shortcode post_type="post"], I can define the post type in the shortcode and the data will be fetched accordingly.
Can something similar be done in Vue? For example <grid-one type="posts" /> or <grid-one type="videos" />. The type in the grid-one tag will change the request.type in the data.
GridOne.vue
<template>
<main class="site-content">
<div class="container">
<section v-if="posts.length" class="articles-list">
<post-item
v-for="post in posts"
:key="post.id"
:post="post"
/>
<pagination
v-if="totalPages > 1"
:total="totalPages"
:current="page"
/>
</section>
</div>
</main>
</template>
<script>
import PostItem from '#/components/template-parts/PostItem'
import Pagination from '#/components/template-parts/Pagination'
export default {
name: 'GridOne',
components: {
PostItem,
Pagination
},
props: {
page: {
type: Number,
required: true
}
},
data() {
return {
request: {
type: 'posts',
params: {
per_page: this.$store.state.site.posts_per_page,
page: this.page
},
showLoading: true
},
totalPages: 0
}
},
computed: {
posts() {
return this.$store.getters.requestedItems(this.request)
}
},
methods: {
getPosts() {
return this.$store.dispatch('getItems', this.request)
},
setTotalPages() {
this.totalPages = this.$store.getters.totalPages(this.request)
}
},
created() {
this.getPosts().then(() => this.setTotalPages())
}
}
</script>