NativeScript - 如何使用SearchBar过滤可观察数组?

时间:2016-03-15 07:41:27

标签: filtering observablecollection nativescript

您好我正在尝试过滤在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);
                }
            });
        }   
    });

};

2 个答案:

答案 0 :(得分:5)

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

<强>解释 我的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()

当用户第一次导航到屏幕时,我们正在加载初始数据。我们也将相同的数据推送到resultList,因为我们在xml中使用{{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

你可能已经解决了你的问题,但这可能在将来帮助其他人。

答案 1 :(得分:1)

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

在您的JS中,您需要创建一个新的PeopleListViewModel,然后通过pageData对象附加到bindingContext。到现在为止还挺好。然后,您在load上调用PeopleListViewModel方法(它会返回一个您未真正做过任何事情的承诺,但针对此特定问题并不重要)

然而,当文本被输入时,你并没有真正做任何事情。这是你的代码:

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

peopleListPeopleListViewModel的一个实例,它返回ObservableArray。 ObservableArray确实有一个名为filter的方法(它就像常规数组的过滤器一样。查看filter的{​​{3}}和NativeScript 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);
        }
    });
});