Add a row from one table to another in LWC - javascript

I am very new to LWC and Javascript. I have an LWC component with a search bar and a table (created in HTML file) showing the search result. I want to add another column in the table with a button on each row which allows me to add that whole row into another table in other component.
This is my HTML file.
' <template>
<lightning-card title="Account Search Bar" icon-name="standard:account">
<lightning-input type="search" onchange={handleKeyChange} class="slds-m-bottom_small" label="Search"
value={accountName}>
</lightning-input>
<table id="table">
<thead>
<tr>
<th class="" scope="col">
<div class="slds-truncate" title="Account Name">Name</div>
</th>
<th class="" scope="col">
<div class="slds-truncate" title="Account Phone">Phone</div>
</th>
<th class="" scope="col">
<div class="slds-truncate" title="Select Account">Select Account</div>
</th>
</tr>
</thead>
<tbody>
<template for:each={accountList} for:item="accountObj" for:index="index">
<tr class="table" key={accountObj.Id}>
<td class="name">{accountObj.Name}</td>
<td class="phone">{accountObj.Phone}</td>
<td class="addButton"><input type="submit" value="Add" onclick={addField}></td>
</tr>
</template>
</tbody>
</table>
</lightning-card>
'
I have added the buttons nd I am trying to get the data of the row from which the button is clicked. But I am getting 'undefined' in every object.
This is my js file.
import { LightningElement,wire,track } from 'lwc';
import getAccounts from '#salesforce/apex/AccountSearchCls.getAccounts';
const DELAY = 300;
export default class TestComponentSearch extends LightningElement {
accountName='';
#track accountList =[];
#track buttonId=1;
#wire(getAccounts,{actName:'$accountName'})
retrieveAccouts({error,data}){
if(data){
this.accountList = data;
}
else if(error){
}
}
handleKeyChange(event){
const searchString= event.target.value;
window.clearTimeout(this.delayTimeout);
this.delayTimeout = setTimeout(()=>{
this.accountName =searchString;
},DELAY);
}
addField()
{
console.log("In addField");
var table = document.getElementById('table');
selected = table.getElementsByClassName('tbody')[0];
var rows= selected.getelEmentsByTagName('tr');
}
}
I am not sure what is the problem or if this is the right way to do it. I would appreciate if anyone helps me in it.

Add an event parameter to addField method get the selector from which the button was clicked and try to find the parent row using the table class that you have defined already and process the columns using innerText.
addField(event)
{
let rows=event.target.closest("tr.table");
let cols=rows.querySelectorAll('td');
let name=cols[0].innerText;
let phone=cols[1].innerText;
alert('Name :'+name+' Phone :'+phone);
}

I am not sure about your complete use case. But, the use case you mentioned is achievable through lightning base components and no need to use table tags.
HTML markup
<lightning-input type="search" value={searchStr} onchange={searchChangeHandler}>
</lightning-input>
<lightning-datatable columns={columns} data={data} key-field="Id"
onrowaction={handleRowAction}>
</lightning-datatable>
</template>
Javascript code
// MyScratchComp.js
import { LightningElement, wire } from 'lwc';
import getAccounts from '#salesforce/apex/LWCHelper.getRecords';
export default class MyScratchComp extends LightningElement {
data;
connectedCallback() {
this.initDefaultValues();
this.getData();
}
initDefaultValues() {
this.searchStr = 'Test';
this.columns = [{
label : 'Name',
fieldName : 'Name',
type : 'Text'
},{
label : 'Industry',
fieldName : 'Industry',
type : 'Text'
},{
label : 'PhotoURL',
fieldName : 'PhotoUrl',
type : 'Text'
},{
label : 'Action',
type : 'button',
typeAttributes : {
label : 'click here'
}
}];
}
searchChangeHandler(event) {
this.searchStr = event.target.value;
this.getData();
}
async getData() {
try {
this.data = await getAccounts(
{
searchQuery : `Select Id, Name, Industry, PhotoURL From Account Where name like '%${this.searchStr}%' LIMIT 10`
}
);
}
catch(e) {
console.error(JSON.stringify(e));
}
}
handleRowAction(event) {
alert('Row selected - ' + JSON.stringify(event.detail.row));
}
}
Apex code
/* LWCHelper Apex class */
public with sharing class LWCHelper {
#AuraEnabled
public static List<SObject> getRecords(String searchQuery){
try {
return Database.query(searchQuery);
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}

Related

vuetify v-radio doesn't select one radio in table nuxt js

so i try to make a multiple grid radio with condition it select one radio button and save it as json with key: question and value: column where the radio buttoin selected
<template>
<v-radio-group class="mt-0" v-model="answer" :rules="answerRule">
<thead>
<tr>
<th>Pilihan</th>
<th v-for="(option, keys) in columns" :key="keys + 'A'">
{{ option.value }}
</th>
</tr>
<tr v-for="(option, keys) in rowsCols" :key="keys + 'C'">
<th>{{ option.value }}</th>
<td
v-for="(optioncol, keys) in option.columns"
:key="keys + 'B'"
>
<v-radio-group
v-model="answer"
#change="update"
:rules="answerRule"
>
<v-radio
class="radioA"
solo
v-bind:key="option.id"
:value="optioncol"
>
</v-radio>
</v-radio-group>
</td>
</tr>
</thead>
<tbody></tbody>
</v-radio-group>
</template>
here how it looks in the browser but it selected all row
[![enter image description here][1]][1]
here is my script on how to load the data and try to save the json...
export default {
props: ['question'],
data() {
return {
rows: this.question.options,
answer: [],
columns: this.question.optionsColumns,
answerRule: [],
}
},
methods: {
async update() {
try {
let payload = {
questionId: this.question.id,
value: this.answer,
questionName: this.question.question,
}
//update question options
await this.$store.commit('answers/update', payload)
} catch (err) {
this.$store.commit('alerts/show', {
type: 'error',
message: err.response
? this.$t(err.response.data.message)
: this.$t('SERVER_ERROR'),
})
}
},
},
beforeMount() {
if (this.question.required) {
this.answerRule.push(
(v) => v.length > 0 || this.$t('QUESTION_REQUIRED')
)
}
},
}
</script>
Any help on that? because i've try and still cannot figure it out
Here is the Data that i already change into new format :
row:[{id:1,value:"asdasd",columns:[a,b,c]},{id:2,value:"asdasd",columns:[a,b,c]}{id:3,value:"asdasd"}{id:1,value:"asdasd",columns:[a,b,c]}]
yet i still got the same problem
[1]: https://i.stack.imgur.com/6fsIF.png

Datatables.net Server-Side pagination with Spring: How to select single row's ID?

I'm working on a web app with React front and Spring Boot back. I'm using datatables with server-side pagination (spring controller and all that). It works to see the table and different pages. However, now I would like to click on the row to select its ID. I tried to follow different examples from datatables, but it always returns empty. I also installed datatables.net-select-bs4 via npm, maybe I can use it somehow? Sorry I'm new to datatables.
What I have now (front):
import React, { Component } from 'react'
import '../dataTables.min.css';
const $ = require('jquery');
$.DataTable = require('datatables.net-bs4');
export default class Table extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
var selected = [];
var table = $('#paginatedTable').DataTable( {
"dom": '<"data-table-wrapper"t>',
"processing": true,
"serverSide": true,
"pageLength": 5,
"ajax": {
"url": "/map-runner/api/calculations",
"data": function (data) {
}},
"columns": this.props.columns,
"rowCallback": function(row, data) {
if ( $.inArray(data.DT_RowId, selected) !== -1 ) {
$(row).addClass('selected');
}
}
});
$('#paginatedTable tbody').on('click', 'tr', function () {
var id = this.id;
var index = $.inArray(id, selected);
if (index === -1) {
selected.push(id);
} else {
selected.splice(index, 1);
}
$(this).toggleClass('selected');
console.log(table.rows( { selected: true } ));
} );
}
componentWillUnmount() {
$('.data-table-wrapper').find('table').DataTable().destroy(true);
}
render() {
return (
<div class="container">
<div class="starter-template">
<div class="table-responsive">
<table id="paginatedTable" class="table table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Date Create</th>
<th>Date Update</th>
<th>User ID</th>
<th>ID Type</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
)
}
}
I'm not sure 100% but I think that if you want to use this.id you yave to use next property in datatable initialization:
rowId: 'staffId'
staffId is the name of the column that you use for set the id.
Documentation:
https://datatables.net/reference/option/rowId

