I have a Vue project with Firebase. I am using v-for for getting data from Firebase database and I have a "description" value. When users click anywhere on tr I want to expand and show only clicked value's description. But in my code; when I click tbody expand all description values. How can I fix this?
My code:
<tbody v-for="item in items" :key="item.id" #click="isClicked = !isClicked">
<tr >
<td>
{{item.name}}
</td>
<td>
{{item.surname}}
</td>
<td>
{{item.explanation}}
</td>
<td>
<span #click="isDelete(item)">X</span>
</td>
</tr>
<tr v-if="isClicked === true">
{{item.desc}}
</tr>
</tbody>
Thank for helping.
Get the index of the loop:
v-for="(item, index) in items"
Make a function which accepts the index as an argument:
setActiveRow(index)
Assign the index to the isClicked variable:
setActiveRow(index) {
this.isClicked = index
}
Now use that as your click function and compare the isClicked variable in your row:
<tbody v-for="(item, index) in items" :key="item.id" #click="setActiveRow(index)">
And then only show the specific row if the index matches:
<tr v-if="isClicked === index">
<td> {{ item.desc }} </td>
</tr>
First you need to understand what actually your code is doing, you are listening to click at tbody, and on click of that you are setting the flag isClicked as true/false, and all your items are v-if(ed) at single flag i.e., isClicked. So whenever the flag changes every v-if will also react, that is why every description is shown, which definitely is not desired.
Listening to click on tbody is wrong you should listen to click at every tr-s, so that you can actually know which tr is clicked. Now on click of any tr, store the id of that tr and display the description of the respective item.
Well there is a small work around you can actually do in your code to make it work as you wanted.
Here is the modified code.
<tbody v-for="item in items" :key="item.id">
<tr #click="isClicked = item.id">
<td>
{{item.name}}
</td>
<td>
{{item.surname}}
</td>
<td>
{{item.explanation}}
</td>
<td>
<span #click="isDelete(item)">X</span>
</td>
</tr>
<tr v-if="isClicked === item.id">
{{item.desc}}
</tr>
</tbody>
I hope it helps.
Related
I am new to angular. I have a set of rows(tr) in the table generated using *ngFor, for each table row(tr) i have a click event, when user clicks on the tr i have expand/collapse functionality to show the data in table format below the tr
Now when the user clicks on the tr, i am calling an API which provides me the data specific to the clicked tr, I am setting that data in the variable which i am using to display in the expanded tr functionality. Until now everything works fine, but when i click on the next tr the data for the previously clicked tr also gets changed and displays the data for the current tr.
<ng-container *ngFor="let data of pageData">
<tr (click)="toggleTable(data.id)">
<td>{{data.x}}</td>
<td>{{data.y}}</td>
<td>{{data.z}}</td>
<td>{{data.a}}</td>
<td>{{data.b}}</td>
</tr>
<tr *ngIf="data.id===currDataid && openTable">
<td colspan="8" [ngClass]="{'p-2': true}">
<div>
...
<tr *ngFor="let idApiData of allApiData"> //problem here: id API data remains same for all parent tr when the user clicks on any parent tr
</tr>
</div>
</td>
</tr>
</ng-container>
All i need is the tr with *ngIf should display the data specific to the id passed, but when the user clicks on multiple tr, data for all the expanded functionality gets changed and displays data with respect to variable set.
is there any way by which i can maintain the data previously clicked tr ?
Add an expanded property to object to handle the expand collapse functionality. And you have to define a method to filter the details of the clicked row
<ng-container *ngFor="let data of data1">
<tr (click)="data.expanded = !data.expanded">
<td> {{ data.expanded ? '–' : '+'}} {{data.name}} </td>
<td> {{data.place}} </td>
<td> {{data.phone}} </td>
<td> {{data.hobbies}} </td>
<td> {{data.profession}} </td>
</tr>
<ng-container *ngIf="data.expanded">
<tr *ngFor="let details of subData(data)">
<td> {{details.datades.name}} </td>
<td> {{details.datades.hobbies}} </td>
<td> {{details.datades.profession}} </td>
</tr>
</ng-container>
</ng-container>
Filter data using the below function or you can create a pipe for that
subData(data) {
return this.data.filter(x => x.id=== data.id);
}
I am using vue.js library for front-end development.
I came a cross a scenario where my JavaScript method returns a list, which has objects, object's number of properties can change each time after method execution.
example my list can contain these type of objects in 2 different executions.
var obj = {
Name : "John",
2020-Jan: 1,
2020-Jul: 2
}var obj = {
Name: "John",
2020-Jan: 1,
2020-Jul: 2,
2021-Jan: 3,
2021-Jul: 4
}
Since Property name is dynamically changes is there any way to bind to HTML ?
<div >
<table>
<thead>
<tr>
<th v-for ="row in Result.Headers">
{{row}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="item in Result.Data ">
<td>
{{item.2020-Jan}} // Because don't know the number of properties until run time
</td> // No of <td/>'s can change on no of properties.
<td> // exactly don't know how many <td>'s needed there.
{{item.2020-Jul}}
</td> <td>
{{item.2021-Jan}}
</td>
</tr>
</tbody>
</table>
</div>
Is there way to bind these type of object to fronted in vue.js ?
You need to loop over the item's keys again. This will show all the values in the object
<tbody>
<tr v-for="item in Result.Data ">
<td v-for="(value, key, index) in item">
{{value}}
</td>
</tr>
</tbody>
If you want to filter some of them, for instance check that the keys are valid dates you need to add a v-if and use Date.parse to check for this.
<tbody>
<tr v-for="item in Result.Data ">
<td v-for="(value, key, index) in item" v-if="Date.parse(key) !== NaN">
{{value}}
</td>
</tr>
</tbody>
if u wana show all attr-> u can use this:
<ul v-for="item in Result ">
<li v-for="(value,key,index) in item">{{value}}</li>
</ul>
if u wana show all days u can use v-if and compute to complete youself fillter
<div id="app">
<ul v-for="item in Result" >
<li v-for="(value,key,index) in item" v-if="canShow(key)"> index:{{index}}------ key: {{key}} ------ value:{{value}} </li>
</ul>
</div>
<script>
var vue=new Vue({
el:'#app',
data:{
Result:[{
name: 'SkyManss',
2020-Jan: 1,
2020-Jul: 2
},{
name: 'SkyManss2',
2020-Jan: 1,
2020-Jul: 2,
2021-Jan: 3,
2021-Jul: 4
}]
},
computed:{
canShow(){
return function(skey){
return skey.indexOf('-') > -1;
}
}
}
});
</script>
after some research and some of your suggestions I came up with an answer.
<div>
<table>
<thead>
<tr>
<th v-for ="row in Result.Headers">
{{row}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="item in Result.Data ">
<td v-for="row in Result.Headers">
{{item[row]}}
</td>
</tr>
</tbody>
</table>
</div>
Javascript code
this.Result.Headers = Object.keys(result.data[0]);
this.Result.Data = result.data;
But this code only worked for the first time. second time data didn't get updated. So I updated JavaScript code to following code.
Vue.set(self.Result, 'Headers', []);
Vue.set(self.Result, 'Result', []);
this.Result.Headers = Object.keys(result.data[0]);
this.Result.Data = result.data;
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. That I got to know from following post.
vue.js is not updating the DOM after updating the array
Thank You All !!!
I am trying to make a table that, when you click a button, it needs to display the row directly beneath it.
I have had a look at this post, but I could not find an answer from it.
When I have it like below, it works, but the problem is, it displays all other hidden rows since they all share the same collapse variable.
This is the working example, but not 100% correct:
<table>
<thead>
<th>Path out of this queue</th>
<th *ngFor="let role of roles">{{role.RoleName}}</th>>
</thead>
<tbody>
<ng-container *ngFor="let queue of workQueues; let i = index">
<tr>
<td><button (click)="collapse=!collapse">{{queue.WorkQueueName}}</button></td>
<td *ngFor="let role of roles">
<input type="checkbox" />
</td>
</tr>
<tr *ngIf="collapse">
Yay...
</tr>
</ng-container>
</tbody>
I thought I would be able to make the collapse variable unique, by appending the i, which is the index, to it, but then I get the following error:
Parser Error: Got interpolation ({{}}) where expression was expected
Here is my attempt:
<table>
<thead>
<th>Path out of this queue</th>
<th *ngFor="let role of roles">{{role.RoleName}}</th>>
</thead>
<tbody>
<ng-container *ngFor="let queue of workQueues; let i = index">
<tr>
<td><button (click)="{{collapse+i}}={{!collapse+i}}">{{queue.WorkQueueName}}</button></td>
<td *ngFor="let role of roles">
<input type="checkbox" />
</td>
</tr>
<tr *ngIf="{{collapse+i}}">
Yay...
</tr>
</ng-container>
</tbody>
Specifically, in my (click) event, how can I make a unique variable that could be used?
(click)="{{collapse+i}}={{!collapse+i}}"
should be
(click)="this[collapse+i] = !this[collapse+i]"
This allows you to use an indexer to obtain the field on the component. If it actually works depends on how you have collapse fields defined on your component.
Personally I would prefer extending the type contained in the workQueues array with an additional field.
(click)="queue.collapsed = !queue.collapsed"
...
<tr *ngIf="queue.collapsed">
An other alternative is to define a new field in the *ngFor.
<ng-container *ngFor="let queue of workQueues; let i = index; let isCollapsed = true">
<tr>
<td><button (click)="isCollapsed = !isCollapsed">{{queue.WorkQueueName}}</button></td>
<td *ngFor="let role of roles">
<input type="checkbox" />
</td>
</tr>
<tr *ngIf="!isCollapsed">
Yay...
</tr>
</ng-container>
stackblitz
I have a ng-repeat and I need to run a function to get some additional data/few calculations for every row.
Something like this
<div id="complaintstable">
<table>
<tr ui-sref="complaints.details({ id: item.Id })" ng-repeat="item in list" class="item" ng-class-odd="'odd'" ng-class-even="'even'" ng-init="rowInit(item)">
<td>
{{item.ConsumerName}}
</td>
<td>
{{dayspan}}
</td>
<td>
{{item.ConsumerCity}}
</td>
<td>
{{item.Agent.First_Name}} {{item.Agent.Last_Name}}
</td>
<td>
{{compname}}
</td>
<td>
{{item.DateOpenedDisplay}}
</td>
</tr>
</table>
</div>
And then in the directives controller
$scope.rowInit = function (row) {
$scope.dayspan = someAJAXCall();
$scope.compname = someCalculation();
console.log("here");
}
Obviously the problem with this is that rowInit gets evaluated in the controller scope and no in the ng-repeat scope, so dayspan and compname get overwritten. How would I go about evaluating rowInit in the ng-repeat scope?
Iterate your list and do the calc for each row, add the data to the row, then display item.compname and item.dayspan in your markup.
$scope.list.forEach(
function(item) {
item.dayspan = someAJAXCall();
item.compname = someCalc();
}
);
Note, a boat load of AJAX calls concurrently is problematic - but that's not a problem asked about in your question.
I try to make a version of "tic tac toe" with AngularJS and be as minimalistic as possible. The only solution for my problem is to assign every button a unique ID (f+i).
HTML
<table>
<tr ng-repeat="f in [5,10,15]">
<!-- numbers chosen for unique combos-->
<td ng-repeat="i in [0,1,2]">
<button ng-click="toTrue()" >
<div >
{{getXO()}}
</div>
</button>
</td>
</tr>
</table>
JavaScript
$scope.XObool=false;
$scope.toTrue = function() {
if(!$scope.XObool){
$scope.XObool=true;
}
else if($scope.XObool) {
$scope.XObool=false;
}
};
$scope.getXO = function(){
if($scope.XObool){
return 'X';
}
else {
return 'O';
}
};
ng-repeat gives you several variables to work with, namely $index. In your case you'll want something like:
<button id="{{$index}}" ...>
More info on the ng-repeat docs.
Second Option
Use the f and i variables to create unique IDs.
<table ng-app>
<tr ng-repeat="f in [5,10,15]" data-id="{{$index}}">
<td ng-repeat="i in [0,1,2]">
<button id={{'id' + (i+f)}} ng-click="toTrue()">
{{'id'+(i+f)}}
</button>
</td>
</tr>
</table>
Here's a demo.
You don't need to assign each button a unique ID.
Instead, you can pass your f and i variables into your functions to track the board state:
<table>
<tr ng-repeat="f in [0,1,2]">
<!-- numbers chosen for unique combos-->
<td ng-repeat="i in [0,1,2]">
<button ng-click="setState(f, i)">
<div >
{{ getXO(f,i) }}
</div>
</button>
</td>
</tr>
</table>
Working fiddle here: http://jsfiddle.net/m76w3kf5/
try markup binding with {{}} or $index
<div id="{{someObject.id}}" class="some-class" ng-repeat="f in [ array ]">
..
</div>
or a slightly extended example with $index, listing thumbnails in array with clicks to reference position by index
<tr ng-repeat="array in thumbnails track by $index">
<td ng-repeat="object in array track by object.id"
ng-click="tableClickHandler($index, object)">
<img class="user-thumbnail" src="{{object.src}}">
</td>
</tr>