angular-ui / bootstrap

PLEASE READ THE PROJECT STATUS BELOW. Native AngularJS (Angular) directives for Bootstrap. Smaller footprint (20kB gzipped), no 3rd party JS dependencies (jQuery, bootstrap JS) required. Please read the README.md file before submitting an issue!
http://angular-ui.github.io/bootstrap/
MIT License
14.28k stars 6.73k forks source link

Fix uibUncheckable for uibBtnRadio #6538

Open navarroaxel opened 7 years ago

navarroaxel commented 7 years ago

Fix #6532

zacronos commented 7 years ago

LGTM.

Does anyone have any idea how soon or not this will be pulled into a release? This bug is impacting a project that is approaching beta launch, so I need to evaluate whether it's worth implementing a hack to work around the bug vs waiting for this fix to be merged.

navarroaxel commented 7 years ago

@zacronos this is my hack by the moment...

gulp.task('fix:angular:bootstrap', () => {
    // fix https://github.com/angular-ui/bootstrap/issues/6532
    const path = './node_modules/angular-ui-bootstrap/src/buttons/';

    return gulp.src(`${path}buttons.js`)
        .pipe(require('gulp-replace')(
            'attrs.$set(\'uncheckable\', uncheckable ? \'\' : undefined);',
            'attrs.$set(\'disabled\', uncheckable ? \'disabled\' : undefined);'
        ))
        .pipe(gulp.dest(path));
});

this should run at clean stage, before the build.

guillaume-brialon commented 6 years ago

Thanks @navarroaxel for the fix. I could not use a solution modifying the source so I added a directive decorator:

  angular.module('ui.bootstrap.buttons')
    .decorator('uibBtnRadioDirective', [ '$delegate', '$parse',
      function uibBtnRadioDirectiveDecorator($delegate, $parse) {
        var directive = $delegate[0];

        // fixed link function
        var link = function (scope, element, attrs, ctrls) {
          var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
          var uncheckableExpr = $parse(attrs.uibUncheckable);

          element.find('input').css({display: 'none'});

          //model -> UI
          ngModelCtrl.$render = function () {
            element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
          };

          //ui->model
          element.on(buttonsCtrl.toggleEvent, function () {
            if (attrs.disabled) {
              return;
            }

            var isActive = element.hasClass(buttonsCtrl.activeClass);

            if (!isActive || angular.isDefined(attrs.uncheckable)) {
              scope.$apply(function () {
                ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
                ngModelCtrl.$render();
              });
            }
          });

          if (attrs.uibUncheckable) {
            scope.$watch(uncheckableExpr, function (uncheckable) {
              attrs.$set('disabled', uncheckable ? 'disabled' : undefined);
            });
          }
        };

        directive.compile = function() {
          return function(scope, element, attrs) {
            link.apply(this, arguments);
          };
        };

        return $delegate;
      }
    ]);