peta
peta

Reputation: 139

Bootstrap Switch stops working with Angular Routing

I have just started learning Angular and have set up Angular routing for my first app.

I have a shell page and some sub pages, for example products.html, help.html etc

the sub pages contains forms with bootstrap switches and validation, which for some reason stops working (the switch is rendered as a regular checkbox and validation does not run - please note that I am not using Angular JS).

However, if a place the same code directly in the shell page it works fine.

How do I get the sub pages to behave exactly like they the code in the shell page?

Basically, if I have the following code in a form in one of my subpages help.html:

        <div class="form-group">
            <label for="active">My checkbox<br />
            <div class="switch">
                <input type="checkbox" value="1" id="active" name="active">
            </div>
        </div>

...the switch does not render correctly, but if I move the code directly to the shell page it renders correctly.

So what is the difference in what happens in the sub page (which is shown in a on the shell page) or somw code that is placed directly in the shell page HTML.

Upvotes: 2

Views: 5285

Answers (1)

GregL
GregL

Reputation: 38103

I am assuming you are using this Bootstrap Switch plugin.

I am also assuming that you are initialising the switches that work on the shell page by doing something like:

$(function () {
    $('input[type="checkbox"]').bootstrapSwitch();
});

The problem with this is that it will only apply the bootstrap switch plugin to the checkboxes that it finds on the first page load, not after you change pages within an ng-view element.

What I recommend you do instead is to create a simple AngularJS directive to apply the bootstrap switch plugin to the checkboxes. The reason this will work is that Angular will compile the contents of a view every time you change pages and the link() functions of all the directives found will be run. Thus, your checkboxes, if they use this new directive, will always have the plugin applied correctly.

The directive could be as simple as:

app.directive('bsSwitch', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attrs, ngModelCtrl) {
      $(element).bootstrapSwitch({
        onSwitchChange: function(event, state) {
          scope.$apply(function() {
            ngModelCtrl.$setViewValue(state);
          });
        }
      });
    }
  }
});

Then change your markup in the views to be:

<div class="form-group">
    <label for="active">My checkbox</label>
    <br />
    <div class="switch">
        <input type="checkbox" value="1" id="active" name="active" bs-switch>
    </div>
</div>

EDIT: If you wish to apply the bootstrap switch to all checkboxes on the application without the need for additional attributes, you could instead create a directive that will apply to all <input>s, and then just check if they are checkboxes.

Like so:

app.directive('input', function() {
    return {
      restrict: 'E',
      require: 'ngModel',
      link: function(scope, element, attrs, ngModelCtrl) {
        if (attrs.type === 'checkbox')
          $(element).bootstrapSwitch({
            onSwitchChange: function(event, state) {
              scope.$apply(function() {
                ngModelCtrl.$setViewValue(state);
              });
            }
          });
      }
    }
});

And then you can omit the bs-switch attribute in your markup.

See my working Plunkr example (Plunkr is a better tool than jsFiddle for Angular examples, and allows you to create multiple HTML, CSS and JS files).

EDIT 2: As Mark pointed out, it was broken if the checkbox was initially checked. Here is a fixed version of the directive (and an updated Plunkr):

// As an element directive that will apply to all checkbox inputs:
// <input type="checkbox" ng-model="blah">
app.directive('input', function() {
  return {
    restrict: 'E',
    require: 'ngModel',
    link: function(scope, element, attrs, ngModelCtrl) {
      if (attrs.type === 'checkbox' && !Object.hasOwnProperty(attrs, 'bsSwitch')) {
        $(element).bootstrapSwitch({
          onSwitchChange: function(event, state) {
            scope.$apply(function() {
              ngModelCtrl.$setViewValue(state);
            });
          }
        });

        var dereg = scope.$watch(function() {
          return ngModelCtrl.$modelValue;
        }, function(newVal) {
          $(element).bootstrapSwitch('state', !! newVal, true);
          dereg();
        });
      }
    }
  }
});

Upvotes: 8

Related Questions