Changing a HTML element dynamically - javascript

I have a side drawer where I'm showing the current cart products selected by the user. Initially, I have a <p> tag saying the cart is empty. However, I want to remove it if the cart has items inside. I'm using an OOP approach to design this page. See below the class I'm working with.
I tried to use an if statement to condition the <p> tag but this seems the wrong approach. Anyone has a better way to do this. See screenshot of the cart in the UI and code below:
class SideCartDrawer {
cartProducts = [];
constructor() {
this.productInCartEl = document.getElementById('item-cart-template');
}
addToCart(product) {
const updatedProducts = [...this.cartProducts];
updatedProducts.push(product);
this.cartProducts = updatedProducts;
this.renderCart();
}
renderCart() {
const cartListHook = document.getElementById('cart-items-list');
let cartEl = null;
if (this.cartProducts.length === 0) {
cartEl = '<h2>You Cart is Empty</h2>';
} else {
const productInCartElTemplate = document.importNode(
this.productInCartEl.content,
true
);
cartEl = productInCartElTemplate.querySelector('.cart-item');
for (let productInCart of this.cartProducts) {
cartEl.querySelector('h3').textContent = productInCart.productName;
cartEl.querySelector('p').textContent = `£ ${productInCart.price}`;
cartEl.querySelector('span').textContent = 1;
}
}
cartListHook.append(cartEl);
}
}
By the way, the <p> should reappear if the cart is back to empty :) !

With how your code is setup, you would want to reset the list on each render. You would do this by totally clearing out #cart-items-list. Here is a deletion method from this question
while (cartListHook.firstChild) {
cartListHook.removeChild(cartListHook.lastChild);
}
But you could use any method to delete the children of an HTML Node. To reiterate, you would put this right after getting the element by its id.
P.S. You probably want to put more code into your for loop, because it seems like it will only create cart-item element even if there are multiple items in this.cartProducts.

Related

svelte-table row select/de-select from Javascript

I am trying to implement row selection functionalty to svelte component I am developing. I am creating component using svelte-table component where I list items from database and the component should allow selection of only two items and remove first row when third is added. Row id's are then recorded in svelte store and passed to an other component. This all works in program level, that is not a problem. The problem is to highlight correct rows in the table so that user is on map which rows are selected. I can get the highlighting working using classNameRowSelected property on svelte-table but the problem is removing the highlighting from the first selected row when that third row is selected. I seem to fail find any example or reference how to do this from Javasctipt...
Here is my SvelteTable element:
<SvelteTable
columns="{COLUMNS}"
rows="{rows}"
rowKey="key"
selectOnClick="{true}"
on:clickRow="{rowSelected}"
classNameTable={['table table-striped']}
classNameThead={['table-primary']}
classNameRowSelected="row-selected"
/>
<style>
:global(.row-selected) {
background-color: #f8c;
}
</style>
and the rowSelected function then row is clicked:
function rowSelected(event)
{
let found = false;
compare_tests.forEach((test,index, compare_tests) => {
console.log(test);
if (test == event.detail.row.key)
{
found = true;
event.detail.row.selected = false;
compare_tests[index] = "";
}
});
if (!found)
{
compare_tests.shift();
compare_tests.push(event.detail.row.key);
event.detail.row.selected = true;
found = false;
}
$testids = compare_tests;
}
Ideally there is some function/property on "row" parameter that I can use to de-select the row based on row.key before I shift it away from the array.
I am relatively new to JavaScript and Svelte so any help where to find information on how to accomplish this would be appreciated.
By using selected (‡ optional array of key values of selected rows) instead of selectOnClick this would be a way >> REPL
<script>
import SvelteTable from "svelte-table";
import {rows, columns} from './data'
let selectedRowIds = []
function handleRowClick(event) {
const rowId = event.detail.row.id
if(selectedRowIds.includes(rowId)) {
selectedRowIds = selectedRowIds.filter(id => id !== rowId)
} else {
selectedRowIds = [rowId, ...selectedRowIds].slice(0,2)
}
}
</script>
<SvelteTable columns="{columns}"
rows="{rows}"
rowKey="id"
on:clickRow={handleRowClick}
selected={selectedRowIds}
classNameRowSelected="row-selected"
>
</SvelteTable>
<style>
:global(.row-selected) {
background-color: #f8c;
}
</style>

Lightning Web Component setting dynamic style not working as expected

