【问题标题】:NativeScript - how can I filter an observable array with SearchBar?NativeScript - 如何使用 SearchBar 过滤可观察数组?
【发布时间】:2016-03-15 07:41:26
【问题描述】:

您好,我正在尝试过滤通过 SearchBar 按键上的 HTTP 请求获取的可观察数据数组。

我设法让 SearchBar 属性更改工作,但我似乎无法弄清楚我在过滤逻辑中做错了什么。

理想情况下,我想在SearchBar 中输入搜索词时更新列表。我在 Telerik 网站上搜索过 API,实际上找不到任何示例。

XML

<Page loaded="pageLoaded">
    <ActivityIndicator busy="{{ isLoading }}" />
    <ActionBar title="People">
    </ActionBar>
    <GridLayout>
        <StackLayout>
            <SearchBar id="searchBar" hint="Search for someone"></SearchBar>
            <ListView items="{{ peopleList }}" itemTap="showDetail">
                <ListView.itemTemplate>
                    <StackLayout>
                        <Label text="{{ fullName }}" horiztonalAlignment="left" verticalAlignment="center"></Label>
                        <Label text="{{ company }}" class="info"></Label>
                    </StackLayout>
                </ListView.itemTemplate>
            </ListView>
        </StackLayout>
    </GridLayout>
</Page>

JS

var frames = require("ui/frame");
var Observable = require("data/observable").Observable;
var PeopleListViewModel = require("../../shared/people-viewModel");
var activityIndicatorModule = require("ui/activity-indicator");

var page;
var userkey;

var peopleList = new PeopleListViewModel([]);
var pageData = new Observable({ peopleList: peopleList });

exports.pageLoaded = function(args) {
    page = args.object;
    page.bindingContext = pageData;
    userkey = userkey || page.navigationContext.userkey;

    peopleList.load(userkey); // fetch data from the backend

    var searchBar = page.getViewById("searchBar");
    searchBar.on("propertyChange", function (args) {
        var searchText = args.object.text;
        if (searchText === "") {
            // NOT SURE WHAT TO DO HERE.
        } else {
            peopleList.filter(function (element, index, array) {
                // DOESN"T WORK PROPERLY
                console.log("element: ", JSON.stringify(element));
                return element.fullName == searchText;
            });
            console.log("Text types: ", searchText);
        }
    });
};

exports.showDetail = function(args) {
    var person = peopleList.getItem(args.index);
    var navigateEntry = {
        moduleName: "views/people/people-detail",
        context: { person: person },
        animated: false
    };
    frames.topmost().navigate(navigateEntry);
};

PeopleListViewModel.js

var config = require("./config");
var fetchModule = require("fetch");
var ObservableArray = require("data/observable-array").ObservableArray;

function PeopleListViewModel(people) {
    var viewModel = new ObservableArray(people);

    viewModel.load = function (userKey) {

        return fetchModule.fetch(config.baseUrl + "/api/people/all/" + userKey)
            .then(function (response) {
                return response.json();
            })
            .then(function (data) {
                data.forEach(function (person) {
                    viewModel.push(person);
                });
            }, function (error) {
                console.log("Error: ", error);
            });
    };

    viewModel.empty = function () {
        while (viewModel.length) {
            viewModel.pop();
        }
    };

    return viewModel;
}

function handleErrors(response) {
    if (!response.ok) {
        console.log("Error occurred");
    }
}

module.exports = PeopleListViewModel;

更新的人员列表

var frames = require("ui/frame");
var Observable = require("data/observable").Observable;
var ObservableArray = require("data/observable-array").ObservableArray;
var PeopleListViewModel = require("../../shared/people-viewModel");
var activityIndicatorModule = require("ui/activity-indicator");

var page;
var userkey;

var peopleList = new PeopleListViewModel([]);
var pageData = new Observable({ peopleList: peopleList });
var resultList = new ObservableArray([]);

exports.pageLoaded = function(args) {
    page = args.object;
    page.bindingContext = pageData;
    userkey = userkey || page.navigationContext.userkey;

    peopleList.load(userkey);

    var searchBar = page.getViewById("searchBar");
    searchBar.on("propertyChange", function (args) {
        var searchText = args.object.text;

        if (searchText === "") {

        } else {

            while (resultList.length > 0) {
                resultList.pop();
            }   

            peopleList.forEach(function (element) {
                if (element.fullName === searchText) {
                    resultList.push(element);
                }
            });
        }   
    });

};

【问题讨论】:

  • 您在过滤中看到的实际行为是什么?它根本没有过滤,还是你得到空白数组或错误的输出?
  • @AkashAgrawal 它根本不起作用。我基本上希望搜索栏充当下面列表的文本过滤器,它只是显示全名 + 公司的人员列表。
  • 你能给我们看一下 PeopleListViewModel。 filter 是自定义方法还是只是数组的 javascript filter 方法?如果是后者,你没有将它分配给任何东西。
  • @EmilOberg 我已经添加了视图模型文件。抱歉,我还是 NativeScript 的新手,其中很多内容对我来说仍然很困惑。

标签: filtering observablecollection nativescript


【解决方案1】:

我有同样的问题。如果您想在搜索栏中的每个字符都更改后过滤数据,您可以尝试我的解决方案。