Angular 8 => Condition : show an else value

I'm trying to filter a default value if no result is found on my query.
I tried using ng-template, but I simply don't manage to do it.
Rather than writing, here are images to better understand :
This is my successful filter : it correctly shows my datas once I filter them in the search box.
However if I try to enter an invalid data as done below :
It simply returns me an empty html table.
Which is not what I'd like to achieve. I'd like it instead to show a message saying : no data found.
How can I do it please?
And here is my source code :
Component :
import {Component, OnInit} from '#angular/core';
import {IProduct} from './product';
#Component({
selector: 'pm-products',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit {
///////////////////////////////////// PROPERTIES //////////////////////////////////////
// String Interpolation
pageTitle = 'Product List';
// Property binding
imageWidth = 50;
imageMargin = 2;
// Event binding
showImage = false;
// Two-way binding
// listFilter = 'cart';
// Filter products
private _listFilter: string;
// Filter Products Array
filteredProducts: IProduct[];
///////////////////////////////////// CONSTRUCTOR //////////////////////////////////////
constructor() {
this.filteredProducts = this.products;
this.listFilter = 'cart';
}
/////////////////////////////////// GETTERS/SETTERS ///////////////////////////////////
get listFilter(): string {
return this._listFilter;
}
set listFilter(value: string) {
this._listFilter = value;
/***
* If there is a list of filtered value, show the list of the filtered values => this.performFilter(this.listFilter)
* Else, (if there is no filtered) return the whole set of products
*/
this.filteredProducts = this.listFilter ? this.performFilter(this.listFilter) : this.products;
}
/////////////////////////////////////// METHODS ///////////////////////////////////////
// Get Products
products: IProduct[] = [
{
productId: 2,
productName: 'Garden Cart',
productCode: 'GDN-0023',
releaseDate: 'March 18, 2019',
description: '15 gallon capacity rolling garden cart',
price: 32.99,
starRating: 4.2,
imageUrl: 'assets/images/garden_cart.png'
},
{
productId: 5,
productName: 'Hammer',
productCode: 'TBX-0048',
releaseDate: 'May 21, 2019',
description: 'Curved claw steel hammer',
price: 8.9,
starRating: 4.8,
imageUrl: 'assets/images/hammer.png'
},
];
performFilter(filterBy: string): IProduct[] {
/**
* filterBy result => to lower case. => case insensitive comparison.
* Return a new array of the filtered productS by the product name,
* by checking if that product name given is an index of the an element in the product array.
*/
filterBy = filterBy.toLowerCase(); // 1.
return this.products.filter((product: IProduct) => product.productName.toLowerCase().indexOf(filterBy) !== - 1); // 2.
}
toggleImage = (): void => {
this.showImage = !this.showImage;
}
////////////////////////////////// LIFECYCLE HOOKS ///////////////////////////////////
ngOnInit(): void {
console.log('hello');
}
}
HTML
<div class="card">
<div class="card-header">{{pageTitle}}</div>
<!-- CARD -->
<div class="card-body">
<div class="row">
<div class="col-md-2"> Filter by:</div>
<div class="col-md-4">
<input [(ngModel)]="listFilter" type="text"/>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h4>Filtered by: {{listFilter}} </h4>
</div>
</div>
<!-- ./CARD -->
<!-- TABLE -->
<div class="table-responsive">
<table *ngIf="products && products.length" class="table">
<thead>
<tr>
<th>
<button (click)="toggleImage()" class="btn btn-primary">{{showImage ? "Hide" : "Show"}} image</button>
</th>
<th>Product</th>
<th>Code</th>
<th>Available</th>
<th>Price</th>
<th>5 Star Rating</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let product of filteredProducts">
<td><img *ngIf="showImage" [src]="product.imageUrl" [title]="product.productName"
[style.width.px]="imageWidth" [style.margin.px]="imageMargin" alt=""></td>
<td>{{product.productName}}</td>
<td>{{product.productCode | lowercase | convertToSpaces: '-'}}</td>
<td>{{product.releaseDate}}</td>
<td>{{product.price | currency: 'EUR':'symbol':'2.2-2'}}</td>
<td>{{product.starRating}}</td>
</tr>
</tbody>
</table>
</div>
<!-- ./TABLE -->
</div>
</div>
Please take care of yourself.
Maybe just include an *ngIf="!filteredProducts.length" wherever you'd like to show your message.
ex.
<tr *ngFor="let product of filteredProducts">
//Your table stuff
</tr>
<tr *ngIf="!filteredProducts.length">
<td colspan="6">No data found</td>
</tr>
You have to add tr contains string no data found after ngfor tr
<tr *ngFor="let product of filteredProducts">
<td><img *ngIf="showImage" [src]="product.imageUrl" [title]="product.productName"
[style.width.px]="imageWidth" [style.margin.px]="imageMargin" alt=""></td>
<td>{{product.productName}}</td>
<td>{{product.productCode | lowercase | convertToSpaces: '-'}}</td>
<td>{{product.releaseDate}}</td>
<td>{{product.price | currency: 'EUR':'symbol':'2.2-2'}}</td>
<td>{{product.starRating}}</td>
</tr>
<tr *ngIf="filteredProducts.length === 0 " >
<td colspan="6" >Your message here </td>
</tr>
You can simply add a row that appears only when the list is empty and the search bar has any value. like this:
<tr *ngFor="let product of filteredProducts">...</tr>
<tr col-span ="6" *ngIf="!filteredProducts.length && listFilter.length"> uh oh,
could not find what you searched for</tr>