I'm currently trying to render a specific class across two lightning-badge components that is suppose to change both badges from inverse to success, but am getting this instead:
When the value on the left badge equals the value on the right (so in this case both are 3), they should both be green, otherwise they should both be grey. They should never be seperate colours.
The value on the left increases as the user saves a record and is checked on status of "Completed". For some reason only the class on the second badge is being updated with the new class that includes slds-theme_success. I may be missing something small, but just haven't been able to figure it out. Please see code below:
badgeClass = "slds-badge_inverse slds-var-m-horizontal_x-small slds-col";
get patientsCompleted() {
if(this.records) {
let completedArr = this.records.filter(value => value.fields.Status__c.value == "Completed");
if(completedArr.length === this.patientsTotal) {
this.badgeClass = "slds-badge_inverse slds-theme_success slds-var-m-horizontal_x-small slds-col";
}
return completedArr.length;
}
};
get patientsTotal(){
if(this.records) {
return this.records.length;
}
};
<span class="slds-col_bump-left">
<div class="slds-grid slds-gutters">
<lightning-badge class={badgeClass} label={patientsCompleted}></lightning-badge>
<div class="slds-col"> of </div>
<lightning-badge class={badgeClass} label={patientsTotal}></lightning-badge>
</div>
</span>
Have you tried moving badgeClass to a getter? Something like this:
get patientsCompleted() {
if(this.records) {
let completedArr = this.records.filter(value => value.fields.Status__c.value == "Completed");
// No longer needed
// if(completedArr.length === this.patientsTotal) {
// this.badgeClass = "slds-badge_inverse slds-theme_success slds-var-m-horizontal_x-small slds-col";
// }
return completedArr.length;
}
};
get patientsTotal(){
if(this.records) {
return this.records.length;
}
};
get badgeClass() {
let baseClass = "slds-badge_inverse slds-var-m-horizontal_x-small slds-col";
return this.patientsCompleted === this.patientsTotal ? `${baseClass} slds-theme_success` : `${baseClass}`
}
I suspect LWC field tracking has some precautionary mechanism and didn't trigger the update.
I am not sure but perhaps if 0 records are available you want the badges to remain gray? In that case include this.patientsTotal > 0 in the get badgeClass() {...}.
Happy coding.

How to know/capture the Detail Grid ID of the specific detail grid you are in? (ag-grid javascript)

