├── CHANGELOG.md ├── dev ├── template.html ├── index.html └── app.js ├── .gitignore ├── images ├── angular.jpg └── newPic.png ├── .travis.yml ├── bower.json ├── package.json ├── karma.conf.js ├── gulpfile.js ├── src └── ng-component.js ├── dist └── ng-component.js ├── specs └── providerSpec.js └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dev/template.html: -------------------------------------------------------------------------------- 1 |
yoooo {{testing}}
2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | coverage -------------------------------------------------------------------------------- /images/angular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maseh87/ngComponent/HEAD/images/angular.jpg -------------------------------------------------------------------------------- /images/newPic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maseh87/ngComponent/HEAD/images/newPic.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_script: 5 | - npm install -g bower 6 | - bower install 7 | after_success: 8 | - npm run coveralls -------------------------------------------------------------------------------- /dev/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dev test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngCompoment", 3 | "version": "0.1.1", 4 | "homepage": "https://github.com/maseh87/ngComponent", 5 | "authors": [ 6 | "Hendrixer ", 7 | "Maseh87 " 8 | ], 9 | "description": "Easier solution for understanding and using angular's directives", 10 | "main": "ng-compontent.js", 11 | "moduleType": [ 12 | "es6", 13 | "node" 14 | ], 15 | "keywords": [ 16 | "angular", 17 | "ng", 18 | "directives", 19 | "components", 20 | "widget", 21 | "widget" 22 | ], 23 | "license": "MIT", 24 | "ignore": [ 25 | "**/.*", 26 | "node_modules", 27 | "bower_components", 28 | "test", 29 | "tests", 30 | "specs", 31 | "dev", 32 | "src", 33 | "coverage", 34 | "images" 35 | ], 36 | "devDependencies": { 37 | "angular": "~1.3.13", 38 | "angular-mocks": "~1.3.13" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dev/app.js: -------------------------------------------------------------------------------- 1 | angular.module("app", ['ngComponent']) 2 | 3 | /*/ 4 | Uncomment code blow to get started 5 | /*/ 6 | 7 | // .directive('demo', function(Component) { 8 | // return new Component() 9 | // .on('click', function(e, scope){ 10 | // scope.message = 'changed'; 11 | // console.log('yooooo') 12 | // }) 13 | // .scopeOptions({ 14 | // 'code': 'one-way' 15 | // }) 16 | // .ready(function(scope, element, attrs){ 17 | // // console.log('ready') 18 | // scope.message = 'Default'; 19 | // console.log(attrs, 'attrs'); 20 | // }) 21 | // .start(function() { 22 | // // console.log('start'); 23 | // }) 24 | // .beforeReady(function(){ 25 | // // console.log('beforeReady'); 26 | // }) 27 | // .setTemplate('

the message: {{ message }}

'); 28 | // }) 29 | 30 | // .directive('other', function(Component){ 31 | // var count = 0; 32 | // var component = new Component() 33 | // .on('click', function(event, scope){ 34 | 35 | // scope.thing = count++; 36 | // }) 37 | // .ready(function(event, scope){ 38 | // scope.thing = 'thing'; 39 | // }) 40 | // .start(function(){ 41 | 42 | // }) 43 | // .setTemplate('

count{{ thing }}

