Skip to content
This repository was archived by the owner on Oct 2, 2019. It is now read-only.

blur is not supported for tagging, tags just disappear #894

Open
jonashartwig opened this issue Apr 29, 2015 · 14 comments
Open

blur is not supported for tagging, tags just disappear #894

jonashartwig opened this issue Apr 29, 2015 · 14 comments

Comments

@jonashartwig
Copy link

I made a local fix like this:

ctrl.searchInput.on('blur', function(e) {
var data = ctrl.searchInput.val();
if (data && data.length > 0 && ctrl.taggingTokens.isActivated) {
var items = data.split(ctrl.taggingTokens.tokens[0]); // split by first token only
if (items && items.length > 0) {
angular.forEach(items, function (item) {
ctrl.select(item, true);
});
e.preventDefault();
e.stopPropagation();
}
}
});

@irongomme
Copy link

+1

@jwawi
Copy link

jwawi commented Jun 15, 2015

+1 could something like this get committed to the main branch? My project is using bower and an automated build process so we have to avoid local fixes.

@keulu
Copy link

keulu commented Jun 19, 2015

+1

@manichandra
Copy link

+1
working plunkr
http://plnkr.co/edit/srfpdXImxEO1dSnhKzVv?p=preview

@sime
Copy link
Contributor

sime commented Sep 14, 2015

Has anyone manage to extract this into a decorator?

@jwawi
Copy link

jwawi commented Sep 14, 2015

This is what I wrote in a controller. It also prevents duplicate tags from being added, which can break the UI unless you are using track by $index. I place this in a bootstrap-ui modal controller, but this could easily be placed in a directive's controller. If I get time today I will build a plnkr example with this in a directive. Let me know what you think

// DOCUMENTATION: Initialize data model used by tagging UI
$scope.tagModel = {tags:[]};

// DOCUMENTATION: filter duplicates or the ui-select will break
$scope.filterTagDuplicates = function(){

      if ($scope.blockFilterRecursion) {
          $scope.blockFilterRecursion = false;
          return;
      }

      var uniqueTags = [];
          angular.forEach($scope.tagModel.tags,function(obj,i){
              var dupe = false;
                  angular.forEach(uniqueTags, function(obj2,i2){
                       if (obj.name === obj2.name) dupe = true;
                  });
                  if (!dupe) uniqueTags.push(obj);
                  $scope.blockFilterRecursion = true;
                  $scope.tagModel.tags = uniqueTags;
          });
};

// DOCUMENTATION: if user hits enter or tab it won't trigger the on blur function
// need to filter any duplicates if found
$scope.$watch('tagModel.tags', function(tags) {
      if (!$scope.tagModel) return;
          $scope.filterTagDuplicates();
});

// DOCUMENTATION: wait for modal to render, then register event listener 
// listener adds tags on blur instead of just hitting enter
// this is kind of a hack until ui-select implements this. 
// can't modify their source code locally
// due to the bower/grunt automated build
$timeout(function(){
      $(".ui-select-search").on('blur', function(event){
          var newTag = ($(".ui-select-search").val());
              if ($(".ui-select-search").val() !== '') {
                  $scope.addTagIfUnique(newTag);
                  $scope.filterTagDuplicates();
                  $scope.$apply();
              }
      });
}, 1000); 
// timeout probably doesn't need to be so long, I wait so the modal is rendered

// DOCUMENTATION: dont allow duplicate tags or ui-select will break 
// ui-select uses ng-repeat which will break on duplicates if not tracking by index
$scope.addTagIfUnique = function(newTag) {
      var dupe = false;
      angular.forEach($scope.tagModel.tags, function(obj,i){
          if (obj.name == newTag) {
              dupe = true;
              return false;
          }
      });
      if (!dupe && $scope.tagModel.tags) $scope.tagModel.tags.push({"name":newTag,"type":"tag"});
      if (!dupe && !$scope.tagModel.tags) $scope.tagModel.tags = [{"name":newTag,"type":"tag"}];
};

@sime
Copy link
Contributor

