I have a Lightning Component that displays some Categories and the Offers per Category. I want to achieve "Search" in those Categories at first and then at offers. So, what did i try to do:
- Have an attribute to store all the info set by Apex during initialization
- Have another attribute to display all the Categories and Offers (Iterate)
- Based on user's search value, dynamically change the second attribute. Whenever the search string is empty, use the initial attribute to "restore" all the Categories and Offers. Problem: It seems like the 1st attribute, although it is not manipulated through my js code, it seems like it "loses" its value. For example, user searches with "123" which returns no results and i do "component.set" to the second attribute to show zero results. Then user "clears" the search bar and when i use in my Controller the 1st attribute, its value is [] as it is in the second one!
Code below: CMP
<!-- @Author : Christos Kolonis & George Galaios
@Date : 24/03/2021
@Desc : Lightning component that shows the available bundled categories and offers for the current user
-->
<aura:component controller="el_bundledOffersController" implements="lightning:availableForFlowScreens,force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global">
<!-- Register Events -->
<aura:handler name="sampleComponentEvent" event="c:el_OfferMemberConfigurationEvent" action="{!c.handleComponentEvent}"/>
<!--Attributes Definitions -->
<!-- Basket Id -->
<aura:attribute name="basketId" type="String"/>
<aura:attribute name="mapList" type="List" />
<aura:attribute name="initialMapList" type="List" />
<aura:attribute name="value" type="String" default=""/>
<aura:attribute name="selectedOfferId" type="String"/>
<aura:attribute name="offerSelected" type="Boolean" default="false"/>
<aura:attribute name="nextClicked" type="Boolean" default="false"/>
<!--GG-->
<!-- attribute for storing the value of the String searched by the user for the Catalogue-->
<aura:attribute name="searchCatalogueString" type="Boolean" default="false"/>
<!--GG-->
<!-- attribute for storing current Account -->
<aura:attribute name="currentAccount" type="String" />
<!-- attribute for offermembers -->
<aura:attribute name="offerMembersList" type="List" />
<!-- Loading spinner Attribute -->
<aura:attribute name="spinner" type="boolean" default="false"/>
<!-- Attribute to gey out background -->
<aura:attribute name="isModalOpen" type="boolean"/>
<!-- Attribute with stored offers when user presses add -->
<aura:attribute name="storedSelectedOffer" type="List"/>
<!-- End of Attributes Definitions -->
<!--loading spinner start... style=Brand Medium (blue dots)-->
<aura:if isTrue="{!v.spinner}">
<div aura:id="spinnerId" class="slds-spinner_container">
<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
<span class="slds-assistive-text">Loading</span>
<div class="slds-spinner__dot-a"></div>
<div class="slds-spinner__dot-b"></div>
</div>
</div>
</aura:if>
<!-- Loading spinner end-->
<!-- Define Handlers -->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<!-- End of Handlers Definition -->
<!-- Bundled Offers Catalogue Section -->
<aura:if isTrue="{!not(v.nextClicked)}">
<lightning:card footer="" title="{!$Label.c.el_Bundled_Offers_Catalogue}">
<!--Search Offer-->
<div class="slds-size_1-of-2">
<lightning:input type="search"
aura:id="searchinput"
required="{!v.required}"
fieldLevelHelp="{!v.fieldLevelHelp}"
value="{!v.searchCatalogueString}"
onchange="{!c.searchOffer}"
placeholder="Search..."
class="leftspace"/>
</div>
<!--End of Search Offer-->
<lightning:layout>
<lightning:layoutItem padding="around-small">
<aura:iteration items="{!v.mapList}" var="map">
<lightning:accordion allowMultipleSectionsOpen="true">
<lightning:accordionSection
name="{!map.category.Id}"
label="{!map.category.Name}">
<aura:set attribute="body">
<aura:iteration items="{!map.offers}" var="offer">
<ui:inputCheckbox class="myCheckbox" aura:id="offer" name="{!offer.value}" label="{!offer.label}" change="{!c.onCheck}"/>
</aura:iteration>
</aura:set>
</lightning:accordionSection>
</lightning:accordion>
</aura:iteration>
<!-- Button to move to the Configuration of the Members -->
</lightning:layoutItem>
</lightning:layout>
</lightning:card>
<div class="slds-col slds-size_1-of-1">
<div class="slds-p-top_medium"></div>
</div>
<lightning:button variant="brand" label="{!$Label.c.el_Next}" title="{!$Label.c.el_Next}"
onclick="{!c.handleNext}" disabled="{!not(v.offerSelected)}" class="slds-float_right"/>
</aura:if>
Controller JS
// initCmp.js
({
//Function on Components Load...
doInit: function(component,conrtoller,helper) {
component.set("v.spinner", true); //Set Loading Spinner to True
// Call the server-side controller to get Eligible Bundled Offers and Categories
var action = component.get("c.getEligibleBundledOffersCategories");
action.setParams({ basketId : component.get("v.basketId") });
// Create a callback that is executed after the server-side action returns
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
// set the mapList with the value returned from the server
component.set("v.mapList", response.getReturnValue());
component.set("v.initialMapList", response.getReturnValue());
component.set("v.spinner", false); //Set Loading Spinner to False
}
else if (state === "INCOMPLETE") {
// do something
}
else if (state === "ERROR") {
var errors = response.getError();
if (errors) {
if (errors[0] && errors[0].message) {
console.log("Error message: " +
errors[0].message);
}
} else {
console.log("Unknown error");
}
}
});
$A.enqueueAction(action);
},
//@ggalaios: Function when user searches for Bundled Offer
searchOffer: function(component,conrtoller,helper) {
component.set("v.spinner", true); //Set Loading Spinner to True
var offersCategories = component.get("v.initialMapList"); //get the list of the returned by APEX Categories & Offers
//alert('Start and OfferCategories are: ' +JSON.stringify(offersCategories));
//alert('Start and OfferCategories are: ' +JSON.stringify(offersCategories));
//If searchSpec null or empty...
if(!component.get("v.searchCatalogueString") || component.get("v.searchCatalogueString").length === 0) {
//if searchSpec has a value
//no actions needed...
} else {
//Iterate through each Category
for (var i=0; i < offersCategories.length; i++) {
//If Category Name included as a substring, do not change anything
console.log('Categ. name is: ' +offersCategories[i].category.Name);
if(offersCategories[i].category.Name.includes(component.get("v.searchCatalogueString"))) {
//alert('Category included in searchSpec!!!');
//No actions needed...
} else {
//alert('Category not included in searchSpec');
if (offersCategories[i].offers.length > 0) {
for (var j=0; j<offersCategories[i].offers.length; j++) {
if (offersCategories[i].offers.length > 0) {
//alert('length of offers is: ' +offersCategories[i].offers.length);
if(offersCategories[i].offers[j].label.includes(component.get("v.searchCatalogueString"))) {
//alert('searchSpec included in offer name' +offersCategories[i].offers[j].label);
} else {
//alert('splice ' +offersCategories[i].offers[j].label);
offersCategories[i].offers.splice(j,1); //Remove Offer from the search results
j=j-1; //reduce j by 1, since the length of the Offers is reduced
}
}
}
if(offersCategories[i].offers.length===0) {
//alert('Splice categ');
offersCategories.splice(i,1); //Remove Category from the Search results
i=i-1;
}
} else {
offersCategories.splice(i,1); //Remove Category from the Search results
i=i-1;
}
}
}
}
component.set("v.mapList", offersCategories);
component.set("v.spinner", false); //Set Loading Spinner to False
},
});
The problem is that the attribute "initialMapList" although i never do a component.set from the js side, it gets manipulated! and the crazy thing is that when i try to iterate through this list in the cmp code (html), it gets displayed properly!!! Don't know what i am missing here... Any ideas?