') 44 | // .scopeOptions({}); 45 | 46 | // return component; 47 | // }) 48 | 49 | // .directive('testing', function(Component){ 50 | // var component = new Component({controller: function($scope){$scope.testing}}) 51 | // .on('click', function(){ 52 | // console.log('im testing'); 53 | // }) 54 | // .ready(function(event, scope){ 55 | // scope.thing = 'thing'; 56 | // }) 57 | // .setTemplateUrl('template.html') 58 | // .scopeOptions({}) 59 | 60 | // return component; 61 | // }); 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngComponent", 3 | "version": "0.1.0", 4 | "description": "Easier solution for understanding and using angular's directives", 5 | "main": "ng-component.js", 6 | "scripts": { 7 | "test": "gulp travis", 8 | "coveralls": "cat coverage/*/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/maseh87/ngComponent.git" 13 | }, 14 | "keywords": [ 15 | "angular", 16 | "widget", 17 | "directive", 18 | "component", 19 | "ng" 20 | ], 21 | "author": "", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/maseh87/ngComponent/issues" 25 | }, 26 | "homepage": "https://github.com/maseh87/ngComponent", 27 | "devDependencies": { 28 | "browser-sync": "^2.0.1", 29 | "chai": "^2.0.0", 30 | "conventional-changelog": "0.0.11", 31 | "coveralls": "^2.11.2", 32 | "del": "^1.1.1", 33 | "gulp": "^3.8.11", 34 | "gulp-bump": "^0.2.2", 35 | "gulp-jshint": "^1.9.2", 36 | "gulp-load-plugins": "^0.8.0", 37 | "gulp-ng-annotate": "^0.5.2", 38 | "gulp-sourcemaps": "^1.3.0", 39 | "gulp-uglify": "^1.1.0", 40 | "gulp-util": "^3.0.3", 41 | "jshint-stylish": "^1.0.0", 42 | "karma": "^0.12.31", 43 | "karma-chai": "^0.1.0", 44 | "karma-chrome-launcher": "^0.1.7", 45 | "karma-mocha": "^0.1.10", 46 | "karma-mocha-reporter": "^0.3.2", 47 | "karma-phantomjs-launcher": "^0.1.4", 48 | "mocha": "^2.1.0", 49 | "run-sequence": "^1.0.2", 50 | "vinyl-paths": "^1.0.0", 51 | "yargs": "^3.5.3" 52 | }, 53 | "dependencies": { 54 | "karma-coverage": "^0.2.7", 55 | "karma-coveralls": "^0.1.5", 56 | "mocha-lcov-reporter": "0.0.2" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Sun Feb 15 2015 20:00:22 GMT-0800 (PST) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['mocha', 'chai'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | 'bower_components/angular/angular.min.js', 19 | 'bower_components/angular-mocks/angular-mocks.js', 20 | 'specs/**/**.js', 21 | 'src/ng-component.js' 22 | ], 23 | 24 | // list of files to exclude 25 | exclude: [ 26 | ], 27 | 28 | 29 | // preprocess matching files before serving them to the browser 30 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 31 | preprocessors: { 32 | '**/src/*.js': 'coverage' 33 | }, 34 | 35 | coverageReporter: { 36 | type : 'lcov', 37 | dir : 'coverage/' 38 | }, 39 | 40 | // test results reporter to use 41 | // possible values: 'dots', 'progress' 42 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 43 | reporters: ['mocha', 'coverage', 'coveralls'], 44 | 45 | coverallsReporter: { 46 | repoToken: process.env.COVERALLS_REPO_TOKEN 47 | }, 48 | 49 | // web server port 50 | port: 9876, 51 | 52 | 53 | // enable / disable colors in the output (reporters and logs) 54 | colors: true, 55 | 56 | 57 | // level of logging 58 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 59 | logLevel: config.LOG_INFO, 60 | 61 | 62 | // enable / disable watching file and executing tests whenever any file changes 63 | autoWatch: false, 64 | 65 | 66 | // start these browsers 67 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 68 | browsers: ['Chrome'], 69 | 70 | 71 | // Continuous Integration mode 72 | // if true, Karma captures browsers, runs the tests and exits 73 | singleRun: false 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var $ = require('gulp-load-plugins')(); 3 | var bs = require('browser-sync'); 4 | var reload = bs.reload; 5 | var del = require('del'); 6 | var vf = require('vinyl-paths'); 7 | var sync = require('run-sequence'); 8 | var karma = require('karma').server; 9 | var changelog = require('conventional-changelog'); 10 | var fs = require('fs'); 11 | var bump = require('gulp-bump'); 12 | var yargs = require('yargs'); 13 | 14 | var argv = yargs.argv, 15 | validBumpTypes = "major|minor|patch|prerelease".split("|"), 16 | Bump = (argv.bump || 'patch').toLowerCase(); 17 | 18 | if(validBumpTypes.indexOf(Bump) === -1) { 19 | throw new Error('Unrecognized bump "' + Bump + '".'); 20 | } 21 | 22 | var args = { bump: Bump }; 23 | 24 | // Paths to all src files 25 | var paths = { 26 | src: ['src/**/*.js'], 27 | dev: ['dev/index.html', 'dev/app.js', 'dev/template.html'], 28 | dist: './dist', 29 | specs: 'specs/**/*.js', 30 | doc: ['./docs'] 31 | }; 32 | 33 | // lint the coffee 34 | gulp.task('lint', function() { 35 | return gulp.src(paths.src) 36 | .pipe($.jshint()) 37 | .pipe($.jshint.reporter('jshint-stylish')) 38 | .pipe($.ngAnnotate()) 39 | // .pipe($.uglify()) 40 | .pipe(gulp.dest(paths.dist)); 41 | }); 42 | 43 | gulp.task('clean', function() { 44 | return gulp.src([paths.dist + '/**/*.**']) 45 | .pipe(vf(del)); 46 | }); 47 | 48 | 49 | // run dev env for visually inspecting the plugin 50 | gulp.task('dev', ['build'], function(done) { 51 | bs({ 52 | port: 9500, 53 | server: { 54 | baseDir: ['./dev', './dist', './bower_components'] 55 | } 56 | }, done); 57 | 58 | gulp.watch(paths.dev, reload); 59 | gulp.watch(paths.src, ['build', reload]); 60 | }); 61 | 62 | // run karma test 63 | gulp.task('test', function(done) { 64 | karma.start({ 65 | configFile: __dirname + '/karma.conf.js', 66 | singleRun: true 67 | }, done); 68 | }); 69 | 70 | // for ci, use phantom 71 | gulp.task('test:ci', function(done){ 72 | karma.start({ 73 | configFile: __dirname + '/karma.conf.js', 74 | singleRun: true, 75 | browsers: ['PhantomJS'] 76 | }, done); 77 | }); 78 | 79 | gulp.task('travis', function(done) { 80 | sync('build', 'test:ci', done); 81 | }); 82 | 83 | gulp.task('bump-version', function(){ 84 | return gulp.src(['./package.json', './bower.json']) 85 | .pipe(bump({type:args.bump })) //major|minor|patch|prerelease 86 | .pipe(gulp.dest('./')); 87 | }); 88 | 89 | gulp.task('changelog', function(callback) { 90 | var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); 91 | 92 | return changelog({ 93 | repository: pkg.repository.url, 94 | version: pkg.version, 95 | file: './CHANGELOG.md', 96 | subtitle: argv.codename || '' 97 | }, function(err, log) { 98 | fs.writeFileSync('./CHANGELOG.md', log); 99 | }); 100 | }); 101 | 102 | gulp.task('build', ['clean'], function(done) { 103 | sync('lint', done); 104 | }); 105 | 106 | gulp.task('del:change', function() { 107 | return gulp.src('./CHANGELOG.md') 108 | .pipe(vf(del)); 109 | }); 110 | 111 | gulp.task('release', function(done){ 112 | return sync( 113 | 'build', 114 | 'lint', 115 | 'bump-version', 116 | 'del:change', 117 | 'changelog', 118 | done 119 | ); 120 | }); 121 | 122 | gulp.task('default', ['build'], function() { 123 | gulp.watch(paths.src, ['lint']); 124 | }); 125 | -------------------------------------------------------------------------------- /src/ng-component.js: -------------------------------------------------------------------------------- 1 | 2 | angular.module('ngComponent', []) 3 | 4 | .provider('Component', function() { 5 | // have to make this local, not global 6 | // all directives will use this one object them 7 | // or clear it when they make a new one 8 | // var cache = { 9 | // domEvents: {} 10 | // }; //events 11 | 12 | var globe = {}; 13 | 14 | var getDefaults = function() { 15 | var cache = { 16 | domEvents: {} 17 | }; 18 | var that = this; 19 | 20 | var defaults = { 21 | template: '
Default ngComponent template, go change it
', 22 | compile: function() { 23 | if (cache.start) { 24 | cache.start.apply(this, arguments); 25 | } 26 | 27 | return { 28 | pre: function() { 29 | if (cache.beforeReadyFn) { 30 | cache.beforeReadyFn.apply(that, arguments); 31 | } 32 | }, 33 | 34 | post: function(scope, element) { 35 | 36 | var args = arguments; 37 | angular.forEach(cache.domEvents, function(cb, event) { 38 | 39 | element.on(event, function() { 40 | scope.$apply(function(e) { 41 | var locals = [].slice.call(args); 42 | locals.unshift(e); 43 | 44 | cb.apply(that, locals); 45 | 46 | }); 47 | }); 48 | }); 49 | 50 | 51 | scope.$on('$destroy', function() { 52 | angular.forEach(cache.domEvents, function(cb, event) { 53 | element.off(event, cb); 54 | }); 55 | }); 56 | 57 | if (cache.readyFn) { 58 | cache.readyFn.apply(that, args); 59 | } 60 | 61 | if (cache._template) { 62 | element.html(cache._template); 63 | globe.$compile(element.contents())(scope); 64 | } 65 | } 66 | }; 67 | }, 68 | 69 | transclude: false, 70 | restrict: 'EA', 71 | replace: false, 72 | scope: false 73 | }; 74 | 75 | return { 76 | defaults: angular.copy(defaults), 77 | cache: cache 78 | }; 79 | }; 80 | 81 | function Component(config) { 82 | 83 | var data = getDefaults.call(this); 84 | this._cache = data.cache; 85 | angular.extend(this, data.defaults, config || {}); 86 | 87 | } 88 | 89 | Component.prototype.children = function (option) { 90 | if (option === true) { 91 | this.transclude = true; 92 | } 93 | 94 | return this; 95 | }; 96 | 97 | Component.prototype.setTemplate = function(template) { 98 | this._cache._template = template; 99 | return this; 100 | }; 101 | 102 | Component.prototype.setTemplateUrl = function(url) { 103 | this._cache._templateUrl = url; 104 | return this; 105 | }; 106 | 107 | Component.prototype.scopeOptions = function (options) { 108 | if (options === 'parent') { 109 | this.scope = false; 110 | } 111 | 112 | if (options === 'child') { 113 | this.scope = true; 114 | } 115 | 116 | if (angular.isObject(options)) { 117 | this.scope = {}; 118 | 119 | angular.forEach(options, function(type, attrName) { 120 | if (type === 'one-way' || type === 'attrValue') { 121 | this.scope[attrName] = '@'; 122 | } 123 | 124 | if (type === 'two-way') { 125 | this.scope[attrName] = '='; 126 | } 127 | 128 | if (type === 'function') { 129 | this.scope[attrName] = '&'; 130 | } 131 | }.bind(this)); 132 | } 133 | 134 | return this; 135 | }; 136 | 137 | //should return promise too 138 | Component.prototype.ready = function (cb) { 139 | this._cache.readyFn = cb || function(){}; 140 | return this; 141 | }; 142 | 143 | Component.prototype.on = function(event, cb) { 144 | this._cache.domEvents[event] = cb; 145 | return this; 146 | }; 147 | 148 | Component.prototype.beforeReady = function(cb) { 149 | this._cache.beforeReadyFn = cb || function(){}; 150 | return this; 151 | }; 152 | 153 | Component.prototype.start = function(cb) { 154 | this._cache.start = cb || function(){}; 155 | return this; 156 | }; 157 | 158 | return { 159 | $get: function ($compile) { 160 | globe.$compile = $compile; 161 | return Component; 162 | }, 163 | 164 | setDefaults: function(config) { 165 | return angular.extend(getDefaults().defaults, config); 166 | } 167 | }; 168 | 169 | }); 170 | -------------------------------------------------------------------------------- /dist/ng-component.js: -------------------------------------------------------------------------------- 1 | 2 | angular.module('ngComponent', []) 3 | 4 | .provider('Component', function() { 5 | // have to make this local, not global 6 | // all directives will use this one object them 7 | // or clear it when they make a new one 8 | // var cache = { 9 | // domEvents: {} 10 | // }; //events 11 | 12 | var globe = {}; 13 | 14 | var getDefaults = function() { 15 | var cache = { 16 | domEvents: {} 17 | }; 18 | var that = this; 19 | 20 | var defaults = { 21 | template: '
Default ngComponent template, go change it
', 22 | compile: function() { 23 | if (cache.start) { 24 | cache.start.apply(this, arguments); 25 | } 26 | 27 | return { 28 | pre: function() { 29 | if (cache.beforeReadyFn) { 30 | cache.beforeReadyFn.apply(that, arguments); 31 | } 32 | }, 33 | 34 | post: function(scope, element) { 35 | 36 | var args = arguments; 37 | angular.forEach(cache.domEvents, function(cb, event) { 38 | 39 | element.on(event, function() { 40 | scope.$apply(function(e) { 41 | var locals = [].slice.call(args); 42 | locals.unshift(e); 43 | 44 | cb.apply(that, locals); 45 | 46 | }); 47 | }); 48 | }); 49 | 50 | 51 | scope.$on('$destroy', function() { 52 | angular.forEach(cache.domEvents, function(cb, event) { 53 | element.off(event, cb); 54 | }); 55 | }); 56 | 57 | if (cache.readyFn) { 58 | cache.readyFn.apply(that, args); 59 | } 60 | 61 | if (cache._template) { 62 | element.html(cache._template); 63 | globe.$compile(element.contents())(scope); 64 | } 65 | } 66 | }; 67 | }, 68 | 69 | transclude: false, 70 | restrict: 'EA', 71 | replace: false, 72 | scope: false 73 | }; 74 | 75 | return { 76 | defaults: angular.copy(defaults), 77 | cache: cache 78 | }; 79 | }; 80 | 81 | function Component(config) { 82 | 83 | var data = getDefaults.call(this); 84 | this._cache = data.cache; 85 | angular.extend(this, data.defaults, config || {}); 86 | 87 | } 88 | 89 | Component.prototype.children = function (option) { 90 | if (option === true) { 91 | this.transclude = true; 92 | } 93 | 94 | return this; 95 | }; 96 | 97 | Component.prototype.setTemplate = function(template) { 98 | this._cache._template = template; 99 | return this; 100 | }; 101 | 102 | Component.prototype.setTemplateUrl = function(url) { 103 | this._cache._templateUrl = url; 104 | return this; 105 | }; 106 | 107 | Component.prototype.scopeOptions = function (options) { 108 | if (options === 'parent') { 109 | this.scope = false; 110 | } 111 | 112 | if (options === 'child') { 113 | this.scope = true; 114 | } 115 | 116 | if (angular.isObject(options)) { 117 | this.scope = {}; 118 | 119 | angular.forEach(options, function(type, attrName) { 120 | if (type === 'one-way' || type === 'attrValue') { 121 | this.scope[attrName] = '@'; 122 | } 123 | 124 | if (type === 'two-way') { 125 | this.scope[attrName] = '='; 126 | } 127 | 128 | if (type === 'function') { 129 | this.scope[attrName] = '&'; 130 | } 131 | }.bind(this)); 132 | } 133 | 134 | return this; 135 | }; 136 | 137 | //should return promise too 138 | Component.prototype.ready = function (cb) { 139 | this._cache.readyFn = cb || function(){}; 140 | return this; 141 | }; 142 | 143 | Component.prototype.on = function(event, cb) { 144 | this._cache.domEvents[event] = cb; 145 | return this; 146 | }; 147 | 148 | Component.prototype.beforeReady = function(cb) { 149 | this._cache.beforeReadyFn = cb || function(){}; 150 | return this; 151 | }; 152 | 153 | Component.prototype.start = function(cb) { 154 | this._cache.start = cb || function(){}; 155 | return this; 156 | }; 157 | 158 | return { 159 | $get: ["$compile", function ($compile) { 160 | globe.$compile = $compile; 161 | return Component; 162 | }], 163 | 164 | setDefaults: function(config) { 165 | return angular.extend(getDefaults().defaults, config); 166 | } 167 | }; 168 | 169 | }); 170 | -------------------------------------------------------------------------------- /specs/providerSpec.js: -------------------------------------------------------------------------------- 1 | describe('ngComponent', function(){ 2 | var ngComponentProvider, 3 | mockModule, 4 | defaults, 5 | testDirectiveObject; 6 | 7 | beforeEach(function() { 8 | mockModule = angular.module('fake', function() {}); 9 | 10 | mockModule.config(function(ComponentProvider) { 11 | ngComponentProvider = ComponentProvider; 12 | testDirectiveObject = ComponentProvider.$get(); 13 | testDirectiveObject = new testDirectiveObject(); 14 | }); 15 | 16 | module('ngComponent', 'fake'); 17 | 18 | inject(function(){}); 19 | }); 20 | 21 | describe('Defaults', function() { 22 | it('should have a function to override the defaults', function() { 23 | expect(ngComponentProvider.setDefaults).to.be.a('function'); 24 | 25 | defaults = ngComponentProvider.setDefaults(); 26 | }); 27 | 28 | it('should have a defaults object', function() { 29 | expect(defaults).to.be.a('object'); 30 | }); 31 | it('should have a transclude property equal to false', function() { 32 | expect(defaults.transclude).to.be.false; 33 | }); 34 | it('should have a restrict property equal to EA', function() { 35 | expect(defaults.restrict).to.equal('EA'); 36 | }); 37 | it('should have a replace property equal to false', function() { 38 | expect(defaults.replace).to.be.false; 39 | }); 40 | it('should have a scope property equal to false', function() { 41 | expect(defaults.scope).to.be.false; 42 | }); 43 | it('should have a default template', function() { 44 | expect(defaults.template).to.be.a('string'); 45 | }); 46 | describe('compile defaults method', function() { 47 | it('should have a compile property', function() { 48 | expect(defaults.compile).to.be.a('function'); 49 | }); 50 | it('should return an object', function() { 51 | expect(defaults.compile()).to.be.an('object'); 52 | }); 53 | describe('pre and post link', function() { 54 | it('should have a prelink method', function() { 55 | var obj = defaults.compile(); 56 | expect(obj.pre).to.be.a('function'); 57 | }); 58 | it('should have a postlink method', function() { 59 | var obj = defaults.compile(); 60 | expect(obj.post).to.be.a('function'); 61 | }); 62 | }); 63 | }); 64 | }); 65 | 66 | describe('Directive options', function() { 67 | it('should be an object', function() { 68 | expect(testDirectiveObject).to.be.an('object'); 69 | }); 70 | 71 | describe('ready method', function() { 72 | it('should have a ready method', function() { 73 | expect(testDirectiveObject.ready).to.be.a('function'); 74 | }); 75 | it('should take return an object', function() { 76 | var obj = testDirectiveObject.ready(function() { 77 | console.log('yo'); 78 | }); 79 | expect(obj).to.be.an('object'); 80 | }); 81 | }); 82 | 83 | describe('on method', function() { 84 | it('should have an on method', function() { 85 | expect(testDirectiveObject.on).to.be.a('function'); 86 | }); 87 | it('should return an object', function() { 88 | expect(testDirectiveObject.on('click', function(){})).to.be.an('object'); 89 | }); 90 | }); 91 | describe('start', function() { 92 | it('should have a start method', function() { 93 | expect(testDirectiveObject.start).to.be.a('function'); 94 | }); 95 | it('should return an object', function() { 96 | expect(testDirectiveObject.start(function(){})).to.be.an('object'); 97 | }); 98 | it('should set a default callback', function() { 99 | var obj = testDirectiveObject.start(); 100 | expect(testDirectiveObject._cache.start).to.be.a('function'); 101 | }); 102 | }); 103 | 104 | describe('scope options', function() { 105 | it('should have a scope options method', function() { 106 | expect(testDirectiveObject.scopeOptions).to.be.a('function'); 107 | }); 108 | it('should return an object', function() { 109 | expect(testDirectiveObject.scopeOptions('parent')).to.be.an('object'); 110 | }); 111 | it('should take 3 different arguments', function() { 112 | expect(testDirectiveObject.scopeOptions('parent')).to.be.an('object'); 113 | expect(testDirectiveObject.scopeOptions('child')).to.be.an('object'); 114 | // expect(testDirectiveObject.scopeOptions({})).to.be.an('object'); 115 | }); 116 | }); 117 | 118 | describe('set template', function() { 119 | it('should have a set template method', function() { 120 | expect(testDirectiveObject.setTemplate).to.be.an('function'); 121 | }); 122 | it('should return an object', function() { 123 | expect(testDirectiveObject.setTemplate('parent')).to.be.an('object'); 124 | }); 125 | }); 126 | 127 | describe('children', function() { 128 | it('should have a children method', function() { 129 | expect(testDirectiveObject.children).to.be.an('function'); 130 | }); 131 | it('should return an object', function() { 132 | expect(testDirectiveObject.children()).to.be.an('object'); 133 | }); 134 | it('should set translude to true', function() { 135 | var obj = testDirectiveObject.children(true); 136 | expect(obj.transclude).to.eql(true); 137 | }); 138 | }); 139 | 140 | describe('beforeReady', function() { 141 | it('should have a beforeReady method', function() { 142 | expect(testDirectiveObject.beforeReady).to.be.a('function'); 143 | }); 144 | it('should return an object', function() { 145 | expect(testDirectiveObject.beforeReady()).to.be.an('object'); 146 | }); 147 | }); 148 | }); 149 | }); 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NgComponent [![Build Status](https://travis-ci.org/maseh87/ngComponent.svg?branch=master)](https://travis-ci.org/maseh87/ngComponent) [![Coverage Status](https://coveralls.io/repos/maseh87/ngComponent/badge.svg?branch=master)](https://coveralls.io/r/maseh87/ngComponent?branch=master) 2 | 3 | ## An easier way to write angular directives 4 | 5 | ![angular directives](/images/newPic.png) 6 | 7 | The easiest way to start using NgComponent is with bower 8 | ```sh 9 | $ bower install --save ng-components 10 | ``` 11 | 12 | ###How to use NgComponent 13 | + NgComponent uses a more "jQuery" approach to directives so it makes more sense. 14 | 15 | + To start, inject the Component service into your directive and create a default directive definition object: 16 | + 17 | ```javascript 18 | angular.module('myApp', [ 19 | 'ngComponent' 20 | ]) 21 | .directive('myNewDirective', function(){ 22 | var component = new Component(); 23 | return component; 24 | }); 25 | ``` 26 | ######The component object gives you access to methods to control your directives functionality. 27 | 28 | #Methods to utilize 29 | 30 | ###.ready (formally the link function) 31 | Call ready to gain access to your directives scope, jQuery wrapped element and the attributes on the directive: 32 | ```javascript 33 | angular.module('myApp', [ 34 | 'ngComponent' 35 | ]) 36 | .directive('myNewDirective', function(){ 37 | var component = new Component() 38 | .ready(function(scope, element, attributes){ 39 | 40 | }); 41 | 42 | return component; 43 | }); 44 | ``` 45 | ###.setTemplate (formally template) 46 | To give your directive a template use the setTemplate method: 47 | ```javascript 48 | .directive('myNewDirective', function(){ 49 | var component = new Component() 50 | .on('click', function(event){ 51 | console.log('I clicked!'); 52 | }) 53 | .setTemplate('