定义 我的 playerList 是你的 peopleList。这是来自视图模型的数据。 resultList 是一个将推送数据的数组。

var observableArrayModule = require("data/observable-array").ObservableArray;
var playerList = new PlayerListViewModel([]);
var resultList = new observableArrayModule([]);

var pageData = new observableModule.Observable({
  resultList: resultList,
  player: ""
});

expors.loaded() 内部

  page = args.object;
  searchBar = page.getViewById("search-bar");
  page.bindingContext = pageData;

加载初始数据 - 在 expors.loaded() 中

当用户第一次导航到屏幕时,我们正在加载初始数据。由于我们在 xml 中使用 {{resultList}},因此我们还将相同的数据推送到 resultList。您可以在填充列表时添加 loadingIndicator。

playerList
.load()
.then(function() {
  setTimeout(function() {
    playerList.forEach(function (element) {
      pageData.resultList.push(element);
    });
  }, 1000);
})
.catch(function(error) {
  dialogsModule.alert({
    message: "An error occurred while loading players.",
    okButtonText: "OK"
  });
});

清除自动对焦 - 在 expors.loaded() 内

这是为了防止键盘在初始屏幕导航时打开。

  if (searchBar.ios) {
    searchBar.ios.endEditing(true);
  } else if (searchBar.android) {
      searchBar.android.clearFocus();
  }

字符改变时搜索数据 - 在 expors.loaded() 中

我正在调用过滤器功能。 Lodash _.debounce 函数用于延迟循环遍历 resultList 数组。没有它,应用程序会在每次输入字母时循环。现在我们正在等待用户停止输入以开始循环。

searchBar.on('propertyChange', _.debounce(searchList, 500));

搜索列表功能

这是实际的循环。您可以根据需要更改 element.name。

function searchList(args) {
  var searchText = args.object.text;
  while(resultList.length > 0) {
    resultList.pop();
  }
  playerList.forEach(function (element) {
    if (element.name.toLowerCase().indexOf(searchText) >= 0) {
      resultList.push(element);
    }
  });
}

如果搜索栏被清除则隐藏键盘 - 在exports.loaded()中

最后,如果用户清除搜索栏,我们想隐藏键盘。

searchBar.on(searchBarModule.SearchBar.clearEvent, function (args) {
  setTimeout(function() {
    searchBar.dismissSoftInput();
  }, 10);
});

PS

您可能解决了您的问题,但这可能会在将来对其他人有所帮助。

【讨论】:

  • 感谢它为我的问题指明了方向。
【解决方案2】:

好的,所以您的问题是 Javascript 问题而不是 NativeScript 问题。为了解决这个问题,请将可观察数组视为普通数组。

在您的 JS 中,您正在创建一个新的 PeopleListViewModel,然后您将通过 pageData 对象将其附加到 bindingContext。到目前为止,一切都很好。然后你在PeopleListViewModel 上调用load 方法(它返回一个你并没有真正做任何事情的承诺,但对于这个特定的问题并不重要)。

但是,当输入文本时,您实际上并没有做任何事情。这是你的代码:

        peopleList.filter(function (element, index, array) {
            // DOESN"T WORK PROPERLY
            console.log("element: ", JSON.stringify(element));
            return element.fullName == searchText;
        });

peopleListPeopleListViewModel 的一个实例,它返回一个ObservableArray。 ObservableArray 确实有一个名为 filter 的方法(它的工作原理类似于常规数组的过滤器。请查看 filterNativeScript documentationJavascript documentation)。

这里你需要了解的是filter 返回一个新数组 过滤结果。执行peopleList.filter() 会将新数组发送到空白空间。你想var yourNewFilteredArray = peopleList.filter()。但是您并不想重新定义绑定到绑定上下文的数组,而是要修改它的内容。

下面是一个示例,说明如何做到这一点:

/*
 * Attach a new obsersable array to the binding context.
 * you can prepopulate it with the data from the 
 * PeopleListViewModel if you want to
 */
var resultList = new ObservableArray([]);
var pageData = new Observable({ resultList: resultList });

/*
 * Then on search/filter you want to modify this new
 * array. Here I first remove every item in it and then
 * push matching items to it.
 */
searchBar.on("propertyChange", function (args) {
    var searchText = args.object.text;
    // ...

    while(resultList.length > 0) {
        resultList.pop();
    }

    peopleList.forEach(function (element) {
        if (element.fullName === searchText) {
            resultList.push(element);
        }
    });
});

【讨论】:

  • 这里有一些来自应用程序的代码,它几乎可以完成您想要做的事情:github.com/emiloberg/oppna-program-reklistan-app/blob/master/…
  • 在上面的示例中,searchResults 是什么。它看起来像一个新的数组/集合?它与上面示例中的 resultList 相同吗?
  • 我已经用更新的 js 文件更新了问题,我仍然不确定我缺少什么,请你看看,让我知道我仍然没有正确地做?
  • 您是否还更新了 XML 以打印来自 resultList 而不是 peopleList 的数据?
  • 我尝试将其更改为结果列表,但这意味着我没有绑定数据,因为数组一开始是空的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
  • 2014-01-18
  • 2018-11-23
  • 1970-01-01
相关资源
最近更新 更多