sime commented Sep 14, 2015

Ahh, much more involved than I imagined. Will take some time to digest this.

@jwawi
Copy link

jwawi commented Sep 14, 2015

The high level overview of what it does is this block

$timeout(function(){
      $(".ui-select-search").on('blur', function(event){
          var newTag = ($(".ui-select-search").val());
              if ($(".ui-select-search").val() !== '') {
                  $scope.addTagIfUnique(newTag);
                  $scope.filterTagDuplicates();
                  $scope.$apply();
              }
      });
}, 1000); 
// timeout probably doesn't need to be so long, I wait so the modal is rendered

@jwawi
Copy link

jwawi commented Sep 14, 2015

  1. This code is taking place in an angular bootstrap modal, so i use a timeout function to allow the modal time to render first; you might not need the timeout set so long
  2. i use a jquery listener to listen for the .ui-select-search to fire a blur event.
  3. when it does here that event, it stores the current value of the input as a var newTag
  4. if newTag has a value, that value gets added to the tag data model (via $scope.addTagIfUnique())
  5. I filter out any duplicate tags because they break the ui unles you track by $index (via$scope.filterTagDuplicates())
  6. $scope.$apply() since this all occurred in a jquery listener and we need to update the view

@sime
Copy link
Contributor

sime commented Sep 15, 2015

I'm still digging for a simpler solution. I made a directive that would send the keycode of comma on the blur of a input element. But no dice.

/**
 * Send the comma key code on blur
 */
app.directive('commaOnBlur', function () {
    'use strict';
    var blurEvent = function (event) {
        angular.element(event.currentTarget).trigger({
            type: 'keydown',
            keyCode: 188
        });

        event.stopPropagation();
    };

    return {
        restrict: 'A',
        link: function (scope, element) {
            element.bind("blur", blurEvent);
        }
    };
});

@jwawi
Copy link

jwawi commented Sep 15, 2015

I similarly was trying something that would send enter or comma on blur, but it wasn't reliable. That's why I landed on pushing the value of the input to my tag model on blur event, which has worked reliably.

My solution really isn't that complex if you pare down the code that is removing and preventing duplicate tags. If you use track by $index and don't care about the dupes (which is another major shortcoming of ui-select's source code imo), then you're really only looking at something like this which is pretty simple, and you might not even need the timeout:

// $scope.tagModel.tags is the array that the tagging interface uses for ng-model in this example
$scope.tagModel = {tags: []};

$timeout(function(){
      $(".ui-select-search").on('blur', function(event){
          var newTag = ($(".ui-select-search").val());
              if ($(".ui-select-search").val() !== '') {
                  $scope.tagModel.tags.push(newTag);
                  $scope.$apply();
              }
      });
}, 1000); 

@sime
Copy link
Contributor

sime commented Sep 22, 2015

I'm still investigating the blur directive approach, though I am about to stop. I believe I have found the limitation in the source that limits the directive from succeeding.

If the $timeount() call is removed from this code, the directive works:

ui-select/dist/select.js

Lines 679 to 686 in bc3de20

$timeout(function() {
ctrl.searchInput.triggerHandler('tagged');
var newItem = ctrl.search.replace(KEY.MAP[e.keyCode],'').trim();
if ( ctrl.tagging.fct ) {
newItem = ctrl.tagging.fct( newItem );
}
if (newItem) ctrl.select(newItem, true);
});

@sime
Copy link
Contributor

sime commented Sep 22, 2015

Link to source, rather than dist.

$timeout(function() {
ctrl.searchInput.triggerHandler('tagged');
var newItem = ctrl.search.replace(KEY.MAP[e.keyCode],'').trim();
if ( ctrl.tagging.fct ) {
newItem = ctrl.tagging.fct( newItem );
}
if (newItem) ctrl.select(newItem, true);
});

@rysilva
Copy link

rysilva commented Jun 22, 2017

There's a problem with the blur approach if the blur was caused by a choice in the dropdown. See my comment and solution here #1544 (comment)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants