Click Event for angularclass for toggle class - angular5

I have multiple button in my page like below. My problem is when I click on one button all the button color are getting changed.
How can I prevent so that the color of only clicked button is changed?
<div class="my_class" (click)="clickEvent()"
[ngClass]="status ? 'success' : 'danger'">
Some content
</div>
<div class="my_class" (click)="clickEvent()"
[ngClass]="status ? 'success' : 'danger'">
Some content
</div>
<div class="my_class" (click)="clickEvent()"
[ngClass]="status ? 'success' : 'danger'">
Some content
</div>
status: boolean = false;
clickEvent(){
this.status = !this.status;
}

The easiest way would to provide a boolean value for each button. I would do something like :
<div class="my_class" (click)="status1=!status1"
[ngClass]="status1 ? 'success' : 'danger'">
Some content
</div>
<div class="my_class" (click)="status2=!status2"
[ngClass]="status2 ? 'success' : 'danger'">
Some content
</div>
<div class="my_class" (click)="status3=!status3"
[ngClass]="status3 ? 'success' : 'danger'">
Some content
</div>
status1: boolean = false;
status2: boolean = false;
status3: boolean = false;
Pay attention that I didn't use any function. I find this manner quicker and easier if we just want to implement some toggles.

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 add class on different if statement in vue

I want to change to color of my element each time it clicks the element, (to show start, in-progress and finished). For this I made a variable and increasing its value each time user clicks it and then applying different classes for styling.
<div v-for="error in errors" :key="error" class="element" > <span #click="modify()"> {{error}}</span></div>
I declare a variable step in data
modify () {
step = step + 1
}
now I want to change styling base of this step (1, 2, 3).
Any idea, how can I do this?
you can try :
<div class="element" v-for="error in errors" :key="error" :class="step==1 ? 'class1' :
step==2 ? 'class2':'class3'">
....
</div>
I would suggest an approach like this:
<div v-for="error in errors" :key="error" :class="`class_${step}`" >
<span #click="modify()"> {{error}}</span>
</div>
Or if you don't want to name your classes that way:
<div v-for="error in errors" :key="error" :class="getClass" >
<span #click="modify()"> {{error}}</span>
</div>
...
methods: {
getClass() {
if (this.step === 1) return 'classA';
if (this.step === 2) return 'classB';
// etc.
}
}

vue.js basic function not working as wanted

