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.

  • 1
    I see you tagged this with 'locker-service'. Does the component behave as expected with LockerService deactivated? – TrevorBliss Dec 2 '16 at 18:25
  • @trevorbliss With locker turned-off, I still see the same behavior. – Praveen Dec 2 '16 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 Nov 28 '17 at 19:38
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"));
  • 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 Dec 3 '16 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 Dec 3 '16 at 7:57
  • Sorry, no known workarounds inside LockerService at the moment. – TrevorBliss Dec 3 '16 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 – Marco Pietersen Nov 28 '17 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.