mtr / angular-iscroll

AngularJS module that enables iScroll 5 functionality, wrapping it in an easy-to-use directive.
MIT License
74 stars 27 forks source link

angular-iscroll

AngularJS module that enables iScroll 5 functionality, wrapping it in an easy-to-use directive

Install

Install the angular-iscroll NPM package

npm install --save angular-iscroll

Install through Yarn

yarn add angular-iscroll

Or, to check out a development version, start by cloning the repository, by

git clone git@github.com:mtr/angular-iscroll.git

If you don't use yarn you may run npm run-script [command] instead of yarn [command]. So, to install the necessary dependencies:

cd angular-iscroll/
yarn install
yarn build          # or `npm run-script build`

After that, you should have a dist directory with a subdirectory named lib:

dist/
└── lib
    ├── angular-iscroll.js
    ├── angular-iscroll.js.gz
    └── scss
        └── _iscroll.scss

Build

To rebuild the library, run

yarn build            # or `yarn watch`

Demo

You may have a look at core-layout (GitHub repo), an Angular demo app that shows how you can use the iscroll directive in a responsive-design web-app with support for both drawers (slide-out menus) and modals. For example, the demo shows how to handle DOM content generated dynamically through ngRepeat.

Usage

In the following, IScroll (with capital 'I' and 'S') refers to instances of the iScroll Javascript library that this package provides an AngularJS wrapper for.

The main usage pattern for angular-iscroll is to define a dependency on the angular-iscroll module in your AngularJS app. For example:

angular.module('myApp', ['angular-iscroll']);

or, in a Browserify-based code base:

angular.module('myApp', [require('angular-iscroll').name]);

The angular-iscroll module includes both a directive, iscroll, and a service, iScrollService, which gives you access to and control over a shared, global state of whether to enable, disable, or refresh the IScroll instances for each iscroll directive instance.

Next, to use the directive, you should set up your HTML template like

…
<body ng-controller="MyAppController as app"
      ng-class="{
      'iscroll-on': app.iScrollState.useIScroll,
      'iscroll-off': !app.iScrollState.useIScroll
      }">
<div class="iscroll-wrapper" iscroll>
  <div class="iscroll-scroller">
  </div>
</div>
…

Let me explain the essential parts of that HTML example. First of all, the iscroll directive is an attribute of an element belonging to the iscroll-wrapper class, which wraps an element of the iscroll-scroller class. Those two classes are defined in the SASS file dist/lib/scss/_iscroll.scss, but they don't have any meaning unless they occur inside an iscroll-on class; and that's where the shared, global state from iScrollService comes in. The controller, MyAppController, in the above example exposes the state variable shared by iScrollService in its scope

function MyAppController(iScrollService) {
    var vm = this;  // Use 'controller as' syntax 

    vm.iScrollState = iScrollService.state;
}

thereby providing a way to globally change the meaning of the iscroll-wrapper + iscroll-scroller combination. Please note: To get more info about the "controller as" syntax, you might enjoy John Papa's AngularJS Style Guide.

Furthermore, the global iScroll state exposed by the service should be changed through the service's enable([signalOnly]), disable([signalOnly]), and toggle([signalOnly]) methods, where each method will change the state accordingly, and then emit a corresponding signal from $rootScope that gets picked up and handled by the available angular-iscroll directive instances. If the signalOnly flag is true, then the state is not changed by the service method, but the signal is sent nonetheless. If the directives receive an iscroll:disabled signal, they will destroy any existing IScroll instances, and if they receive an iscroll:enabled signal, they will create a new IScroll instances per directive instance if it doesn't already exist.

It should also be noted that during instantiation, in the directive's post-link phase, the iscroll directive will check the iScrollService's useIScroll state to decide whether or not it will create an actual IScroll instance. Consequently, if you would like to create an AngularJS solution that uses iScroll only on, for example, iOS devices, you should determine the current browser type early, probably inside the app controller's configuration block, and set the service's useIscroll state accordingly. Please note that angular-iscroll does not contain any code to detect which browser or platform it is currently running on, which is a separate, complex task better solved by specialized libraries, like platform.js.

Manual Interaction with Each Directive's IScroll Instance

If you want access to a scope's IScroll instance, you can supply an optional iscroll-instance attribute when applying the iscroll directive, like

…
<div class="iscroll-wrapper" iscroll iscroll-instance="instance">
  <div class="iscroll-scroller">
  </div>
</div>
…

That way, the scope's instance variable will hold a reference to the actual IScroll instance, so you can access the IScroll instance's own API, for example to define custom events or access its scroller info.

Configuration

I've designed this module so that it should be easy to configure. First of all, you can supply per-instance options, both for IScroll and the directive itself, when you apply the directive. For example

<div iscroll="{mouseWheel: true, momentum: true, refreshInterval: 500}">…</div>

would pass along the options {mouseWheel: true, momentum: true} to IScroll, while the directive-specific configuration parameter, {refreshInterval: 500}, is only interpreted by the directive. Any config option not recognized as a directive-specific option, will be forwarded to IScroll.

There are lots of configuration options for IScroll itself; those are best documented by iScroll.

Directive Options

The directive provides two configuration options:

Globally Configuring the Directive's Default Options

The iscroll directive gets its default configuration from the iScrollService. To provide a way to easily, globally configure the defaults for all iscroll instances, the module defines an iScrollServiceProvider which can be injected into the app controller's configuration block which is guaranteed to run before the controller is used anywhere. For example:

/* @ngInject */
function _config(iScrollServiceProvider) {
    // Supply a default configuration object, eg:
    iScrollServiceProvider.configureDefaults({
        iScroll: {
            // Passed through to the iScroll library
            scrollbars: true,
            fadeScrollbars: true
        },
        directive: {
            // Interpreted by the directive
            refreshInterval: 500
        }
    });
}

angular
    .module('myApp', ['angular-iscroll'])
    .config(_config);

The configuration you provide this way will serve as the updated global default for all iscroll directive instances.

Please note that the above example relies on ng-annotate for adding AngularJS dependency-injection annotations during builds, as indicated by the /* @ngInject */ comment.

Support

Thanks to a generous “free for Open Source” sponsorship from BrowserStack I've been able to test core-layout, and thereby angular-iscroll, with a plethora of devices and browsers. The following browsers and devices has been tested successfully:

Incompatible Browsers

During testing, the core-layout demo broke in the following browsers:

This does not necessarily mean that angular-iscroll itself breaks in the same browsers, but the demo code did.