How to add row to table dynamically on button click with Angular2? - javascript

I am working on Angular2 project and want to want to add rows to table dynamically, when a button is clicked. I know that *ngFor can be used to add rows dynamically, but I am interested if it is possible to use *ngFor only if button is clicked.

usually you use *ngFor to iterate over an array (generally of objects). So if your array is called "data" yo can have some like
<table *ngIf="data.length"> <!--Don't show nothing if no data-->
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr *ngFor="let item of data">
<td>{{item.firstName}}</td>
<td>{{item.lastName}}</td>
<td>{{item.age}}</td>
</tr>
</table>
Where you has a variable data like
data:any[]=[] //don't forget initialize!
A button can execute some like
onClick()
{
this.data.push({firstName:"firstName",lastName:"Last Name",age:18})
}

A quick sample:
<button (onClick)="sth=!sth">click</button>
<div *ngIf="sth">
<div *ngFor="...">
...
</div>
</div>

Related

Show and hide Blade table rows with Alpine JS

I am using Blade with Laravel 8.x and would like to show or hide table rows using x-show in Alpine JS (v3).
<table>
#foreach($items as $item)
<div x-data="{show: false}">
<tr>
<td>
<button type="button" x-on:click="show = !show">
Toggle Show
</button>
</td>
</tr>
<tr x-show="show">
<td>{{ $item->name }}</td>
</tr>
</div>
#endforeach
</table>
When I do this however, I get a ReferenceError: show is not defined error. Is this approach possible without using x-for as I would like to still have access to the Blade variables within the rows.
The problem is that you cannot place <div> element in a <table> element. When browser executes your code, it places the <div> outside <table> element, so in <tr> the show is not defined.
You can workaround this by replacing <div> tag with <tbody> or making the button and item name in one row as separate <td> and appending x-data to <tr>.

hide an item in the parent based on the data in the child loop in Angular

I have the following HTML code snippet where i am looping through my json response to display the data. It is a nested loop with *ngIf as in the HMTL below. What i need is based on the value of one of the items in the child loop i want to hide/show an item in the parent loop.
Basically i have mtr.eu inside the child loop which is an input type.Initially it will be empty and when the user enter any value in it, i want to show the item in the parent shown below. What would be the best suitable way to achieve this.
<div class="row accordian-head" accordion-heading>
<span class="w20">MPRN: {{header.gpr}}</span>
<span class="w20">Meter ID: {{header.num}}</span>
<span class="w20" *ngIf="mtr.eu">New data added</span>
</div>
<div class="accordian-inner-content">
<table class="table table-borderless">
<thead>
<tr class="meter-reading-header">
<th scope="col">Last read date</th>
<th scope="col">Last meter read</th>
<th scope="col">New reading (kWh)</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let mtr of header.mtrs" class="meter-reading-content">
<td>{{mtr.lrd}}</td>
<td>{{mtr.lrv}}</td>
<td>
<input type="number" name="newReading" class="form-control new-reading-input"
placeholder="eg. 12345" [(ngModel)]="mtr.eu">
</td>
</tr>
</tbody>
</table>
</div>
</accordion-group>
You can not use the child variables in the parent node. Instead, you can make a getter where you can check the records which has mu value. based on that getter value you can show the record on the parent element.
The only thing you need to do is write a function and pass your list of items into it. inside that use filter to check for required data(in your case its mt.mu). then return the data. Based on returned data you can show the element.
But when you call any function from the template it calls the function recursively.
So as a best practice I would prefer using pipes to do the same logic. Which makes my code much better.
I hope this helps you to achieve your need.

Applying css to the table row dynamically created with Vue.js

