3

I'm trying to build a column filter component for a table where there can be multiple column filter can be specified.

When I click on the Add button, new filter item appears below. But if I delete the filter item using remove button, the actual object in the array is removed but it is not reflected in the view.

I tried couple of workarounds, but it doesn't work. I'm running out of options here.

Here's the code:

filterBox.cmp

<aura:component>
    <aura:attribute name="columns" type="List" default="['Name','Email','Birthdate']" />
    <aura:attribute name="operators" type="List" default="['less than','greater than','equal to']" />
    <aura:attribute name="filters" type="List" default="[{'column':'','op':'','value':''}]" />
    <div class="filter-box">
        <aura:iteration items="{!v.filters}" var="filter" indexVar="index">
            <div class="filter-item">
                <ui:inputSelect value="{!filter.column}">
                    <aura:iteration items="{!v.columns}" var="column">
                        <ui:inputSelectOption label="{!column}" text="{!column}" />
                    </aura:iteration>
                </ui:inputSelect>
                <ui:inputSelect value="{!filter.op}">
                    <aura:iteration items="{!v.operators}" var="op">
                        <ui:inputSelectOption label="{!op}" text="{!op}" />
                    </aura:iteration>
                </ui:inputSelect>
                <ui:inputText value="{!filter.value}" />
                <button type="button" onclick="{!c.addFilter}">Add</button>
                <aura:if isTrue="{!index != 0}">
                    <button type="button" onclick="{!c.removeFilter}" data-index="{!index}">Remove</button>
                </aura:if>
            </div>
        </aura:iteration>
    </div>    
</aura:component>

filterBoxController.js

({
    addFilter : function(cmp, event, helper) {
        var filters = cmp.get("v.filters");
        filters.push({'column':'','op':'','value':''});
        cmp.set("v.filters",filters);
        console.log(cmp.get("v.filters"))
    },
    removeFilter : function(cmp, event, helper) {
        var index = event.target.getAttribute('data-index');
        var filters = cmp.get("v.filters");
        filters.splice(Number(index),1);
        console.log(filters,index);
        cmp.set("v.filters",filters); // didn't work
        cmp.set("v.filters",[].concat(filters)); // didn't work
        cmp.set("v.filters",JSON.parse(JSON.stringify(filters))); // didn't work
    }
})

FYI, I see the same behavior with locker service turned on and off as well.

3
  • 1
    I see you tagged this with 'locker-service'. Does the component behave as expected with LockerService deactivated? Commented Dec 2, 2016 at 18:25
  • @trevorbliss With locker turned-off, I still see the same behavior.
    – Praveen
    Commented Dec 2, 2016 at 18:53
  • replace all ui tag by its equivalent lightning tag and it should work fine. example: ui:inputselect will be lightning:select
    – Kevan
    Commented Nov 28, 2017 at 19:38

3 Answers 3

3

This is a known issue with ui:inputSelectOption inside an aura:iteration. If you file a case you can reference the internal bug number W-3430029.

For now I'd see if you can structure your code to not use the ui:inputSelectOption inside your iteration.

I'll update this answer if I find a workaround for the issue.

Edit (12/2): A dirty workaround you can try is to destroy the last ui:inputSelect component in the list before removing the item from the array. For example, put an aura:id on each ui:inputSelect component and then do the following when you remove the row:

function destroySelect(select) {
    if ($A.util.isArray(select)) {
        var last = select.pop();
        last.destroy();
    }
}
destroySelect(cmp.find("mySelect1"));
destroySelect(cmp.find("mySelect2"));
4
  • Your solution works when locker is turned-off. With locker on, i get this error :This page has an error. You might just need to refresh it. Action failed: c$dtFilterBox$controller$removeFilter [last.destroy is not a function] Failing descriptor: {c$dtFilterBox$controller$removeFilter}
    – Praveen
    Commented Dec 3, 2016 at 7:50
  • Do you have any workarounds with locker service turned on?. I'm think of deferring the use of ui:inputSelect/ui:inputSelectOption and use select tag instead.
    – Praveen
    Commented Dec 3, 2016 at 7:57
  • Sorry, no known workarounds inside LockerService at the moment. Commented Dec 3, 2016 at 23:59
  • I've found this while investigating an issue handling the component.get result of an array. I believe Lightning is not handling Array's in the right manner. JS array methods are failing in my tests. I wanted to use shift(), just like you are using .destroy() and it fails. I did find this workaround: var a = component.get("v.questionList"); var a = a.shift(); ---> this fails var b = JSON.parse(JSON.stringify(component.get("v.questionList"))); var b = b.shift(); ----> this works Commented Nov 28, 2017 at 9:22
1

As a workaround, I used <select /> tag instead <ui:inputSelect /> and do some logic for data binding to make it work with locker turned on.

markup:

<select data-index="{!index}" data-prop="column" onchange="{!c.setProp}">
    <aura:iteration items="{!v.columns}" var="column">
        <option value="{!column}">{!column}</option>
    </aura:iteration>
</select>

js:

setProp : function(cmp,event,helper) {
    var index = event.target.getAttribute("data-index");
    var propToSet = event.target.getAttribute("data-prop");
    var filters = cmp.set("v.filters");
    filters[index][propToSet] = event.target.value;
    cmp.set("v.filters",filters);
}
1

I've found the discussion in the accepted answer where the error last.destroy is not a function is thrown. I believe this is an issue handling the component.get() result of an array where Lightning is not handling Array's in the right manner.

JS array methods are failing in my tests. I wanted to use shift(), just like you are using .destroy() and it fails. I did find this workaround:

var a = component.get("v.questionList"); 
var a = a.shift(); // this fails 
var b = JSON.parse(JSON.stringify(component.get("v.questionList"))); 
var b = b.shift(); // this works

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .