Pass data between 2 different components using emit - vuejs2

I am new to vueJS.I am using vue2. I have 3 components catalogue.vue,sellingPrice.vue and profit.vue. Catalogue.vue is a list of all products with a cost price,selling price and a profit percentage. Selling price and profit percentage are individual components inside the catalogue components.Scenario is, when selling price is edited, profit should be automatically calculated and when profit is input manually selling price should be automatically calculated. I am having difficulty passing the profit percentage to catalogue.
I tried using $emit in profit and $on in catalogue. I dont see that working. Can someone help me figure this out.
Profit.vue.
<template id="profit">
<div>
<div v-if="!isActive" #click="toggle()">
<span>{{ calculateProfit }}%</span>
</div>
<div v-if="isActive" class="control has-icons-right">
<input v-model="calculateProfit" class="input is-medium" type="text" placeholder="Text input">
<div class="icon-container is-pulled-right">
<span class="icon is-medium is-right edit-success" #click="emitProfitPercentage()">
<i class="fas fa-check is-success" />
</span>
<span class="icon is-medium is-right edit-cancel" #click="toggle()">
<i class="fas fa-times" />
</span>
</div>
</div>
</div>
</template>
computed:{
calculateProfit:{
get(){
let profPercentage = 0;
if(this.product.profit > 0){
profPercentage = this.product.profit;
}else{
let profitPercentage = 0;
let profitAmt = 0;
if(this.product.sellingCost === 0){
profitPercentage = 0;
}else if(this.product.calculatedLandedPrice > 0 && this.product.sellingCost > 0){
profitAmt = this.product.sellingCost - this.product.calculatedLandedPrice;
profitPercentage = Math.round((profitAmt/this.product.cost)*100);
}else if(this.product.calculatedLandedPrice === 0 && this.product.sellingCost > 0){
profitAmt = this.product.sellingCost - this.product.cost;
profitPercentage = Math.round((profitAmt/this.product.cost)*100);
}
profPercentage= profitPercentage;
}
return profPercentage;
},
set(newValue){
this.product.profit = newValue;
}
},
},
methods:{
toggle() {
this.isActive = !this.isActive;
},
emitProfitPercentage(){
console.log('calculateProfit:', this.product.profit);
this.$emit('update-profit',this.product.profit);
this.toggle();
}
}
Catalogue.vue:
updateSellingPrice(profit){
console.log('profit:', profit);
}
Also tried using this.$on inside created. That doesn't seem to work as well.

Related

Vue 3 : Event, emit and props

I have a problem with event, emit and props (and probably some logic too)
I have a component A in which I have a loop with a component B.
In this loop, I have a method to open the modal (which is a component C) but this method is not part of component B.
Like this :
<a>
<!-- MODAL-->
<div v-if="showModal">
<modal-cat #cat="catId = getCatId($event)" #addTx="addTx($event)"></modal-cat>
</div>
<div v-if="transactions.length != 0" class="mx-auto">
<div v-for="tx in transactions" :key="tx">
<div class="mb-2 border border-gray-600 rounded-lg bg-white pt-2 pb-4">
<div class="flex justify-end">
<span
class="inline-flex items-center justify-center h-6 w-6 rounded-full text-lg bg-blue-800 text-white"
#click="showModal = true, txToAdd = tx">
<i class='bx bx-plus'></i>
</span>
</div>
<transaction-data :transaction="tx" :address="walletAddress"></transaction-data>
</div>
</div>
</div>
</a>
In this modal, I fetch some data (in fact, a array of categories) that I also display in a loop.
Like this :
<div class="modal-container">
<div v-for="(categorie) in categories" :key="categorie">
<p #click="$emit('cat', categorie.id)">{{ categorie.name}}</p>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button" #click="$emit('addTx', 'ok')">
OK
</button>
</slot>
</div>
</div>
I need some data from my modal in my component A but I also need some data from my component B in my component A (to add a transactions to a category)
I managed to get the data I wanted like this (And I can get it):
const showModal = ref(false);
const txToAdd = ref({});
const catId = ref(0);
function getCatId(event) {
return event
}
const addTx = (value) => {
if (value === "ok") {
//console.log(txToAdd.value); <= the value are well displayed in the console.
let data = {
tx: txToAdd.value,
catId: catId.value
}
store.dispatch("categories/addTxToCategories", data);
}
}
But in my store, when I try to get the payload, I can't access to the data and I only get the payload object.
Is there something wrong with my logic ? What am I doing wrong ?
EDIT
I just need to wrap the result in spread operator, like this :
const addTx = (value) => {
if (value === "ok") {
//console.log(txToAdd.value);
let data = {
tx: {...txToAdd },
catId: catId.value
}
store.dispatch("categories/addTxToCategories", {...data });
}
}
And in my store, the payload MUST be the second argument :
async addTxToCategories({ commit }, payload) {}

How to conditionally render the screens with button in vuejs?

export default {
name: "HelloWworld",
data: function () {
return {
isHidden: false,
isWelcome: false,
isFadeout: false,
}
}
<div v-if="!isHidden">
//some code for screen1
<button v-on:click="isHidden = true"> HELLO</button>
</div>
<div v-else-if="isHidden && !isFadeout">
//some code for screen 2
<button v-on:click="isFadeout = true"> Hi</button>
</div>
<div v-else-if="isFadeout && isHidden && !isWelcome">
<input
:type="passwordFieldType"
v-model="user.password"
id="password"
name="password"
class="input-section-three"
:class="{ 'is-invalid': submitted && $v.user.password.$error }"
placeholder="Enter new password"
:maxlength="maxpassword"
v-on:keypress="isPassword($event)"
/>
<div
v-if="submitted && $v.user.password.$error"
class="invalid-feedback-two"
>
<span v-if="!$v.user.password.required">Password is required</span>
<span v-if="!$v.user.password.minLength"
>Minimum 8 character with
alphanumeric along with 1 capital letter, 1 small letter
and 1 special character at least</span
>
</div>
<input
:type="passwordFieldTypetwo"
v-model="user.confirmPassword"
id="confirmPassword"
name="confirmPassword"
class="input-section-three"
:class="{
'is-invalid': submitted && $v.user.confirmPassword.$error
}"
placeholder="Confirm password"
:maxlength="maxconfirmpassword"
v-on:keypress="isconfirmPassword($event)"
:disabled="user.password.length < 8"
/>
<div
v-if="submitted && $v.user.confirmPassword.$error"
class="invalid-feedback-two"
>
<span v-if="!$v.user.confirmPassword.required"
>Confirm Password is required</span
>
<span v-else-if="!$v.user.confirmPassword.sameAsPassword"
>Password must match</span
>
</div>
<button v-on:click="isWelcome = true" :disabled="user.confirmPassword.length < 8" > SUBMIT </button>
</div>
<div v-else-if="isWelcome">
//some code for screen 4
<button>Fine</button>
</div>
conditional rendering is working fine but, In screen3 submit button code, i have ""v-on:click="isWelcome = true""", if i remove this line, password validation is happening onclick of submit button, Where if i place again ""v-on:click="isWelcome = true""" without checking the validations simply its moving to 4th screen
You could try to use steps to show everything you need.
data() {
return{
submissionStep: 1
}
}
I believe that you render different components for each screen. So you can show each component with v-if="submissionStep === 1"
Each time you click the next/submit button you change the submissionStep with a method binded like
update(){
if(this.submissionStep <= 3){
this.submissionStep += 1
}
},
Instead of 3 you can limit your steps as the number of screens you have
<button v-if="isHidden" v-on:click="isWelcome = true"> HELLO</button>
<button v-if="!isHidden" v-on:click="isWelcome = true"> HELLO</button>
Use the v-if attribute inside the buttons.
If you have more than one condition, do it in computed.
computed: {
displayButton() {
return (this.isHidden && this.isWelcome);
},
}
<button v-if="displayButton" v-on:click="isWelcome = true"> HELLO</button>

How to sum values in VueJs

Can someone help me in sum values in VueJs
im try to get a sum of products
All my products display in select options tag by v-for
Im add event on click and after this im get price value in method
If someone have info about solved this problem im so gratefull
this is my code in Vue component
<div class="form-group row">
<label class="typo__label col-sm-3 col-form-label">Продукты</label>
<div class="col-sm-9">
<multiselect selectLabel="Выбрать" deselectLabel="Удалить" selectedLabel="Выбрано" #input="setSelected"
:taggable="true" v-model="formData.products" :options="this.$store.state.order.products" :multiple="true"
:hideSelected="true" :custom-label="titleWithPrice" :close-on-select="false" :clear-on-select="false"
:preserve-search="true" placeholder="Выберите продукты" label="title" track-by="id" :preselect-first="true">
<template slot="selection" slot-scope="{ values, search, isOpen }"><span class="multiselect__single"
v-if="values.length && !isOpen">{{ values.length }} options selected</span></template>
</multiselect>
</div>
</div>
this is my code in method
setSelected(value) {
let prices = value;
prices.forEach((price) => {
this.price = price.price
})
}
this is my code in computed
totalItem: function(){
let sum = 0;
this.items.forEach(function(item) {
sum += (parseFloat(item.price));
});
return sum;
}
If the formData.products is correctly setting the values selected, then you can directly get the sum of values from the computed section.
computed:{
sum : function(){
let products = this.formData.products;
return products.reduce((a,b)=>{
return parseFloat(a.price) + parseFloat(b.price);
})
}
}

vue+bulma Tabs Error:'openTab' is defined but never used

