angular-ui-select2 and breezejs: load ajax list after typing in 2 characters

江枫思渺然 提交于 2020-01-02 11:44:07

问题


I have a project where I'm using BreezeJS to fetch data from my webserver. I'm using AngularJS with the ui-select2 module. Currently, I have it where when I load my page, breezejs makes a call to fetch the data that I dump into a scope variable. From there, select2 can easily make the reference to it and build accordingly.

If I want to ajaxify things, it gets really tricky. I want to have the ability to use select2's ajax or query support, but instead of using it to fetch data, I want to use breezejs to do it. So during a page load, nothing is loaded up until I start typing in X minimum characters before it makes an ajax fetch.

Constraints: I do not want fetch data using select2's "ajax". I want BreezeJS to handle the service calls. When I use ajax, it makes an ajax call everytime I press a character in order to filter the results (and resemble autocomplete). I just want the list to load up once and use the native filtering after that.

Here is what I have so far:

breezejs - StateContext.JS

m.app.factory('StateContext', ['$http', function ($http) {
    configureBreeze();

    var dataService = new breeze.DataService({
        serviceName: "/Map/api",
        hasServerMetadata: false
    });

    var manager = new breeze.EntityManager({ dataService: dataService});

    var datacontext = {
        getAllStates: getAllStates
    };
    return datacontext;


    function getAllStates() {
        var query = breeze.EntityQuery
                .from("States");
        return manager.executeQuery(query);
    }


    function configureBreeze() {
        breeze.config.initializeAdapterInstances({ dataService: "webApi" });
    }
}]);

This works and returns my json object correctly.

Here is how I call the service:

m.app.controller('LocationCtrl', ['$scope', 'StateContext', function ($scope, StateContext) {

    $scope.getAllStates = function () {
        StateContext.getAllStates().then(stateQuerySucceeded).fail(queryFailed);
    }
    $scope.getAllStates();


    $scope.states = [];
    function stateQuerySucceeded(data) {
        data.results.forEach(function (item) {
            $scope.states.push(item);
        });
        $scope.$apply();

        console.log("Fetched States");
    }

    function queryFailed(error) {
        console.log("Query failed");
    }

    $scope.select2StateOptions = {
        placeholder: "Choose a State",
        allowClear: true,
        minimumInputLength: 2
    };
}

and here is my html:

<div ng-app="m" id="ng-app">
    ...
    ...

    <select ui-select2="select2StateOptions" ng-model="LocationModel.State">
        <option value=""></option>
        <option ng-repeat="state in states" value="{{state.id}}">{{state.name}}</option>
    </select>
</div>

Currently the html select2 control loads up when the page loads. But I want to have it so when I type in more than 2 characters, I'll be able to make the call to $scope.getAllStates(); as an ajax call. BreezeJS already uses ajax natively when configuring the BreezeAdapter for webapi.

I was thinking about using select2's ajax, or query calls.. but I'd rather use breeze to fetch the data, since it makes querying extendable, and I don't want to violate my design pattern, or make the code harder to maintain, and I don't want the ajax calls to be made everytime I enter a new character into the textbox, I just want it to occur once.

Close attempt:

changed my html to:

<!-- Select2 needs to make this type="hidden" to use query or ajax, then it applies the UI skin afterwards -->
<input type="hidden" ui-select2="select2StateOptions" ng-model="LocationModel.State" /><br />

in my controller, changing select2StateOptions:

$scope.select2StateOptions = {
    placeholder: "Choose a State",
    allowClear: true,
    minimumInputLength: 2,
    query: function (query) {
        debugger;
        var data = StateContext.getAllStates().then(stateQuerySucceeded).fail(queryFailed);
    }
};

Here's the problem. BreezeJS uses a Q library, which makes use of a thing called a "promise"; which is a promise that data will be returned after making the ajax call. The problem with this, the query function is expecting data to be populated, but the promise to call the "stateQuerySucceeded" function is made after returning from the query function.

So it hits the query function first. Then hits getAllStates(). Returns from the query (nothing is populated), then "stateQuerySucceeded" is called after that.

In otherwords, even though I have been able to fetch data, this is done too late.. select2's query function did not receive the data at the right time, and my html select is hanging on "Searching ... " with a search spinner.gif.


回答1:


I don't really know this angular-ui-select2 control. I think the relevant part of the documentation is this example:

$("#e5").select2({
    minimumInputLength: 2,
    query: function (query) {
       var data = {results: []}, i, j, s;
        // simulate getting data from the server
        for (i = 1; i < 5; i++) {
            s = "";
            for (j = 0; j < i; j++) {s = s + query.term;}
            data.results.push({id: query.term + i, text: s});
        }
        query.callback(data);
    }
});

I will leave aside the fact that you don't seem to be interested in using the two-or-more characters that the user enters in your query (maybe you just left that out). I'll proceed with what seems to me to be nonsense, namely, to fetch all states after the user types any two letters.

What I think you're missing is the role of the query.callback which is to tell "angular-ui-select2" when the data have arrived. I'm guessing you want to call query.callback in your success function.

$scope.select2StateOptions = {
    placeholder: "Choose a State",
    allowClear: true,
    minimumInputLength: 2,
    query: function (query) {
        StateContext.getAllStates()
                    .then(querySucceeded).catch(queryFailed);

       function querySucceeded(response) {
            // give the {results:data-array} to the query callback
            query.callback(response);
       }

       function queryFailed(error) {
           // I don't know what you're supposed to do.
           // maybe return nothing in the query callback?
           // Tell the user SOMETHING and then
           query.callback({results:[]});      
       }
    }
};

As I said, I'm just guessing based on a quick reading of the documentation. Consider this answer a "hint" and please don't expect me to follow through and make this actually work.



来源:https://stackoverflow.com/questions/21586773/angular-ui-select2-and-breezejs-load-ajax-list-after-typing-in-2-characters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!