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. 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 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} 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. 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.