I would like to create a tab using vue and bulma, but I'm getting the following error:
error: 'openTab' is defined but never used (no-unused-vars) at
code:
<template>
<div>
<div class="tabs is-centered">
<ul>
<li class="tab is-active" onclick="openTab(e,'adult')"><a>Adult</a></li>
<li class="tab" onclick="openTab(e,'card')"><a>Card</a></li>
<li class="tab" onclick="openTab(e,'food')"><a>Food</a></li>
</ul>
</div>
<div id="adult" class="content-tab">
<img :src="require(`../../static/dst/${parentData.file_name}`)">
<div v-bind:style="{ color: `${parentData.font_color}`}">
{{parentData.info_text}}<br>
{{parentData.rate_adult}}{{parentData.part_name}}<br>
{{parentData.rate_part}}
</div>
</div>
<div id="card" class="content-tab" style="display: none">
<div :style="{ color: `${parentData.card_color}`}">
{{parentData.card_text}}
</div>
</div>
<div id="food" class="content-tab" style="display: none">
{{parentData.class_name}}
</div>
</div>
</template>
<script>
export default {
name: 'Child',
props: ['parentData'],
openTab(e, tabName) {
var i, x, tablinks;
x = document.getElementsByClassName("content-tab");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tab");
for (i = 0; i < x.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" is-active", "");
}
document.getElementById(tabName).style.display = "block";
e.currentTarget.className += " is-active";
}
}
</script>
                               
I want to find the answer to this problem.
Tap function does not apply.
Process:
Upload an image
Return json contents
Check by tap
Can you tell me what the error is?
Your openTabs function should be part of methods. So:
props: {},
methods: {
openTab()
}
More information here: https://v2.vuejs.org/v2/guide/events.html#Method-Event-Handlers

JSFiddle not recognizing Angular

This JSfiddle does not seem to be recognizing Angular, even though that library has been selected in the JS pane.
http://jsfiddle.net/knot22/rxqh8jtc/12/
Why isn't it working?
Code is posted below, as required by SO; however, it is probably most beneficial to navigate to the fiddle to see all the settings.
Here is the HTML:
<html ng-app="myApp">
<script src="//cdnjs.cloudflare.com/ajax/libs/validate.js/0.12.0/validate.min.js"></script>
<div ng-controller="formulaCtrlr as vm" >
<form name="vm.formContainer.form" autocomplete="off">
<div class="form-group" ng-class="{'has-error': vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$invalid}">
<label for="FatA" class="col-sm-2 control-label">Fat A</label>
<input name="FatA" type="text" class="form-control col-sm-10 input-sm" ng-required="true" ng-model-options="{updateOn: 'blur'}" ui-validate="{numberCheck: 'vm.numberCheck($value)', fatRangeCheck: 'vm.fatRangeCheck($value)'}" ng-model="vm.formulaInput.Fats[0]" /><span>%</span>
<span class="error" ng-show="vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$invalid">Invalid entry.</span>
<span class="error" ng-show="vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$error.numberCheck">{{vm.errorMessages.numberCheck}}</span>
<span class="error" ng-show="vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$error.fatRangeCheck">{{vm.errorMessages.fatRangeCheck}}</span>
</div>
<div class="form-group" ng-class="{'has-error': vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$invalid}">
<label for="FatB" class="col-sm-2 control-label">Fat B</label>
<input name="FatB" type="text" class="form-control col-sm-10 input-sm" ng-required="true" ng-model-options="{updateOn: 'blur'}" ui-validate="{numberCheck: 'vm.numberCheck($value)', fatRangeCheck: 'vm.fatRangeCheck($value)'}" ng-model="vm.formulaInput.Fats[1]" /><span>%</span>
<span class="error" ng-show="vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$invalid">Invalid entry.</span>
<span class="error" ng-show="vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$error.numberCheck">{{vm.errorMessages.numberCheck}}</span>
<span class="error" ng-show="vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$error.fatRangeCheck">{{vm.errorMessages.fatRangeCheck}}</span>
</div>
<div class="col-sm-offset-2">
<input type="reset" value="Clear" class="btn btn-default" ng-click="vm.clear()" ng-disabled="vm.formContainer.form.$pristine" />
</div>
</form>
<div>formula input: {{vm.formulaInput}}</div>
</div>
</html>
Here is the JS:
angular.module('myApp', ['ui.validate'])
.controller("formulaCtrlr", ['$scope', function ($scope) {
var vm = this;
vm.formContainer = {
form: {}
}
var originalFormContainer = angular.copy(vm.formContainer); //used for clear function below (to clear form)
vm.formulaInput = {};
vm.formulaInput.Fats = [];
vm.clear = function () {
//vm.formulaInput.Fats = [];
//vm.formContainer.form.$setPristine();
vm.formContainer = angular.copy(originalFormContainer);
}
vm.errorMessages = {
numberCheck: 'Value must be a number.',
fatRangeCheck: 'Number must be between 0 and 100.'
}
vm.numberCheck = function (value) {
var result = !(isNaN(parseFloat(value)));
return result;
//return !(isNaN(parseFloat(value)));
}
vm.fatRangeCheck = function (value) {
var result = (value && value > 0.0 && value < 100.0);
return result;
//return (value && value > 0.0 && value < 100.0);
}
}]);
After adding
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-validate/1.2.3/validate.min.js"></script> to the HTML it worked.