├── .gitignore ├── .editorconfig ├── demo ├── app.css ├── app.js └── index.html ├── .eslintrc.js ├── dist ├── ng-resize.min.js └── ng-resize.js ├── LICENSE ├── package.json ├── gulpfile.js ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | node_modules/ 3 | tmp/ 4 | pages/ 5 | build/ 6 | .DS_Store 7 | .idea 8 | .log 9 | .vscode 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,package-lock.json}] 12 | indent_size = 2 13 | 14 | [*.yml] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /demo/app.css: -------------------------------------------------------------------------------- 1 | html, body{ 2 | height: 100%; 3 | } 4 | 5 | .size-item{ 6 | text-align: center; 7 | background-color: #c09; 8 | width: 100%; 9 | display: block; 10 | color: white; 11 | text-transform: capitalize; 12 | padding: 30% 0; 13 | } 14 | 15 | [resize-test]{ 16 | height: 100%; 17 | } 18 | 19 | [resize-test] .row{ 20 | height: 100%; 21 | } 22 | -------------------------------------------------------------------------------- /demo/app.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('app', ['ngResize']); 2 | 3 | app.directive('resizeTest', ['resize', function(resize) { 4 | return { 5 | restrict: 'A', 6 | controller: function($scope) { 7 | 8 | $scope.$on('resize', function($event, data) { 9 | $scope.size = data; 10 | }); 11 | 12 | }, 13 | link: function($scope, $elem, $attr) { 14 | 15 | } 16 | }; 17 | }]); 18 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "node": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "rules": { 7 | "indent": [ 8 | "error", 9 | 4 10 | ], 11 | "linebreak-style": [ 12 | "error", 13 | "unix" 14 | ], 15 | "quotes": [ 16 | "error", 17 | "single" 18 | ], 19 | "semi": [ 20 | "error", 21 | "always" 22 | ], 23 | "no-console": [ 24 | "warn" 25 | ], 26 | "no-undef": [ 27 | "warn" 28 | ], 29 | "no-unused-vars": [ 30 | "warn" 31 | ] 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /dist/ng-resize.min.js: -------------------------------------------------------------------------------- 1 | !function(t,i){var n=i.module("ngResize",[]);n.provider("resize",function(){this.throttle=32,this.initBind=1,this.$get=["$rootScope","$window","$interval",function(t,n,e){function r(t){h.throttle=t}function o(){return h.throttle}function u(i){i=i||t,i.$broadcast("resize",{width:n.innerWidth,height:n.innerHeight})}function c(){f||(g.on("resize",function(){a||(d=e(function(){a&&(a=0,e.cancel(d),h.trigger())},h.throttle)),a=1}),f=1,g.triggerHandler("resize"))}function s(){f&&(g.off("resize"),f=0)}var h=this,f=0,d=0,a=0,g=i.element(n);this.getThrottle=o,this.setThrottle=r,this.trigger=u,this.bind=c,this.unbind=s,h.initBind&&h.bind()}]}),n.directive("ngResize",["$parse","$timeout","resize",function(t,i,n){return{compile:function(n,e){var r=t(e.ngResize);return function(t,n,e){t.$on("resize",function(n,e){i(function(){t.$apply(function(){r(t,{$event:n,data:e})})})})}}}}])}(window,window.angular); -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | window width:
{{size.width}}
15 |
16 |
17 | window height:
{{size.height}}
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 danmasta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-resize", 3 | "version": "2.0.0", 4 | "author": "Daniel Smith ", 5 | "description": "Manage resize events in Angular applications", 6 | "license": "MIT", 7 | "keywords": [ 8 | "lightweight", 9 | "ui", 10 | "resize", 11 | "angular", 12 | "javascript" 13 | ], 14 | "main": "index.js", 15 | "scripts": { 16 | "test": "echo Error: no test specified" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/danmasta/ng-resize.git" 21 | }, 22 | "dependencies": {}, 23 | "devDependencies": { 24 | "eslint-config-standard": "^11.0.0", 25 | "eslint-plugin-import": "^2.11.0", 26 | "eslint-plugin-node": "^6.0.1", 27 | "eslint-plugin-promise": "^3.7.0", 28 | "eslint-plugin-standard": "^3.1.0", 29 | "express": "^4.15.2", 30 | "gulp": "^3.9.1", 31 | "gulp-concat": "^2.6.1", 32 | "gulp-gh-pages": "^0.5.4", 33 | "gulp-livereload": "^3.8.1", 34 | "gulp-ng-annotate": "^2.0.0", 35 | "gulp-uglify": "^2.1.2" 36 | }, 37 | "engines": { 38 | "node": ">=0.10", 39 | "npm": ">=2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var gulp = require('gulp'); 3 | var express = require('express'); 4 | var livereload = require('gulp-livereload'); 5 | var concat = require('gulp-concat'); 6 | var deploy = require('gulp-gh-pages'); 7 | var uglify = require('gulp-uglify'); 8 | var annotate = require('gulp-ng-annotate'); 9 | 10 | gulp.task('js', function(){ 11 | return gulp.src('./index.js') 12 | .pipe(annotate()) 13 | .pipe(concat('ng-resize.js')) 14 | .pipe(gulp.dest('dist')); 15 | }); 16 | 17 | gulp.task('js:min', function(){ 18 | return gulp.src('./index.js') 19 | .pipe(annotate()) 20 | .pipe(uglify()) 21 | .pipe(concat('ng-resize.min.js')) 22 | .pipe(gulp.dest('dist')); 23 | }); 24 | 25 | gulp.task('build', ['js', 'js:min']); 26 | 27 | gulp.task('deploy', ['build'], function(){ 28 | return gulp.src(['dist/**/*', 'demo/**/*']).pipe(deploy({ cacheDir:'pages' })); 29 | }); 30 | 31 | gulp.task('server', ['build'], function(done){ 32 | 33 | var app = express(); 34 | 35 | app.use(express.static('demo')); 36 | app.use(express.static('dist')); 37 | 38 | console.log('[development] server listening on: 8080'); 39 | 40 | app.listen(8080, done); 41 | 42 | }); 43 | 44 | gulp.task('watch', ['server'], function(){ 45 | 46 | livereload.listen(); 47 | 48 | gulp.watch('./index.js', ['js', 'js:min']); 49 | 50 | gulp.watch(['demo/**/*', 'dist/**/*'], function(file){ 51 | livereload.changed(file.path); 52 | }); 53 | 54 | }); 55 | 56 | gulp.task('default', ['watch']); 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngResize 2 | 3 | Angular module for managing resize events in your applications. Some of goals of this project worth noting include: 4 | 5 | * Lightweight, flexible and easy to use 6 | * Bind resize event handler one time for entire app 7 | * Throttle window resize events 8 | 9 | ## Usage 10 | ngResize is composed of a `provider`, a `service`, and a `directive`. The service can be injected into other modules and used to programatically bind `resize` events in angular applications. The `provider` can be injected into your app's configuration phase to set things like throttle time. 11 | 12 | Set `ngResize` as a dependency in your module: 13 | 14 | ```javascript 15 | var app = angular.module('YourApp', ['ngResize']); 16 | ``` 17 | 18 | ### Provider 19 | ```javascript 20 | app.config(function(resizeProvider) { 21 | 22 | // set throttle time 23 | resizeProvider.throttle = 100; 24 | 25 | // don't bind resize events on service initialization 26 | resizeProvider.initBind = false; 27 | 28 | }); 29 | ``` 30 | 31 | #### Properties 32 | name | description 33 | ---- | ---- 34 | `throttle` | Throttle time for resize events, default is `32` (30 fps) 35 | `initBind` | Boolean to determine if we should bind resize event when service object is created, default is `true` 36 | 37 | ### Service 38 | ```javascript 39 | app.directive('someDirective', function(resize) { 40 | return { 41 | controller: function($scope) { 42 | 43 | }, 44 | link: function($scope, $elem, $attr, ctrl) { 45 | 46 | // on resize event, set dimensions 47 | // $event, and data arguments are available 48 | $scope.$on('resize', function($event, data) { 49 | $scope.width = data.width; 50 | $scope.height = data.height; 51 | }); 52 | 53 | } 54 | }; 55 | }); 56 | ``` 57 | *The event listener callback accepts two arguments: `$event`, and `data`* 58 | 59 | #### Methods 60 | name | description 61 | ---- | ---- 62 | `getThrottle()` | Returns current throttle time 63 | `setThrottle(integer)` | Sets current throttle time, applies to entire app 64 | `trigger([$scope])` | `$broadcast` a `resize` event from specified `$scope` or `$rootScope` 65 | `bind()` | Bind resize event to `$window`, only useful if `initBind = false` or if event has been previously unbound 66 | `unbind()` | Unbinds `resize` even from `$window` 67 | 68 | ### Directive 69 | Something worth noting is that when the `resize` event is triggered, `$timeout` is used to debounce the expression to the end of the current `$digest`. This is to try and ensure that any costly calculations you might be doing won't interfere with the current `$digest` cycle. This approach was taken because resize events are often not critical functionality points, but necessary to maintain ui/ux stability. The goal is to provide efficient, useful access to `resize` events without crippling the ui. 70 | 71 | ```html 72 |
73 | ``` 74 | *Two arguments are available to directive expressions: `$event`, and `data`* 75 | 76 | ## Roadmap 77 | A few things I'm interested in pursuing with this project in the future are: 78 | 79 | * option to disable `$rootScope` `$broadcast` 80 | * ability to subscribe and unsubscribe `$scopes` from resize `$broadcast` 81 | * ability to bind `resize` events to `$elements` 82 | * manage detaching of `$scopes` and `$elements` when `$destroyed` 83 | * set unique throttle time for each `$scope` or `$element` 84 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | (function(window, angular) { 2 | 3 | var ngResize = angular.module('ngResize', []); 4 | 5 | ngResize.provider('resize', function resizeProvider() { 6 | 7 | // default throttle time 30fps 8 | this.throttle = 32; 9 | 10 | // bind to window resize event when service created 11 | this.initBind = 1; 12 | 13 | // service object 14 | this.$get = function($rootScope, $window, $interval) { 15 | 16 | var resize = this; 17 | var bound = 0; 18 | var timer = 0; 19 | var resized = 0; 20 | var $w = angular.element($window); 21 | 22 | // api to set throttle amount 23 | function setThrottle(time) { 24 | resize.throttle = time; 25 | } 26 | 27 | // api to get current throttle amount 28 | function getThrottle() { 29 | return resize.throttle; 30 | } 31 | 32 | // trigger a resize event on provided $scope or $rootScope 33 | function trigger($scope) { 34 | 35 | $scope = $scope || $rootScope; 36 | 37 | $scope.$broadcast('resize', { 38 | width: $window.innerWidth, 39 | height: $window.innerHeight 40 | }); 41 | 42 | } 43 | 44 | // bind to window resize event, will only ever be bound 45 | // one time for entire app 46 | function bind() { 47 | 48 | if (!bound) { 49 | 50 | $w.on('resize', function() { 51 | 52 | if (!resized) { 53 | timer = $interval(function() { 54 | if (resized) { 55 | resized = 0; 56 | $interval.cancel(timer); 57 | resize.trigger(); 58 | } 59 | }, resize.throttle); 60 | } 61 | 62 | resized = 1; 63 | 64 | }); 65 | 66 | bound = 1; 67 | 68 | $w.triggerHandler('resize'); 69 | 70 | } 71 | 72 | } 73 | 74 | // unbind window resize event 75 | function unbind() { 76 | 77 | if (bound) { 78 | $w.off('resize'); 79 | bound = 0; 80 | } 81 | 82 | } 83 | 84 | this.getThrottle = getThrottle; 85 | this.setThrottle = setThrottle; 86 | this.trigger = trigger; 87 | this.bind = bind; 88 | this.unbind = unbind; 89 | 90 | // bind window resize event when service created 91 | if (resize.initBind) { 92 | resize.bind(); 93 | } 94 | 95 | }; 96 | 97 | }); 98 | 99 | ngResize.directive('ngResize', function($parse, $timeout, resize) { 100 | 101 | return { 102 | 103 | compile: function compile(elem, attr) { 104 | 105 | var fn = $parse(attr['ngResize']); 106 | 107 | return function resizeDirective($scope, $elem, $attr) { 108 | $scope.$on('resize', function($event, data) { 109 | $timeout(function() { 110 | $scope.$apply(function() { 111 | fn($scope, { $event: $event, data: data }); 112 | }); 113 | }); 114 | }); 115 | }; 116 | 117 | } 118 | 119 | }; 120 | 121 | }); 122 | 123 | })(window, window.angular); 124 | -------------------------------------------------------------------------------- /dist/ng-resize.js: -------------------------------------------------------------------------------- 1 | (function(window, angular) { 2 | 3 | var ngResize = angular.module('ngResize', []); 4 | 5 | ngResize.provider('resize', function resizeProvider() { 6 | 7 | // default throttle time 30fps 8 | this.throttle = 32; 9 | 10 | // bind to window resize event when service created 11 | this.initBind = 1; 12 | 13 | // service object 14 | this.$get = ["$rootScope", "$window", "$interval", function($rootScope, $window, $interval) { 15 | 16 | var resize = this; 17 | var bound = 0; 18 | var timer = 0; 19 | var resized = 0; 20 | var $w = angular.element($window); 21 | 22 | // api to set throttle amount 23 | function setThrottle(time) { 24 | resize.throttle = time; 25 | } 26 | 27 | // api to get current throttle amount 28 | function getThrottle() { 29 | return resize.throttle; 30 | } 31 | 32 | // trigger a resize event on provided $scope or $rootScope 33 | function trigger($scope) { 34 | 35 | $scope = $scope || $rootScope; 36 | 37 | $scope.$broadcast('resize', { 38 | width: $window.innerWidth, 39 | height: $window.innerHeight 40 | }); 41 | 42 | } 43 | 44 | // bind to window resize event, will only ever be bound 45 | // one time for entire app 46 | function bind() { 47 | 48 | if (!bound) { 49 | 50 | $w.on('resize', function() { 51 | 52 | if (!resized) { 53 | timer = $interval(function() { 54 | if (resized) { 55 | resized = 0; 56 | $interval.cancel(timer); 57 | resize.trigger(); 58 | } 59 | }, resize.throttle); 60 | } 61 | 62 | resized = 1; 63 | 64 | }); 65 | 66 | bound = 1; 67 | 68 | $w.triggerHandler('resize'); 69 | 70 | } 71 | 72 | } 73 | 74 | // unbind window resize event 75 | function unbind() { 76 | 77 | if (bound) { 78 | $w.off('resize'); 79 | bound = 0; 80 | } 81 | 82 | } 83 | 84 | this.getThrottle = getThrottle; 85 | this.setThrottle = setThrottle; 86 | this.trigger = trigger; 87 | this.bind = bind; 88 | this.unbind = unbind; 89 | 90 | // bind window resize event when service created 91 | if (resize.initBind) { 92 | resize.bind(); 93 | } 94 | 95 | }]; 96 | 97 | }); 98 | 99 | ngResize.directive('ngResize', ["$parse", "$timeout", "resize", function($parse, $timeout, resize) { 100 | 101 | return { 102 | 103 | compile: function compile(elem, attr) { 104 | 105 | var fn = $parse(attr['ngResize']); 106 | 107 | return function resizeDirective($scope, $elem, $attr) { 108 | $scope.$on('resize', function($event, data) { 109 | $timeout(function() { 110 | $scope.$apply(function() { 111 | fn($scope, { $event: $event, data: data }); 112 | }); 113 | }); 114 | }); 115 | }; 116 | 117 | } 118 | 119 | }; 120 | 121 | }]); 122 | 123 | })(window, window.angular); 124 | --------------------------------------------------------------------------------