I have a Master-Detail ag-grid. One column has checkboxes, (checkboxSelection: true). The details grid have a custom status panel with a button. When the user clicks the button in any specific Detail grid, I don't know how to get the SelectedRows from just that one specific detail grid.
The problem is they might leave multiple details displayed/open, and then looping over each Detail Grid will include results from all open grids. I'm trying to isolate to just the grid where the user clicked the button.
I tried looping through all displayed/open detail grids to get the Detail grid ID. But I don't see any info in this that shows me which one they clicked the button in.
I tried in the button component to see if, in the params, there is anything referencing the detailgrid ID that the button is in, but I did not see anything there either.
This is the button component:
function ClickableStatusBarComponent() {}
ClickableStatusBarComponent.prototype.init = function(params)
{
this.params = params;
this.eGui = document.createElement('div');
this.eGui.className = 'ag-name-value';
this.eButton = document.createElement('button');
this.buttonListener = this.onButtonClicked.bind(this);
this.eButton.addEventListener("click", this.buttonListener);
this.eButton.innerHTML = 'Cancel Selected Records <em class="fas fa-check" aria-hidden="true"></em>';
console.log(this.params);
this.eGui.appendChild(this.eButton);
};
ClickableStatusBarComponent.prototype.getGui = function()
{
return this.eGui;
};
ClickableStatusBarComponent.prototype.destroy = function()
{
this.eButton.removeEventListener("click", this.buttonListener);
};
ClickableStatusBarComponent.prototype.onButtonClicked = function()
{
getSelectedRows();
};
Here is the code to loop through and find all open detail grids:
function getSelectedRows()
{
this.gridOptions.api.forEachDetailGridInfo(function(detailGridApi) {
console.log(detailGridApi.id);
});
I was able to work this out, so thought I'd post my answer in case others have the same issue. I'm not sure I took the best approach, but it's seemingly working as I need.
First, I also tried using a custom detail cell renderer, as per the documentation, but ultimately had the same issue. I was able to retrieve the DetailGridID in the detail onGridReady function--but couldn't figure out how to use that variable elsewhere.
So I went back to the code posted above, and when the button was clicked, I do a jquery .closest to find the nearest div with a row-id attribute (which represents the the DetailgridID), then I use that specific ID to get the rows selected in just that detail grid.
Updated button click code:
ClickableStatusBarComponent.prototype.onButtonClicked = function()
{
getSelectedRows(this);
};
Updated getSelectedRow function:
function getSelectedRows(clickedBtn)
{
var detailGridID = $(clickedBtn.eButton).closest('div[row-id]').attr('row-id');
var detailGridInfo = gridOptions.api.getDetailGridInfo(detailGridID);
const selectedNodes = detailGridInfo.api.getSelectedNodes()
const selectedData = selectedNodes.map( function(node) { return node.data })
const selectedDataStringPresentation = selectedData.map( function(node) {return node.UniqueID}).join(', ')
console.log(selectedDataStringPresentation);
}

How to re-factor this NativeScript simple image css swapper

I have a page that displays 3 images, and the user is expected to tap on one, then tap the Next button to continue.
So basically, I am simply adding some CSS to the image when it is tapped.
BUT... my code is ugly, and doesn't keep track of whether they ALREADY have one selected.
onPlanTap: function (args) {
const planImage = args.object;
const planImageSrc = planImage.src;
const planId = plan.id;
this.set("nextButtonOn", true);
var n = planImageSrc.search("off");
// Found, it is off - turn on
if (n > 0) {
var newOnSrc = planId + "-off.png";
planImage.src = newOnSrc;
this.set("currentPlan",planId);
FancyAlertService.showFancySuccess("Plan Secected!", "You have chosen the FREE plan.", "Ok");
}
else {
// It's already on, turn off
var newOnSrc = planId + "-on.png";
planImage.src = newOnSrc;
this.set("currentPlan","");
}
}
[ oh, the css I am adding, simply adds a thick white border to the image ]
I can't figure out how to only have one selected.
Is there some sort of "toggle" feature in NS I am missing, or would I have to write the logic myself? If that's the case, can anyone give me a nudge with some code?
Wrap your images inside some custom object.
class MyImage {
image;
isSelected:boolean;
}
Add data binding:
<ListView [items]="myDataItems" class="list-group">
<ng-template let-item="item" let-i="index">
<Image (tap)="selectImage(item)" class="item.isSelected ? styleA : styleB"></Image>
</ng-template>
</ListView>
And in your component file:
class MyComponent {
myDataItems: Array<MyImage>
selectImage(item: MyImage) {
//deselect all Items
//select this item
}
}

Listjs Make button for last page after filter

list.js Question:
How do you create a div so when you click it, it shows the last page of the pagination, after I have done a filter.
I have a list
var List = new List('list', {
valueNames: ['name'],
page: 5,
plugins: [ListPagination({})]
});
And say this list has 20 pages.
After I apply a filter:
List.filter(function(item) {
if (item.values().category.toLowerCase().indexOf('wordtofilter') > -1) {
return true;
} else {
return false;
}
});
It now has 5 pages. I want to have a button when I click it will take me to the last page.
Currently I can get to the last page of an unfiltered list using this:
$('.go-to-last-page').on('click', function(){
List.show(List.size(), 5);
});
But If i filter my list, and click it, it will attempt to take me to page 20, instead of 5. How do I make it so it takes me to the last page of the filtered list? (page 5)
I had this same problem a few years after your question, so I'm sharing my solution in case it might help someone else:
First I create the first and last page buttons:
<nav>
<button id="btn-first">FIRST</button>
<ul class="pagination"></ul>
<button id="btn-last">LAST</button>
</nav>
Secondly, in js I assign the data attributes that the paging buttons generated by list.js have by default.
const LIST_PAGINATION = 10;
var btn_first = document.getElementById('btn-first');
var btn_last = document.getElementById('btn-last');
btn_first.addEventListener("click",function(e){
btn_first.dataset.i = 1;
btn_first.dataset.page = LIST_PAGINATION;
},false);
btn_last.addEventListener("click",function(e){
let total = list.matchingItems.length; // list.js object in my case I called it "list"
let page = Math.ceil(total / LIST_PAGINATION);
btn_last.dataset.i = page;
btn_last.dataset.page = LIST_PAGINATION;
},false);

Categories