├── .gitignore
├── README.md
├── bower.json
├── demo
├── angular-list.html
└── js
│ └── angular-list.js
├── dist
└── angular-elements.js
├── gulpfile.js
├── package.json
└── src
└── angular-elements.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | bower_components
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AngularJS directives as native HTML elements (web components)
6 | =============================================================
7 | [](https://gitter.im/MVC-Elements/AngularElements?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8 |
9 | Tiny Google [Polymer](http://polymer-project.org) or Mozilla [X-Tags](http://www.x-tags.org/) add-on which allows to use [AngularJS](https://github.com/angular/angular.js) components as [custom HTML elements](http://w3c.github.io/webcomponents/spec/custom/). Also works with a native Custom Elements implementation if present.
10 |
11 | [Demo](http://pixelscommander.com/polygon/angular-elements/demo/)
12 |
13 | Example
14 | -------
15 |
16 | **Using component in HTML**
17 |
18 | ```html
19 |
20 |
21 |
22 | ```
23 |
24 | **Angular directive definition**
25 | ```js
26 | angular.module('demo', []).directive('angularList', function () {
27 | return {
28 | restrict: 'E',
29 | scope: {},
30 | template: '',
31 | link: function (scope) {
32 | scope.testMethod = function () {
33 | alert('Directive method called as node method');
34 | }
35 | }
36 | };
37 | });
38 |
39 | document.registerAngular('my-angular-component', 'demo');
40 | ```
41 |
42 | **Find complete examples in corresponding folder.**
43 |
44 | Nesting
45 | -------
46 |
47 | Original content of a custom element is injected to component as:
48 |
49 | ```html
50 | Hello world
51 | ```
52 |
53 | In this case we can use "Hello world" as transclude to "my-angular-component" directive.
54 |
55 |
56 | Dependencies
57 | ------------
58 |
59 | - [AngularJS](https://github.com/angular/angular.js)
60 | - [X-Tag core](https://github.com/x-tag/core) or [Polymer custom elements](https://github.com/Polymer/CustomElements) or native browser support for custom elements.
61 |
62 | License
63 | -------
64 |
65 | MIT: http://mit-license.org/
66 |
67 | Copyright 2015 Stepan Suvorov aka [stevermeister](http://github.com/stevermeister), Denis Radin aka [PixelsCommander](http://pixelscommander.com)
68 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-elements",
3 | "version": "0.0.0",
4 | "homepage": "https://github.com/MVC-Elements/angular-elements",
5 | "authors": [
6 | "PixelsCommander "
7 | ],
8 | "description": "Allows to use AngularJS component as HTML element",
9 | "keywords": [
10 | "angularjs",
11 | "angular",
12 | "web-components",
13 | "custom",
14 | "elements"
15 | ],
16 | "license": "MIT",
17 | "ignore": [
18 | "**/.*",
19 | "node_modules",
20 | "bower_components",
21 | "test",
22 | "tests"
23 | ],
24 | "devDependencies": {
25 | "angular": "~1.2.23"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/demo/angular-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Angular list as a web-component
5 |
6 |
7 |
8 |
9 |
10 |
27 |
28 |
36 |
37 |
--------------------------------------------------------------------------------
/demo/js/angular-list.js:
--------------------------------------------------------------------------------
1 | angular.module('demo', []).directive('angularList', function () {
2 | return {
3 | restrict: 'E',
4 | scope: {},
5 | template: '',
6 | link: function (scope) {
7 | scope.testMethod = function () {
8 | alert('Directive method called as node method');
9 | }
10 | }
11 | };
12 | });
13 |
14 | document.registerAngular('angular-list', 'demo');
--------------------------------------------------------------------------------
/dist/angular-elements.js:
--------------------------------------------------------------------------------
1 | !function(w){var PROPERTY_DELIMITER_CHARACTERS=[":","-","_"],registrationFunction=(document.registerElement||document.register).bind(document);if(void 0!==registrationFunction){var registerAngular=function(e,t){var r=Object.create(HTMLElement.prototype);r.createdCallback=function(){this._content=getContentNodes(this),this.angularModuleName=t,angular.bootstrap(this.parentNode,[this.angularModuleName]),this.scope=angular.element(this).isolateScope()||angular.element(this.parentNode).scope(),extend(this,this.scope);var e=getAllProperties(this,this.attributes);extend(this.scope,e),this.scope.$digest()},r.attributeChangedCallback=function(){var e=getAllProperties(this,this.attributes);extend(this.scope,e,!0),this.scope.$digest()},registrationFunction(e,{prototype:r})};document.registerAngular=registerAngular,void 0!==w.xtag&&(w.xtag.registerAngular=registerAngular);var extend=function(e,t,r){for(var n in t)(void 0===e[n]||r===!0)&&(e[n]="function"==typeof t[n]?t[n].bind(t):t[n])},getContentNodes=function(e){for(var t=document.createElement("content");e.childNodes.length;)t.appendChild(e.childNodes[0]);return t},getAllProperties=function(e,t){for(var r={},n=0;n0&&(value=eval(matches[0].replace("{","").replace("}",""))),value},getterSetter=function(e,t,r,n){Object.defineProperty?Object.defineProperty(e,t,{get:r,set:n}):document.__defineGetter__&&(e.__defineGetter__(t,r),e.__defineSetter__(t,n)),e["get"+t]=r,e["set"+t]=n}}}(window),Function.prototype.bind||(Function.prototype.bind=function(e){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var t=Array.prototype.slice.call(arguments,1),r=this,n=function(){},i=function(){return r.apply(this instanceof n&&e?this:e,t.concat(Array.prototype.slice.call(arguments)))};return n.prototype=this.prototype,i.prototype=new n,i});
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var uglify = require('gulp-uglify');
3 |
4 | gulp.task('default', function() {
5 | gulp.src('src/*.js')
6 | .pipe(uglify())
7 | .pipe(gulp.dest('dist'));
8 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-elements",
3 | "version": "0.0.0",
4 | "description": "Allows to use AngularJS component as HTML element",
5 | "main": "index.js",
6 | "scripts": {
7 | "postinstall": "./node_modules/.bin/bower install",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/MVC-Elements/angular-elements.git"
13 | },
14 | "keywords": [
15 | "mvc",
16 | "angularjs",
17 | "web-components"
18 | ],
19 | "author": "",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/MVC-Elements/angular-elements/issues"
23 | },
24 | "homepage": "https://github.com/MVC-Elements/angular-elements",
25 | "devDependencies": {
26 | "gulp": "~3.8.5",
27 | "gulp-uglify": "~0.3.1",
28 | "bower": "~1.3.11"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/angular-elements.js:
--------------------------------------------------------------------------------
1 | (function (w) {
2 |
3 | var PROPERTY_DELIMITER_CHARACTERS = [':', '-', '_'];
4 |
5 | var registrationFunction = (document.registerElement || document.register).bind(document);
6 |
7 | if (registrationFunction === undefined) {
8 | return;
9 | }
10 |
11 | var registerAngular = function (elementName, moduleName) {
12 | var elementPrototype = Object.create(HTMLElement.prototype);
13 | elementPrototype.createdCallback = function () {
14 | this._content = getContentNodes(this);
15 | this.angularModuleName = moduleName;
16 |
17 | angular.bootstrap(this, [this.angularModuleName]);
18 |
19 | this.scope = angular.element(this).isolateScope() || angular.element(this.parentNode).scope();
20 | extend(this, this.scope);
21 |
22 | var attributesObjects = getAllProperties(this, this.attributes);
23 | extend(this.scope, attributesObjects);
24 |
25 | this.scope.$digest();
26 | };
27 |
28 | elementPrototype.attributeChangedCallback = function () {
29 | var attributesObjects = getAllProperties(this, this.attributes);
30 | extend(this.scope, attributesObjects, true);
31 |
32 | this.scope.$digest();
33 | }
34 |
35 | registrationFunction(elementName, {
36 | prototype: elementPrototype
37 | });
38 | };
39 |
40 | document.registerAngular = registerAngular;
41 |
42 | if (w.xtag !== undefined) {
43 | w.xtag.registerAngular = registerAngular;
44 | }
45 |
46 | var extend = function (extandable, extending, replace) {
47 | for (var i in extending) {
48 | if (extandable[i] === undefined || replace === true) {
49 |
50 | if (typeof extending[i] === 'function') {
51 | extandable[i] = extending[i].bind(extending);
52 | } else {
53 | extandable[i] = extending[i];
54 | }
55 | }
56 | }
57 | };
58 |
59 | var getContentNodes = function (el) {
60 | var fragment = document.createElement('content');
61 | while (el.childNodes.length) {
62 | fragment.appendChild(el.childNodes[0]);
63 | }
64 | return fragment;
65 | };
66 |
67 | var getAllProperties = function (el, attributes) {
68 | var result = {};
69 |
70 | for (var i = 0; i < attributes.length; i++) {
71 | var attribute = attributes[i];
72 | var propertyName = attributeNameToPropertyName(attribute.name);
73 | result[propertyName] = parseAttributeValue(attributes[i].value);
74 | }
75 |
76 | result._content = el._content;
77 | return result;
78 | };
79 |
80 | var attributeNameToPropertyName = function (attributeName) {
81 | var result = attributeName.replace('x-', '').replace('data-', '');
82 | var delimiterIndex = -1;
83 |
84 | while ((delimiterIndex = getNextDelimiterIndex(result)) !== -1) {
85 | result = result.slice(0, delimiterIndex) + result.charAt(delimiterIndex + 1).toUpperCase() + result.slice(delimiterIndex + 2, result.length);
86 | }
87 |
88 | return result;
89 | };
90 |
91 | var getNextDelimiterIndex = function (string) {
92 | var result = -1;
93 |
94 | for (var i = 0; i < PROPERTY_DELIMITER_CHARACTERS.length; i++) {
95 | var char = PROPERTY_DELIMITER_CHARACTERS[i];
96 | result = string.indexOf(char);
97 | if (result !== -1) {
98 | break;
99 | }
100 | }
101 |
102 | return result;
103 | }
104 |
105 | var parseAttributeValue = function (value) {
106 | var regexp = /\{.*?\}/g;
107 | var matches = value.match(regexp);
108 |
109 | if (matches !== null && matches !== undefined && matches.length > 0) {
110 | value = eval(matches[0].replace('{', '').replace('}', ''));
111 | }
112 |
113 | return value;
114 | };
115 |
116 | var getterSetter = function (variableParent, variableName, getterFunction, setterFunction) {
117 | if (Object.defineProperty) {
118 | Object.defineProperty(variableParent, variableName, {
119 | get: getterFunction,
120 | set: setterFunction
121 | });
122 | }
123 | else if (document.__defineGetter__) {
124 | variableParent.__defineGetter__(variableName, getterFunction);
125 | variableParent.__defineSetter__(variableName, setterFunction);
126 | }
127 |
128 | variableParent["get" + variableName] = getterFunction;
129 | variableParent["set" + variableName] = setterFunction;
130 | };
131 | })(window);
132 |
133 | //Mozilla bind polyfill
134 | if (!Function.prototype.bind) {
135 | Function.prototype.bind = function (oThis) {
136 | if (typeof this !== "function") {
137 | // closest thing possible to the ECMAScript 5 internal IsCallable function
138 | throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
139 | }
140 |
141 | var aArgs = Array.prototype.slice.call(arguments, 1),
142 | fToBind = this,
143 | fNOP = function () {
144 | },
145 | fBound = function () {
146 | return fToBind.apply(this instanceof fNOP && oThis
147 | ? this
148 | : oThis,
149 | aArgs.concat(Array.prototype.slice.call(arguments)));
150 | };
151 |
152 | fNOP.prototype = this.prototype;
153 | fBound.prototype = new fNOP();
154 |
155 | return fBound;
156 | };
157 | }
158 |
--------------------------------------------------------------------------------