the message: {{ message }}

'); 54 | 55 | return component; 56 | }); 57 | ``` 58 | ###.scopeOptions (formally scope) 59 | To configure the scope of your object use the scopeOptions method: 60 | ```javascript 61 | .directive('myNewDirective', function(){ 62 | var component = new Component() 63 | 64 | // set to 'parent' if you want to use the parent scope 65 | .scopeOptions('parent'); 66 | 67 | // set to 'child' if you want to have an isolated scope 68 | .scopeOptions('child'); 69 | 70 | // pass an object of attribute names mapped to binding types 71 | // suppored binding types: 'one-way', 'two-way', 'function' 72 | .scopeOptions({ 73 | myOneWayAttr: 'one-way', 74 | myTwoWayAttr: 'two-way', 75 | myEventTriggeringAttr: 'function' 76 | }); 77 | 78 | return component; 79 | }); 80 | ``` 81 | ###.beforeReady (formally pre-link) 82 | The beforeReady method lets you configure your directive before the ready function is invoked but after the start function runs: 83 | ```javascript 84 | .directive('myNewDirective', function(){ 85 | var component = new Component() 86 | .beforeReady(function(scope, element, attributes){ 87 | //this method will execute before the ready method 88 | }); 89 | 90 | return component; 91 | }); 92 | ``` 93 | 94 | ###.start (formally compile) 95 | The before ready method give you access to the raw directive before it is compiled and given its scope: 96 | ```javascript 97 | .directive('myNewDirective', function(){ 98 | var component = new Component() 99 | .start(function(element, attributes){ 100 | //This method will execute first 101 | //your directive will not have it's own scope when this function 102 | //is executed. 103 | }); 104 | 105 | return component; 106 | }); 107 | ``` 108 | ###.on 109 | Register event listeners for your directive by using the on function: 110 | ```javascript 111 | .directive('myNewDirective', function(){ 112 | var component = new Component() 113 | .on('click', function(event){ 114 | console.log('I clicked!'); 115 | }); 116 | 117 | return component; 118 | }); 119 | ``` 120 | #Overiding defaults 121 | You can pass in a directive object to `Component` to override defaults. 122 | ```javascript 123 | angular.module('myApp', [ 124 | 'ngComponent' 125 | ]) 126 | .directive('myNewComponent', function(){ 127 | var component = new Component({ 128 | template: '