React: e.target.getAttribute ("id") works 20% of the time?

I'm new to React and been trying to fix this for hours. I'm trying to get the id of the button which is clicked But this only gets the id around 20% of the time and the rest it returns nulltext. I have no idea what else to do. I have tried different binding methods but haven't been able to make it work.
I simplified the code here and put it below.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Popupright extends React.Component {
popupnewshow = (e) => {
let ids = e.target.getAttribute("id") + "text";
console.log(ids)
let elements = document.getElementsByClassName('poprighttext showtext');
while(elements.length > 0){
elements[0].classList.remove('showtext');
};
document.getElementById(ids).classList.toggle("showtext");
};
render() {
return (
<div>
<table className="table-bordered">
<tbody>
<tr className="table-samewidth">
<td className="td-general"><button className="popup" id="xxx" onClick={this.popupnewshow}><div className="popuptitle">xxx</div></button></td>
</tr>
<tr className="table-samewidth">
<td className="td-general"><button className="popup" id="yyy" onClick={this.popupnewshow}><div className="popuptitle">yyy</div></button></td>
</tr>
<tr className="table-samewidth">
<td className="td-general"><button className="popup" id="zzz" onClick={this.popupnewshow}><div className="popuptitle">zzz</div></button></td>
</tr>
</tbody>
</table>
<div id="xxxtext" className="poprighttext">
<p>xxx.</p>
</div>
<div id="yyytext" className="poprighttext">
<p>yyy</p>
</div>
<div id="zzztext" className="poprighttext">
<p>zzz</p>
</div>
</div>
);
}
}
export default Popupright;
Console Image: The buttons should give the id xxxtext, yyytext or zzztext depending on the button clicked but this only works 20% of the time. The rest it returns nulltext and after some clicks it returns again the proper id:
Using e.currentTarget.id should solve your issue.
e.target holds the element that you clicked on, but e.currentTarget will hold the element where you have bind the handler.
When you use e.currentTarget:
<button className="popup" id="xxx" onClick={this.popupnewshow}>
<div className="popuptitle">xxx</div><!-- clicking on here:
e.currentTarget.id is xxx -->
</button>
When you use e.target:
<button className="popup" id="xxx" onClick={this.popupnewshow}>
<div className="popuptitle">xxx</div><!-- clicking on here:
there's no id here (the clicked element id) -->
</button>
In general better to avoid direct DOM manipulation like remove. Also you can get the id directly rather than from the event:
const toggleItem = (arrayOfObjects, objectId) => {
//some implementation of toggle object's vislble prop based on id property in array
}
class Popupright extends React.Component {
state = {
popups: [
{id: 'xxx', text: 'xxxtext', visible: false},
{id: 'yyy', text: 'yyytext', visible: false},
...
]
}
togglePopup = id => {
this.setState(prevState => ({
popups: [...toggleItem(prevState.popups, id)]
})
}
render() {
return (
<table>
...
<td>
<button onClick={() => this.togglePopup('xxx')} />
</td>
...
</table>
<div className="popupsWrap">
{this.state.popups.map(popup => {
if (popup.visible) {
return (
<div className="poprighttext">{popup.text}</div>
)
}
}}
</div>
...

Issue with Aurelia class.bind with checked.bind

I am trying to use class.bind in a way that makes it dependent on checked.bind.
My use case is pretty simple. I have a list of items, displayed using a table. Each row of this table has a checkbox. I want to mark a row as "selected" whenever the corresponding checkbox (of the row) is checked.
For this I have used following binding:
<table class="table table-striped table-hover table-responsive">
<tbody>
<tr repeat.for="item of items" class.bind="$parent.selectedItems.indexOf(item)>-1?'info':''">
<td>
<input type="checkbox" checked.bind="$parent.selectedItems" model.bind="item" />
</td>
<td>${item.id}</td>
<td>${item.name}</td>
</tr>
</tbody>
</table>
However, the same doesn't work as intended, and this can be seen in this plunk.
As a workaround I used a getter with #computedFrom('selectedItems', 'items') and/or declarePropertyDependencies(App, 'classes', ['selectedItems', 'items']); , as follows:
import {computedFrom, declarePropertyDependencies} from "aurelia-framework";
export class App {
...
#computedFrom('selectedItems', 'items')
get classes() {
const self = this;
const retval= self.items.map((item: any) => {
return self.selectedItems.indexOf(item) > -1 ? "info" : "";
});
return retval;
}
}
//declarePropertyDependencies(App, 'classes', ['selectedItems', 'items']);
However, this too does not work as can be seen here in this workaround plunk.
It only works, if none of #computedFrom and/or declarePropertyDependencies is used, and that obviously involves dirty-checking.
Is there a clean way to do this?
The binding system will reevaluate the class binding expression class.bind="$parent.selectedItems.indexOf(item)>-1?'info':''" anytime a property used in the expression changes. The selectedItems property never changes, it remains the same array instance. Understandably this is a little confusing because the array instance is mutating. Here's a workaround you can use: add selectedItems.length to the binding expression... we know it will change when items are pushed/popped from the array.
Here's an example: https://gist.run?id=09d32941842352ff0025
app.html
<template>
<p>${message}</p>
<table class="table table-striped table-hover table-responsive">
<tbody>
<tr repeat.for="item of items" class.bind="selectedItems.length === 0 || selectedItems.indexOf(item) === -1 ? '' : 'info'">
<td>
<input type="checkbox" checked.bind="selectedItems" model.bind="item" />
</td>
<td>${item.id}</td>
<td>${item.name}</td>
</tr>
</tbody>
</table>
${selectedItems.length} items selected
</template>
app.js
export class App {
constructor(router) {
this.message = "Hello World!";
this.items = [{
id: 1,
name: "A"
}, {
id: 2,
name: "B"
}, {
id: 3,
name: "C"
}];
this.selectedItems=[];
}
}

Categories