I have create a set of pairs of divs, that use the v-for method to get data from a set of dummy objects in an array. The goal is for each pair of divs when I click on the visible div it opens the corresponding relevant div. At the moment my function which I have attached as a property of a method object only opens the invisible div of the first pair of divs even if I click on the 3rd visible div it still displays the invisible div. i am using the vue framework.
I have attached pictures of my code then the actual code.
[The div I am trying to open is session-on-click details atm it is opening only that for the first index][1]
<div class = "rowuserlog" id="log-container" v-for="session in sessions" :key="session.id"
>
<div id="log-container-user-row-1">
<div id="profile-log-title" #click="logToggler()"> {{session.name}} </div>
<div id="profile-log-date"> {{session.date}}</div>
<div id="user-log-metric-container">
<div id ="user-log-hr" class="log-metric">{{session.hr}}</div>
<div id ="user-log-time" class="log-metric"> {{session.time}}</div>
<div id ="user-log-meters" class="log-metric"> {{session.distance}} </div>
<div id="session-onclick-details">
<div id="log-comments">
{{session.comments}}
</div>
<div id="log-edit-buttons">
<button id="log-save" class="log-edit-button"> Save </button>
<button id="log-delete" class="log-edit-button"> Delete </button>
<button id="log-cancel" class="log-edit-button"> Cancel </button>
<button id="log-edit" class="log-edit-button"> Edit </button>
</div>
</div>
```
My method
The toggler is the method I want:Here is the code
methods:{
logToggler () {
const extraInfoLog = document.getElementById("session-onclick-details");
return extraInfoLog.style.display="block";
}
]
[2]
[1]: https://i.stack.imgur.com/ddrYo.png
[2]: https://i.stack.imgur.com/evriF.png
data() {
return {
sessions:[
{ name:'test',
value:false,
id:1
},
{ name:'test1',
value:false,
id:2
},
{ name:'test2',
value:false,
id:3
}
]
}
}
methods:{
logToggler(_index) {
for(let index=0;index<sessions.length;index++) {
if(index == _index)
sessions[index].value = true
else
sessions[index].value = false
}
}
}
Template
<div class = "rowuserlog" id="log-container" v-for="(session,index) in sessions" :key="session.id"
>
<div id="profile-log-title" #click="logToggler(index)" v-if="session.value"> {{session.name}} </div>

Vue inline click event `this` is undefined

i'm getting an error in the below code saying this is undefined.
<div class="location-list__item" v-for="(value, key) in locations.data">
<div class="location-list__item--text"
:class="{ selected: selected === key }"
#click="() => { this.selected = key; this.manageSurrounding = false }">
<i class="fas fa-compass"></i> {{ value.name }}
<span v-if="value.changed" class="has-text-danger"> Changed</span>
</div>
</div>
However if I change this line:
#click="() => { this.selected = key; this.manageSurrounding = false }"
to this
#click="selected = key"
It works fine, however I need to change manageSurrounding at the same time and I don't want to create a method for such a simple thing.
You can do multiple assignments by using semicolon like the above statement which you have written.
<div class="location-list__item" v-for="(value, key) in locations.data">
<div class="location-list__item--text"
:class="{ selected: selected === key }"
#click="selected = key;manageSurrounding = false"> # Like this
<i class="fas fa-compass"></i> {{ value.name }}
<span v-if="value.changed" class="has-text-danger"> Changed</span>
</div>
</div>
You can use a anonymous function like,
<div onclick="return function()
{ selected = key; manageSurrounding = false }'
</div>
Just create a method and put in the update lines, you are better off on the long run, if your list is changing/reordering/re-rendering often.
It’s an optimization opportunity, so don’t try to force it in just because it seems small. Have a look at this answer: anonymus function in template

Why still adding even with validation form?

When I click the button on my modal with an empty field on my input its give me an undefined value on my console. And when I put a value on my input and click the button it is adding to my database. The problem is even the empty field or the undefined value are also adding to my database and the sweetalert is not working. I want to prevent the empty field adding to my database and prevent the undefined. Can somebody help me?
//start of method
checkForm: function(e) {
if (this.category_description) {
return true;
}
this.errors = [];
if (!this.category_description) {
this.errors.push('Category required.');
}
e.preventDefault();
},
addCategory : function() {
axios({
method : "POST",
url : this.urlRoot + "category/add_category.php",
data : {
description : this.category_description
}
}).then(function (response){
vm.checkForm(); //for FORM validation
vm.retrieveCategory();
console.log(response);
swal("Congrats!", " New category added!", "success");
vm.clearData();
}).catch(error => {
console.log(error.response);
});
},
//end of method
<form id="vue-app" #submit="checkForm">
<div class="modal" id="myModal" > <!-- start add modal -->
<div class="modal-dialog">
<div class="modal-content " style="height:auto">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title"> Add Category </h4>
<button #click="clearData" type="button" class="close" data-dismiss="modal"><i class="fas fa-times"></i></button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div class="form-group">
<div class="col-lg-12">
<input type="text" class="form-control" id="category_description" name="category_description" v-model="category_description" placeholder="Enter Description">
<p v-if="errors.length">
<span v-for="error in errors"> {{ error }} </span>
</p>
</div>
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button type="submit"#click="category_description !== undefined ? addCategory : ''" class="btn btn-primary"> Add Category </button>
</div>
</div>
</div>
</div>
</form>
The easiest way to stop this is adding data check. And condition check at top of your method.category_description !== undefined. Btw, move your e.preventDefault() to top too.
First of all do this:
- <button type="submit" #click="category_description !== undefined ? addCategory : ''" class="btn btn-primary"> Add Category </button>
+ <button type="submit" #click.prevent="addCategory" class="btn btn-primary"> Add Category </button>
and then in addCategory:
addCategory() {
if (!this.category_description) {
return;
} else {
// do your stuff here
}
}
When you are clicking on Add Category button, it is triggering the addCategory along with your validation method.
The return value of validation method has no impact on triggering of addCategory.
This issue can be handled in following ways.
Call addCategory only when there is some valid data
<button type="submit" #click="category_description != undefined ? addCategory() : ''" class="btn btn-primary"> Add Category </button>
Call the validation method inside addCategory and then proceed.