So I have table, where rows are created dynamically using v-for:
<table>
<tr><th class='name'>Name</th><th>Surname</th></tr>
<tr v-for='data in datas'><td class='name'>#{{data.name}}</td><td>#{{data.surname}}</td></tr>
</table>
And then, using jQuery, I want to hide the column with class 'name', but when I make
$('.name').hide();
Only header disappears. I suppose it's because the row is made dynamically, but how could I handle this?
I've tried:
making each() function on all elements with this class,
writing script like .css('display','none'), not hide()
but it didn't help. Strange, but alert() in each() fires each time it should, but hide() ignores added elements
More concrete data:
the table itself:
<table class="striped bordered responsive">
<thead>
<tr><th class="stat-table-creatives" colspan="2">Creative info</th><th>Impressions</th><th>Clicks</th><th>CTR</th><th>CPC</th><th>Price per unit</th><th>Sum</th><th class="stat-table-date">Date</th></tr>
</thead>
<tbody>
<tr v-for="stat in stats"><td class="stat-table-creatives">#{{ stat.creativeTitle }}</td><td class="stat-table-creatives">#{{ stat.creativeType }}</td><td>#{{ stat.impressions }}</td><td>#{{ stat.clicks }}</td><td>#{{ stat.ctr }}</td><td>#{{ stat.cpc }}</td><td>#{{ stat.client_price }}</td><td>#{{ stat.sum }}</td><td class="stat-table-date">#{{ stat.date }}</td></tr>
</tbody>
</table>
function called on button click
getJsonForStats: function(filters){
if((filters[0]=='creative')||(filters[1]=='creative')){
$('.stat-table-creatives').show();
} else {
$('.stat-table-creatives').hide();
}
if((filters[0]=='date')||(filters[1]=='date')){
$('.stat-table-date').show();
} else {
$('.stat-table-date').hide();
}
});
The function is called from another function, which is called on v-on:click
jQuery works just fine with Vue dynamically created table. Check this fiddle: https://jsfiddle.net/crabbly/9o69f2yr/
I believe the problem will be on how your application is loading, your filters, etc.
Depending on how you are trying to access the table rows, you may want to make sure that DOM is completely updated before querying it. You can use Vue's nextTick (http://vuejs.org/api/#Vue-nextTick).
Inside your Vue method, before you try to hide the rows, in your example, calling getJsonForStats method, you can do
this.$nextTick(function() {
//DOM updated and ready to rock and roll
this.getJsonForStats(..your filter params...);
}
suppose it's because you use the same class for <th> and <td>
try <th class="name-th"> and then $(".name-th").hide(); $(".name").hide();
maybe it will help
UPD:
I don't know.. this code is working perfectly:
<table id="tab" border='1'>
<tr><th class='name'>Name</th><th>Surname</th></tr>
<tr><td class='name'>n</td><td>s</td></tr>
</table>
<input id="click" type="button" value="Hide" />
js:
$(document).ready(function(){
$("#click").click(function(){
$("#tab").find(".name").hide();
});
});

Get multiple Angular.js controllers for multilple <td> within the same <tr>

I'm trying to add multiple angular controllers within the same tr tag, the problem is that chrome rewrites the table to standardize it, and there is no element between tr and td in the HTML table hierarchy.
Here is what I currently have, each color represents a different controller to call.
The final aim is to have a table like below, with a different controller for one or multiple td, instead or multiple trs
I know I could use a global controller to handle all the data, or use multiple div elements with a fixed width to achieve this, but I'd prefer using a single tr table.
Here is the code :
<table>
<tr>
<div ng-controller="testController">
<td>{{testcontrollerscopevalue}}</td> <!-- empty when displayed -->
<td>{{testcontrollerscopevalue2}}</td> <!-- empty when displayed -->
<td>{{testcontrollerscopevalue3}}</td> <!-- empty when displayed -->
</div>
<div ng-controller="testController2">
<td>{{testcontroller2scopevalue}}</td> <!-- empty when displayed -->
</div>
</tr>
</table>
The following works :
<table ng-controller="testController">
<tr>
<td>{{testcontrollerscopevalue}}</td> <!-- set when displayed-->
</tr>
</table>
Here is what chrome generates :
<body>
<div ng-controller="testController"></div>
<div ng-controller="testController2"></div>
<table>
<tbody>
<tr>
<td>{{testcontrollerscopevalue}}</td> <!-- out of scope-->
<td>{{testcontrollerscopevalue2}}</td> <!-- out of scope-->
<td>{{testcontrollerscopevalue3}}</td> <!-- out of scope-->
<td>{{testcontroller2scopevalue1}}</td> <!-- out of scope-->
</tr>
</tbody>
</table>
<table ng-controller="testController">
<tbody>
<tr>
<td>{{testcontrollerscopevalue}}</td> <!-- set -->
</tr>
</tbody>
</table>
Is there any way I can achieve this ?
Is there any tag I could use instead of div to get this to work?
Thanks,
As we discussed at length in the chat session, This is a case where you are best served by using the ControllerAs Syntax, and wrapping the <table> element in multiple <div> elements holding each controller's logic.
My suggestion was something similar to the following code:
<div ng-controller="testController as tc">
<div ng-controller="testController2 as tc2">
<table>
<tbody>
<tr>
<td>{{tc1.testcontrollervalue}}</td>
<td>{{tc1.testcontrollervalue2}}</td>
<td>{{tc1.testcontrollervalue3}}</td>
<td>{{tc2.testcontroller2value1}}</td>
</tr>
</tbody>
</table>
</div>
</div>
For this to work, your variables need to be converted to be properties of the controller, rather than properties of $scope.
var tc1 = this; //consistent reference to controller object
SomeService.get(function(data) {
tc1.someProperty = data;
});
You should probably just do:
<td ng-controller="testController2">{{testcontrollerscopevalue}}</td>
If you really need the div:
<td><div ng-controller="testController2">{{testcontrollerscopevalue}}</div></td>

dynamically close and open table rows

I have a table on my site that contains the options for different products. I didn't put it there, the ecommerce platform did. It lists the options in a row in the table. The table looks like this:
<div class="attributes">
<table>
<tbody>
<tr>
<td>Size:</td>
<td> </td>
<td><select><option>Sizes here</option></select></td>
</tr>
</tbody>
</table>
</div>
Then if there were another option it would be in an additional row with the same markup.
This renders with the label (size in this case) out in front of the <select> box. I want the label above the <select> box and I figured the easiest way to accomplish this would be to close the <tr> and open a new one. Any ideas on how to do this?
EDIT: I should mention that the ecommerce platform generates the html and I do not have access to the source code
Assuming that it follows that exact structure, try this:
$(".attributes select").each(function(){
$label = $(this).parent().prev().prev();
$label.parent().before("<tr></tr>");
$label.parent().prev().append($label.clone());
$label.remove();
$(this).parent().prev().remove();
});
Here's an example: Demo
Like so?
<div class="attributes">
<table>
<tbody>
<tr>
<td>Size:</td>
</tr>
<tr>
<td><select><option>Sizes here</option></select></td>
</tr>
</tbody>
</table>
</div>
I think you have to do it in two steps:
remove the elements:
$('.attributes').find('tr:first-child').remove();
$('.attributes').find('tr:first-child').remove();
2.append them back in the same place
$('.attributes').find('tr:first-child').before('<tr><td>Sizes here</td></tr>');

Categories