{{ ready }}

', 129 | restrict: 'EA' 130 | }) 131 | .ready(function(scope, element, attributes){ 132 | scope.ready = '(づ ̄ ³ ̄)づ'; 133 | }); 134 | return component; 135 | }); 136 | ``` 137 | 138 | # Subclassing components 139 | Since directives are classes now, you can subclass them 140 | 141 | ```javascript 142 | angular.module('myApp', [ 143 | 'ngComponent' 144 | ]) 145 | .factory('CardComponent', function(Component){ 146 | class CardComponet extends Component { 147 | constructor(opts){ 148 | super(opts); 149 | this.templateUrl = 'path/to/card-component.html'; 150 | this.restrict = 'E'; 151 | this.replace = true; 152 | } 153 | } 154 | 155 | return CardComponent; 156 | }) 157 | .directive('feedCard', function(CardComponent){ 158 | return new CardComponent({ 159 | .beforeReady(function(scope){ 160 | scope.dataForChildDirectives = [1,2,3]; 161 | }) 162 | .ready(function(scope, element, attributes){ 163 | scope.ready = '(づ ̄ ³ ̄)づ'; 164 | }) 165 | .on('mouseenter', function(evt, scope, element){ 166 | // no need to $apply here 167 | // we do! 168 | element.css('color', 'red'); 169 | scope.thing = true; 170 | }) 171 | }); 172 | ``` 173 | # Features 174 | * `.on()` DOM events are wrapped in a `$scope.apply` so you dont have to. 175 | * All DOM events are cleaned up on `$destroy` to prevent memory leaks so you don't have to. 176 | 177 | ##Contributing 178 | 1. Fork it 179 | 2. Clone your fork 180 | 3. Create new branch 181 | 4. Make changes 182 | 5. Make a test and then check your test to verify it passes 183 | 6. Run ```gulp``` and the files will be linted, concatenated, and minified 184 | 7. Push to new branch on your forked repo 185 | 8. Pull request from your branch to ngComponent master 186 | 187 | ###Format for pull request 188 | + in your commit message; ```(type) message [issue # closed]``` 189 | + ```(bug) killed that bug, closes #45``` 190 | + Submit issues as you see them. Let's help everyone learn angular! ;) 191 | 192 | ###Testing 193 | + ngComponent uses Karma + Jasmine + Travis for unit and ci 194 | + Verify all the tests are passing 195 | + run ```gulp test``` to test in Chrome with karma 196 | + Features will not be accepted without specs created for them 197 | + Run ```gulp``` and all the source files will be watched and concatenated 198 | + Open the ```index.html``` and use the test app as a playground 199 | 200 | --------------------------------------------------------------------------------