AngularJs: How make ui-select working properly?
Asked Answered
S

3

9

THE SITUATION:

I am making an angular app where I have to use ui-select: in the user info page, in the select have to be possible to choose one or many tag. It is almost working, except from the fact that i have problems to get and display the pre-existent tags.

THE CODE:

View:

<ui-select multiple ng-model="info_data.tags" theme="bootstrap" ng-disabled="disabled">

  <ui-select-match placeholder="Select tag...">{{$item.name}} </ui-select-match>

  <ui-select-choices repeat="tag in all_tags | propsFilter: {name: $select.search}">

    {{tag.name}}

  </ui-select-choices>

</ui-select>

<p>Selected: {{info_data.tags}}</p>

Controller:

$http({

    url: base_url + 'main/db_get_all_tags',
    method: "POST",

 }).success(function (data) {

    $scope.all_tags = data;

});

$scope.show_info = function() {

    var result_info = DbService.get_info( $stateParams.db_data_id );

    result_info.then( function( data )
    {
        $scope.info_data = data;

    });

};

ATTEMPT 1:

It happens a very strange behavior. I don't see the tags in the info page of the user, and not even in the ui-select. Except if refresh 5/6 times, then suddenly it will magically work, displaying the tags in the user info page and in the ui-select. In both cases, working and not, i get several error message of the same kind:

Cannot read property 'length' of undefined.

ATTEMPT 2:

In order to resolve this problem, I have added this code in the controller:

$scope.info_data = { tags: [] };
$scope. all_tags = [];

And i don't get anymore any error message. The app is stable and i can see the proper tags in the user info page. The only problem is that the tags are not loaded anymore in the ui-select.

If i select a new tag then it works fine, but i loose the pre-existing tags.

QUESTION(s):

How can i make ui-select properly working? (currently v0.8.3) There is a problem of conflict?

How can i properly call pre-existent data from the server?

Thank you very much!

Sundew answered 22/10, 2014 at 11:45 Comment(0)
D
41

You haven't been particularly descriptive with the errors you're seeing so I don't know if the following will help..

I had a problem originally when using the ui-select demo code as an example because they're using the propsFilter filter which is a custom filter they have written for the demo:

<ui-select-choices repeat="tag in all_tags | propsFilter: {name: $select.search}">

I am assuming you're not including this filter in your code which may be a reason you're experiencing a problem. You can resolve it by using angular's normal filter:

<ui-select-choices repeat="tag in all_tags | filter: {name: $select.search}">

Alternatively, if you have multiple properties to filter you can write the propsFilter filter to filter on OR rather than AND. If you use 'filter' to filter multiple properties it will try to match the search value across all of the properties.

app.filter('propsFilter', function() {
  return function(items, props) {
            var out = [];
                if (angular.isArray(items)) {
                  items.forEach(function(item) {
                        var itemMatches = false;

                        var keys = Object.keys(props);
                        for (var i = 0; i < keys.length; i++) {
                              var prop = keys[i];
                              var text = props[prop].toLowerCase();
                              if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
                                    itemMatches = true;
                                    break;
                                  }
                            }

                            if (itemMatches) {
                              out.push(item);
                            }
                      });
                } else {
                  // Let the output be the input untouched
                      out = items;
                }

                return out;
          };
    });

you can see the commit with the filter in it here: https://github.com/angular-ui/ui-select/commit/3fac88cfad0ad2369c567142eadba52bdb7998b1

Although if you have some specific filtering requirements I would recommend you to write your own filter to ensure optimum performance.

Deach answered 16/11, 2014 at 14:57 Comment(3)
Thanks for pointing out that propsFilter is a custom method. That potentially saved me hours of head-scratching. I'm going to submit a PR to ui-select that states as much.Liberec
Hi, thank you for your explanation. I started using the code you provided but run into a bug. Turns out that there is a difference in what is posted here with the commit you reference: The inner for should be: for (var i = 0; i < keys.length; i++) to avoid getting into an infinite loop.Draper
Ohhhhhhhhhh that custom propsFilter! Thank you! This is the end of hours and hours and hours of head-scratching!Brume
N
3

I don't know what the situation was like before Select2#4.0, but it's really not all that hard to use it without angular-ui-select (and it's one less dependency)

Just include select2 in your bower dependencies and use it in your link function within the directive:

.directive('someDirective', function() {
    return {
        restrict: 'E',
        link: function(scope, element, attrs) {
            element.find('.your-select2').select2({
                theme: 'classic',
                placeholder: 'Select a placeholder...',
                allowClear: true,
                data: [{ id: 'New', text: 'New'}]...
            });
        },
    };
})

and your HTML:

<select class="your-select2" ng-model="a.model.field"></select>

You can also load the data from the controller via a service if you want, then just use the scope to set it!

I say this as I tried using angular-ui-select because I thought "hey it's Angular, you must use a plugin for it!", but that's not always the case :). Plus I found the docs not so helpful (call me lazy but hey)

Nineteenth answered 23/7, 2015 at 20:23 Comment(0)
U
0

I optimized the propsFilter a bit. It is doing

props[prop].toLowerCase();

inside the items iteration, but this actually needs to be evaluated only as many times as many properties we have. Currently it was evaluated items count * props count.

So the final optimized code looks like this:

app.filter('casinoPropsFilter', function() {
    return function(items, props) {
        var out = [];

        if (angular.isArray(items)) {
            var keys = Object.keys(props);
            var propCache = {};

            for (var i = 0; i < keys.length; i++) {
                var prop = keys[i];
                var text = props[prop].toLowerCase();
                propCache[props[prop]] = text;
            }

            items.forEach(function(item) {
                var itemMatches = false;

                for (var i = 0; i < keys.length; i++) {
                    var prop = keys[i];
                    var text = propCache[props[prop]];
                    if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
                        itemMatches = true;
                        break;
                    }
                }

                if (itemMatches) {
                    out.push(item);
                }
            });
        } else {
            // Let the output be the input untouched
            out = items;
        }

        return out;
    };
});    

Undercoating answered 29/8, 2019 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.