"
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 [](https://travis-ci.org/maseh87/ngComponent) [](https://coveralls.io/r/maseh87/ngComponent?branch=master)
2 |
3 | ## An easier way to write angular directives
4 |
5 | 
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 |
--------------------------------------------------------------------------------