├── demo ├── .meteor │ ├── .gitignore │ ├── release │ ├── platforms │ ├── .id │ ├── .finished-upgraders │ ├── packages │ └── versions ├── client │ ├── components │ │ ├── demo-index │ │ │ ├── demo-index.html │ │ │ └── demo-index.js │ │ ├── side-menu │ │ │ ├── side-menu.css │ │ │ ├── side-menu.html │ │ │ └── side-menu.js │ │ ├── menu-heading │ │ │ ├── menu-heading.html │ │ │ ├── menu-heading.css │ │ │ └── menu-heading.js │ │ ├── menu-link │ │ │ ├── menu-link.html │ │ │ ├── menu-link.css │ │ │ └── menu-link.js │ │ ├── demo │ │ │ ├── demo.css │ │ │ ├── demo.html │ │ │ └── demo.js │ │ ├── demo-view-form │ │ │ ├── demo-view-form.html │ │ │ └── demo-view-form.js │ │ ├── side-menu-item │ │ │ ├── side-menu-item.css │ │ │ ├── side-menu-item.html │ │ │ └── side-menu-item.js │ │ ├── menu-toggle │ │ │ ├── menu-toggle.html │ │ │ ├── menu-toggle.css │ │ │ └── menu-toggle.js │ │ ├── demo-view-source │ │ │ ├── demo-view-source.html │ │ │ └── demo-view-source.js │ │ └── demo-view │ │ │ ├── demo-view.html │ │ │ └── demo-view.js │ ├── index.html │ └── data │ │ ├── examples │ │ ├── switch.js │ │ ├── checkbox.js │ │ ├── label.js │ │ ├── chips.js │ │ ├── textarea.js │ │ ├── slider.js │ │ ├── radio.js │ │ ├── select.js │ │ ├── divider.js │ │ └── input.js │ │ ├── examples.js │ │ ├── menu.js │ │ └── codepen.js └── .eslintrc ├── tests ├── runs │ ├── index.js │ └── class-name-spec.js ├── helpers │ ├── index.js │ └── ng-model-attrs-manipulator-spec.js ├── index-spec.js ├── wrappers │ ├── index.js │ ├── input-container-spec.js │ ├── messages-spec.js │ ├── divider-spec.js │ └── label-spec.js ├── test-utils.js └── types │ ├── index.js │ ├── checkbox-spec.js │ ├── switch-spec.js │ ├── slider-spec.js │ ├── datepicker-spec.js │ ├── textarea-spec.js │ ├── chips-spec.js │ ├── radio-spec.js │ ├── input-spec.js │ └── select-spec.js ├── src ├── types │ ├── input │ │ ├── input.html │ │ └── input.js │ ├── textarea │ │ ├── textarea.html │ │ └── textarea.js │ ├── slider │ │ ├── slider.html │ │ └── slider.js │ ├── chips │ │ ├── chips.html │ │ └── chips.js │ ├── switch │ │ ├── switch.html │ │ └── switch.js │ ├── datepicker │ │ ├── datepicker.html │ │ └── datepicker.js │ ├── checkbox │ │ ├── checkbox.html │ │ └── checkbox.js │ ├── select │ │ ├── select.html │ │ └── select.js │ ├── radio │ │ ├── radio.html │ │ └── radio.js │ └── index.js ├── runs │ ├── index.js │ └── class-name.js ├── wrappers │ ├── input-container │ │ ├── input-container.html │ │ └── input-container.js │ ├── divider │ │ ├── divider.html │ │ └── divider.js │ ├── messages │ │ ├── messages.js │ │ └── messages.html │ ├── label │ │ ├── label.html │ │ └── label.js │ └── index.js ├── index.js └── helpers │ └── index.js ├── docs ├── wrappers │ ├── messages.md │ ├── label.md │ ├── input-container.md │ └── divider.md └── types │ ├── switch.md │ ├── checkbox.md │ ├── textarea.md │ ├── input.md │ ├── slider.md │ ├── radio.md │ ├── datepicker.md │ ├── select.md │ └── chips.md ├── .eslintignore ├── webpack ├── dist.js ├── index.js ├── prod.js ├── test.js └── common.js ├── webpack.config.js ├── .npmignore ├── .editorconfig ├── .eslintrc ├── bower.json ├── package.js ├── LICENSE ├── .travis.yml ├── .gitignore ├── karma.conf.js ├── package.json ├── README.md ├── CHANGELOG.md └── dist ├── formly-material.min.js └── formly-material.js /demo/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /demo/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.2.1 2 | -------------------------------------------------------------------------------- /demo/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /tests/runs/index.js: -------------------------------------------------------------------------------- 1 | import './class-name-spec'; 2 | -------------------------------------------------------------------------------- /src/types/input/input.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/client/components/demo-index/demo-index.html: -------------------------------------------------------------------------------- 1 | Getting started! 2 | -------------------------------------------------------------------------------- /tests/helpers/index.js: -------------------------------------------------------------------------------- 1 | import './ng-model-attrs-manipulator-spec'; 2 | -------------------------------------------------------------------------------- /src/types/textarea/textarea.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/wrappers/messages.md: -------------------------------------------------------------------------------- 1 | messages 2 | ======== 3 | 4 | ng-messages 5 | ----------- 6 | -------------------------------------------------------------------------------- /src/runs/index.js: -------------------------------------------------------------------------------- 1 | import className from './class-name'; 2 | 3 | export default [className]; 4 | -------------------------------------------------------------------------------- /demo/client/components/side-menu/side-menu.css: -------------------------------------------------------------------------------- 1 | side-menu ul { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | -------------------------------------------------------------------------------- /demo/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./../.eslintrc", 3 | "globals": { 4 | "angular": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/wrappers/label.md: -------------------------------------------------------------------------------- 1 | label 2 | ===== 3 | 4 | label 5 | ----- 6 | 7 | ### templateOptions.label *{String}* 8 | -------------------------------------------------------------------------------- /src/types/slider/slider.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | docs 3 | node_modules 4 | webpack 5 | package.js 6 | karma.conf.js 7 | webpack.config.js 8 | -------------------------------------------------------------------------------- /docs/wrappers/input-container.md: -------------------------------------------------------------------------------- 1 | input-container 2 | =============== 3 | 4 | md-input-container 5 | ------------------ 6 | -------------------------------------------------------------------------------- /webpack/dist.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./common')({ 2 | output: { 3 | filename: '[name].js' 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /demo/client/components/menu-heading/menu-heading.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/types/chips/chips.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/types/switch/switch.html: -------------------------------------------------------------------------------- 1 | 2 | {{to.label}} 3 | 4 | -------------------------------------------------------------------------------- /tests/index-spec.js: -------------------------------------------------------------------------------- 1 | import './../src'; 2 | 3 | import './helpers'; 4 | import './runs'; 5 | import './types'; 6 | import './wrappers'; 7 | -------------------------------------------------------------------------------- /tests/wrappers/index.js: -------------------------------------------------------------------------------- 1 | import './input-container-spec'; 2 | import './label-spec'; 3 | import './messages-spec'; 4 | import './divider-spec'; 5 | -------------------------------------------------------------------------------- /src/types/datepicker/datepicker.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/wrappers/input-container/input-container.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/types/checkbox/checkbox.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | {{to.label}} 4 | 5 |
6 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | require('argv-set-env')(); 2 | var webpack = require('./webpack')(process.env.NODE_ENV === 'production' ? 'prod' : 'dist'); 3 | 4 | module.exports = webpack; 5 | -------------------------------------------------------------------------------- /demo/client/components/menu-link/menu-link.html: -------------------------------------------------------------------------------- 1 | 2 | {{::vm.sectionName}} 3 | 4 | -------------------------------------------------------------------------------- /demo/client/components/side-menu/side-menu.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/wrappers/divider/divider.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | coverage 3 | demo 4 | node_modules 5 | bower_components 6 | src 7 | tests 8 | webpack 9 | .editorconfig 10 | .eslintrc 11 | .travis.yml 12 | karma.conf.js 13 | package.js 14 | webpack.config.js 15 | -------------------------------------------------------------------------------- /demo/client/components/demo/demo.css: -------------------------------------------------------------------------------- 1 | #demo-sidenav { 2 | background: #35176A; 3 | } 4 | 5 | #demo-sidenav-content { 6 | background: transparent; 7 | } 8 | 9 | .purple-headline { 10 | color: #673AB7; 11 | } 12 | -------------------------------------------------------------------------------- /tests/test-utils.js: -------------------------------------------------------------------------------- 1 | export default class testUtils { 2 | static getFormTemplate() { 3 | return `
`; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/wrappers/messages/messages.js: -------------------------------------------------------------------------------- 1 | import template from './messages.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setWrapper({ 5 | template, 6 | name: 'messages' 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /src/wrappers/input-container/input-container.js: -------------------------------------------------------------------------------- 1 | import template from './input-container.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setWrapper({ 5 | template, 6 | name: 'inputContainer' 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /webpack/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(type) { 2 | const types = ['dist', 'prod', 'test']; 3 | 4 | if (types.indexOf(type) === -1) { 5 | throw new Error('Unknown webpack configuration'); 6 | } 7 | 8 | return require('./' + type); 9 | }; 10 | -------------------------------------------------------------------------------- /src/types/select/select.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ option[to.labelProp || 'name'] }} 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/wrappers/label/label.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /webpack/prod.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = require('./common')({ 4 | output: { 5 | filename: '[name].min.js' 6 | }, 7 | devtool: 'source-map', 8 | plugins: [ 9 | new webpack.optimize.UglifyJsPlugin() 10 | ] 11 | }); 12 | -------------------------------------------------------------------------------- /demo/client/components/menu-heading/menu-heading.css: -------------------------------------------------------------------------------- 1 | menu-heading .menu-heading { 2 | display: block; 3 | line-height: 32px; 4 | margin: 0; 5 | padding: 8px 16px; 6 | text-align: left; 7 | color: rgba(255, 255, 255, 0.6); 8 | text-transform: uppercase; 9 | } 10 | -------------------------------------------------------------------------------- /tests/types/index.js: -------------------------------------------------------------------------------- 1 | import './checkbox-spec'; 2 | import './chips-spec'; 3 | import './datepicker-spec'; 4 | import './input-spec'; 5 | import './radio-spec'; 6 | import './select-spec'; 7 | import './slider-spec'; 8 | import './switch-spec'; 9 | import './textarea-spec'; 10 | -------------------------------------------------------------------------------- /demo/client/components/demo-view-form/demo-view-form.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | Submit 4 | 5 |
6 | -------------------------------------------------------------------------------- /demo/client/components/side-menu-item/side-menu-item.css: -------------------------------------------------------------------------------- 1 | side-menu ul.side-menu > li.opened { 2 | background: #432677; 3 | } 4 | 5 | side-menu ul.side-menu > li { 6 | border-bottom: 1px solid #673AB7; 7 | } 8 | 9 | side-menu ul.side-menu > li:last-child { 10 | border-bottom: 0 none; 11 | } 12 | -------------------------------------------------------------------------------- /src/wrappers/messages/messages.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
5 | {{message(fc.$viewValue, fc.$modelValue, this)}} 6 |
7 |
8 | -------------------------------------------------------------------------------- /demo/client/components/menu-toggle/menu-toggle.html: -------------------------------------------------------------------------------- 1 | 2 | {{::vm.sectionName}} 3 | 4 | 9 | -------------------------------------------------------------------------------- /src/wrappers/label/label.js: -------------------------------------------------------------------------------- 1 | import template from './label.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setWrapper({ 5 | template, 6 | name: 'label', 7 | apiCheck: (check) => ({ 8 | templateOptions: { 9 | label: check.string 10 | } 11 | }) 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /demo/client/components/side-menu/side-menu.js: -------------------------------------------------------------------------------- 1 | const { Component, View } = angular2now; 2 | 3 | @Component({ 4 | selector: 'side-menu', 5 | bind: { 6 | menu: '=' 7 | } 8 | }) 9 | @View({ 10 | templateUrl: 'client/components/side-menu/side-menu.html' 11 | }) 12 | class SideMenuComponent { 13 | constructor() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | FormlyMaterial demo 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/wrappers/divider.md: -------------------------------------------------------------------------------- 1 | divider 2 | ======= 3 | 4 | md-divider 5 | ---------- 6 | 7 | ### templateOptions.divider *{String}* 8 | 9 | Expects one of: 10 | 11 | - **after** (default) - moves divider after the field or other wrappers 12 | - **before** - moves divider before the field or other wrappers 13 | 14 | Make sure you use lower cases 15 | -------------------------------------------------------------------------------- /demo/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | fy74xsbb3je312muj19 8 | -------------------------------------------------------------------------------- /demo/client/components/menu-heading/menu-heading.js: -------------------------------------------------------------------------------- 1 | const { Component, View } = angular2now; 2 | 3 | @Component({ 4 | selector: 'menu-heading', 5 | bind: { 6 | name: '=' 7 | } 8 | }) 9 | @View({ 10 | templateUrl: 'client/components/menu-heading/menu-heading.html' 11 | }) 12 | class MenuHeadingComponent { 13 | constructor() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/types/radio/radio.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | {{option[to.labelProp || 'name']}} 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/wrappers/divider/divider.js: -------------------------------------------------------------------------------- 1 | import template from './divider.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setWrapper({ 5 | template, 6 | name: 'divider', 7 | apiCheck: (check) => ({ 8 | templateOptions: { 9 | divider: check.oneOf(['before', 'after']).optional 10 | } 11 | }) 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /src/wrappers/index.js: -------------------------------------------------------------------------------- 1 | import inputContainerWrapper from './input-container/input-container'; 2 | import labelWrapper from './label/label'; 3 | import messagesWrapper from './messages/messages'; 4 | import dividerWrapper from './divider/divider'; 5 | 6 | export default [ 7 | inputContainerWrapper, 8 | labelWrapper, 9 | messagesWrapper, 10 | dividerWrapper 11 | ]; 12 | -------------------------------------------------------------------------------- /demo/client/components/demo-view-source/demo-view-source.html: -------------------------------------------------------------------------------- 1 |

Scope

2 | 3 | 4 | 5 |
6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /demo/client/components/demo-index/demo-index.js: -------------------------------------------------------------------------------- 1 | const { Component, View, State } = angular2now; 2 | 3 | @Component({ 4 | selector: 'demo-index' 5 | }) 6 | @View({ 7 | templateUrl: 'client/components/demo-index/demo-index.html' 8 | }) 9 | @State({ 10 | name: 'demo.index', 11 | url: '/', 12 | defaultRoute: '/demo/' 13 | }) 14 | class DemoIndexComponent { 15 | constructor() { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /webpack/test.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./common')({ 2 | entry: './tests/index-spec.js', 3 | output: undefined, 4 | module: { 5 | loaders: [ 6 | // transpile and instrument only testing sources with isparta 7 | { 8 | test: /\.js$/, 9 | include: /src/, 10 | exclude: /node_modules/, 11 | loader: 'isparta' 12 | } 13 | ] 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /demo/client/components/menu-link/menu-link.css: -------------------------------------------------------------------------------- 1 | menu-link .menu-link { 2 | border-radius: 0; 3 | color: white; 4 | cursor: pointer; 5 | display: block; 6 | align-items: inherit; 7 | line-height: 40px; 8 | margin: 0; 9 | max-height: 40px; 10 | overflow: hidden; 11 | padding: 0px 16px; 12 | text-align: left; 13 | text-decoration: none; 14 | white-space: normal; 15 | width: 100%; 16 | } 17 | -------------------------------------------------------------------------------- /demo/client/components/demo-view-form/demo-view-form.js: -------------------------------------------------------------------------------- 1 | const { Component, View } = angular2now; 2 | 3 | @Component({ 4 | selector: 'demo-view-form', 5 | bind: { 6 | fields: '=', 7 | model: '=?', 8 | options: '=?', 9 | form: '=?' 10 | } 11 | }) 12 | @View({ 13 | templateUrl: 'client/components/demo-view-form/demo-view-form.html' 14 | }) 15 | class DemoViewFormComponent { 16 | constructor() { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is Awesome: http://editorconfig.org 2 | 3 | # Top-most EditorConfig file. 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file. 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | indent_style = space 12 | indent_size = 2 13 | trim_trailing_whitespace = true 14 | 15 | # Don't trim whitespace in Markdown in order to be able 16 | # to do two spaces for line breaks. 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /demo/client/components/menu-toggle/menu-toggle.css: -------------------------------------------------------------------------------- 1 | menu-toggle .menu-toggle { 2 | border-radius: 0; 3 | color: white; 4 | cursor: pointer; 5 | display: block; 6 | align-items: inherit; 7 | line-height: 40px; 8 | margin: 0; 9 | max-height: 40px; 10 | overflow: hidden; 11 | padding: 0px 16px; 12 | text-align: left; 13 | text-decoration: none; 14 | white-space: normal; 15 | width: 100%; 16 | } 17 | 18 | menu-toggle menu-link .menu-link { 19 | padding: 0px 16px 0 32px; 20 | } 21 | -------------------------------------------------------------------------------- /docs/types/switch.md: -------------------------------------------------------------------------------- 1 | # switch 2 | ## md-switch 3 | 4 | ### Example 5 | 6 | ```javascript 7 | { 8 | type: "switch", 9 | key: "terms", 10 | templateOptions: { 11 | label: "Terms and Conditions", 12 | theme: "custom" 13 | } 14 | } 15 | ``` 16 | 17 | ### Configuration 18 | 19 | #### templateOptions.label _: string_ 20 | 21 | #### templateOptions.theme _: string_ 22 | 23 | #### templateOptions.disabled _: boolean_ 24 | 25 | #### templateOptions.className _: string | expression | array_ 26 | -------------------------------------------------------------------------------- /src/runs/class-name.js: -------------------------------------------------------------------------------- 1 | import { ngModelAttrsTransformer } from './../helpers'; 2 | 3 | export default (formlyConfigProvider) => { 4 | // add only step attribute because min and max are both built-in 5 | formlyConfigProvider.extras.fieldTransform.push((fields) => { 6 | return ngModelAttrsTransformer(fields, (field) => ( 7 | field.templateOptions && typeof field.templateOptions.className !== 'undefined' 8 | ), 'className', { 9 | bound: 'ng-class' 10 | }); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /src/types/radio/radio.js: -------------------------------------------------------------------------------- 1 | import template from './radio.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setType({ 5 | template, 6 | name: 'radio', 7 | wrapper: ['label'], 8 | apiCheck: (check) => ({ 9 | templateOptions: { 10 | options: check.arrayOf(check.object), 11 | labelProp: check.string.optional, 12 | valueProp: check.string.optional, 13 | theme: check.string.optional 14 | } 15 | }) 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /docs/types/checkbox.md: -------------------------------------------------------------------------------- 1 | # checkbox 2 | ## md-checkbox 3 | 4 | ### Example 5 | 6 | ```javascript 7 | { 8 | type: "checkbox", 9 | key: "terms", 10 | templateOptions: { 11 | label: "Terms and Conditions", 12 | theme: "custom" 13 | } 14 | } 15 | ``` 16 | 17 | ### Configuration 18 | 19 | #### templateOptions.label _: string_ 20 | 21 | #### templateOptions.theme _: string_ 22 | 23 | #### templateOptions.disabled _: boolean_ 24 | 25 | #### templateOptions.className _: string | expression | array_ 26 | -------------------------------------------------------------------------------- /demo/client/data/examples/switch.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | type: 'switch', 8 | key: 'terms', 9 | templateOptions: { 10 | label: 'Terms and conditions' 11 | } 12 | }); 13 | 14 | // set example 15 | Examples.set('switch', { 16 | fields 17 | }, 'types/switch.md'); 18 | 19 | // add menu item to types 20 | Menu.addChild('types', 'switch'); 21 | }]); 22 | -------------------------------------------------------------------------------- /demo/client/data/examples/checkbox.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | key: 'terms', 8 | type: 'checkbox', 9 | templateOptions: { 10 | label: 'Terms and Conditions' 11 | } 12 | }); 13 | 14 | // set example 15 | Examples.set('checkbox', { 16 | fields 17 | }, 'types/checkbox.md'); 18 | 19 | // add menu item to types 20 | Menu.addChild('types', 'checkbox'); 21 | }]); 22 | -------------------------------------------------------------------------------- /src/types/checkbox/checkbox.js: -------------------------------------------------------------------------------- 1 | import template from './checkbox.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setType({ 5 | template, 6 | name: 'checkbox', 7 | defaultOptions: { 8 | ngModelAttrs: { 9 | disabled: { 10 | bound: 'ng-disabled' 11 | } 12 | } 13 | }, 14 | apiCheck: (check) => ({ 15 | templateOptions: { 16 | disabled: check.bool.optional, 17 | theme: check.string.optional 18 | } 19 | }) 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /demo/client/data/examples/label.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | key: 'terms', 8 | type: 'switch', 9 | wrapper: ['label'], 10 | templateOptions: { 11 | label: 'Terms and conditions' 12 | } 13 | }); 14 | 15 | // set example 16 | Examples.set('label', { 17 | fields 18 | }, 'wrappers/label.md'); 19 | 20 | // add menu item to wrappers 21 | Menu.addChild('wrappers', 'label'); 22 | }]); 23 | -------------------------------------------------------------------------------- /demo/client/components/demo-view/demo-view.html: -------------------------------------------------------------------------------- 1 |

2 | Example 3 | Go to API 4 |

5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /src/types/switch/switch.js: -------------------------------------------------------------------------------- 1 | import template from './switch.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setType({ 5 | template, 6 | name: 'switch', 7 | defaultOptions: { 8 | templateOptions: { 9 | disabled: false 10 | }, 11 | ngModelAttrs: { 12 | disabled: { 13 | bound: 'ng-disabled' 14 | } 15 | } 16 | }, 17 | apiCheck: (check) => ({ 18 | templateOptions: { 19 | disabled: check.bool.optional, 20 | theme: check.string.optional 21 | } 22 | }) 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /demo/client/data/examples/chips.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | type: 'chips', 8 | key: 'tags', 9 | templateOptions: { 10 | placeholder: '+tags', 11 | secondaryPlaceholder: 'Add tag', 12 | deleteButtonLabel: 'Remove', 13 | deleteHint: 'Remove tag' 14 | } 15 | }); 16 | 17 | // set example 18 | Examples.set('chips', { 19 | fields 20 | }, 'types/chips.md'); 21 | 22 | // add menu item to types 23 | Menu.addChild('types', 'chips'); 24 | }]); 25 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | 3 | import runs from './runs'; 4 | import wrappers from './wrappers'; 5 | import types from './types'; 6 | 7 | const ngModuleName = 'formlyMaterial'; 8 | 9 | angular.module(ngModuleName, ['ngMessages', 'ngMaterial', 'formly']) 10 | .config(['formlyConfigProvider', (formlyConfigProvider) => { 11 | const configs = [runs, wrappers, types]; 12 | 13 | configs.forEach((config) => { 14 | let i = 0; 15 | for (; i < config.length; i++) { 16 | config[i](formlyConfigProvider); 17 | } 18 | }); 19 | }]); 20 | 21 | export default ngModuleName; 22 | -------------------------------------------------------------------------------- /src/types/index.js: -------------------------------------------------------------------------------- 1 | import checkboxType from './checkbox/checkbox'; 2 | import chipsType from './chips/chips'; 3 | import datepickerType from './datepicker/datepicker'; 4 | import inputType from './input/input'; 5 | import radioType from './radio/radio'; 6 | import selectType from './select/select'; 7 | import sliderType from './slider/slider'; 8 | import switchType from './switch/switch'; 9 | import textareaType from './textarea/textarea'; 10 | 11 | export default [ 12 | checkboxType, 13 | chipsType, 14 | datepickerType, 15 | inputType, 16 | radioType, 17 | selectType, 18 | sliderType, 19 | switchType, 20 | textareaType 21 | ]; 22 | -------------------------------------------------------------------------------- /demo/client/components/menu-link/menu-link.js: -------------------------------------------------------------------------------- 1 | const { Component, View, Inject } = angular2now; 2 | 3 | @Component({ 4 | selector: 'menu-link', 5 | bind: { 6 | sectionId: '=', 7 | sectionName: '=', 8 | } 9 | }) 10 | @View({ 11 | templateUrl: 'client/components/menu-link/menu-link.html' 12 | }) 13 | @Inject(['$mdSidenav']) 14 | class MenuLinkComponent { 15 | constructor($mdSidenav) { 16 | this.$mdSidenav = $mdSidenav; 17 | 18 | this.componentId = 'left'; 19 | } 20 | 21 | close() { 22 | if (!this.$mdSidenav(this.componentId).isLockedOpen()) { 23 | this.$mdSidenav(this.componentId).close(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/client/components/side-menu-item/side-menu-item.html: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 22 | 23 |
  • 24 | -------------------------------------------------------------------------------- /demo/client/data/examples/textarea.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | key: 'text-grow', 8 | type: 'textarea', 9 | templateOptions: { 10 | label: 'Growing textarea' 11 | } 12 | }); 13 | 14 | fields.push({ 15 | key: 'text-notgrow', 16 | type: 'textarea', 17 | templateOptions: { 18 | label: 'Not growing textarea', 19 | grow: false 20 | } 21 | }); 22 | 23 | // set example 24 | Examples.set('textarea', { 25 | fields 26 | }, 'types/textarea.md'); 27 | 28 | // add menu item to types 29 | Menu.addChild('types', 'textarea'); 30 | }]); 31 | -------------------------------------------------------------------------------- /demo/client/components/side-menu-item/side-menu-item.js: -------------------------------------------------------------------------------- 1 | const { Component, View, Inject } = angular2now; 2 | 3 | @Component({ 4 | selector: 'side-menu-item', 5 | replace: true, 6 | bind: { 7 | section: '=' 8 | } 9 | }) 10 | @View({ 11 | templateUrl: 'client/components/side-menu-item/side-menu-item.html' 12 | }) 13 | @Inject(['$animate', '$element']) 14 | class SideMenuItemComponent { 15 | constructor($animate, $element) { 16 | this.$animate = $animate; 17 | this.$element = $element; 18 | 19 | this.opened = false; 20 | } 21 | 22 | onOpen() { 23 | this.$animate.addClass(this.$element, 'opened'); 24 | this.opened = true; 25 | } 26 | 27 | onClose() { 28 | this.$animate.removeClass(this.$element, 'opened'); 29 | this.opened = false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/types/textarea.md: -------------------------------------------------------------------------------- 1 | textarea 2 | ======== 3 | 4 | textarea 5 | -------- 6 | 7 | ### Example 8 | 9 | ```javascript 10 | { 11 | type: "textarea", 12 | key: "bio", 13 | templateOptions: { 14 | label: "Biography", 15 | theme: "custom", 16 | rows: 5, 17 | grow: false 18 | } 19 | } 20 | ``` 21 | 22 | #### Configuration 23 | 24 | #### templateOptions.label *: string* 25 | 26 | #### templateOptions.theme *: string* 27 | 28 | #### templateOptions.disabled _: boolean_ 29 | 30 | #### templateOptions.className _: string | expression | array_ 31 | 32 | #### templateOptions.rows *: integer* 33 | 34 | Number of rows 35 | 36 | #### templateOptions.grow *: boolean (default true)* 37 | 38 | Equivalent to md-no-autogrow 39 | 40 | When present, textareas will not grow automatically. 41 | -------------------------------------------------------------------------------- /demo/client/data/examples/slider.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | type: 'slider', 8 | key: 'rate-discrete', 9 | templateOptions: { 10 | label: 'Rate (with discrete)', 11 | min: 1, 12 | max: 5, 13 | step: 0.5, 14 | discrete: true 15 | } 16 | }); 17 | 18 | fields.push({ 19 | type: 'slider', 20 | key: 'rate', 21 | templateOptions: { 22 | label: 'Rate', 23 | min: 1, 24 | max: 5, 25 | step: 0.5 26 | } 27 | }); 28 | 29 | // set example 30 | Examples.set('slider', { 31 | fields 32 | }, 'types/slider.md'); 33 | 34 | // add menu item to types 35 | Menu.addChild('types', 'slider'); 36 | }]); 37 | -------------------------------------------------------------------------------- /demo/client/components/demo-view/demo-view.js: -------------------------------------------------------------------------------- 1 | const { Component, View, State, Inject } = angular2now; 2 | 3 | @Component({ 4 | selector: 'demo-view' 5 | }) 6 | @View({ 7 | templateUrl: 'client/components/demo-view/demo-view.html' 8 | }) 9 | @State({ 10 | name: 'demo.view', 11 | url: '/{id:[a-z\-]+}' 12 | }) 13 | @Inject(['$stateParams', 'Examples']) 14 | class DemoViewComponent { 15 | constructor($stateParams, Examples) { 16 | this.id = $stateParams.id; 17 | this.Examples = Examples; 18 | 19 | const example = Examples.get(this.id); 20 | 21 | if (example) { 22 | this.fields = angular.copy(example.formly.fields); 23 | this.model = angular.copy(example.formly.model) || {}; 24 | this.options = angular.copy(example.formly.options) || {}; 25 | this.api = example.api; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/client/data/examples/radio.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | type: 'radio', 8 | key: 'name', 9 | templateOptions: { 10 | label: 'Name', 11 | labelProp: 'firstName', 12 | valueProp: 'id', 13 | options: [ 14 | { 15 | firstName: 'Sarah', 16 | id: 1 17 | }, 18 | { 19 | firstName: 'Jessica', 20 | id: 2 21 | }, 22 | { 23 | firstName: 'Parker', 24 | id: 3 25 | } 26 | ] 27 | } 28 | }); 29 | 30 | // set example 31 | Examples.set('radio', { 32 | fields 33 | }, 'types/radio.md'); 34 | 35 | // add menu item to types 36 | Menu.addChild('types', 'radio'); 37 | }]); 38 | -------------------------------------------------------------------------------- /demo/client/components/menu-toggle/menu-toggle.js: -------------------------------------------------------------------------------- 1 | const { Component, View } = angular2now; 2 | 3 | @Component({ 4 | selector: 'menu-toggle', 5 | bind: { 6 | sectionName: '=', 7 | sectionState: '=', 8 | sectionChildren: '=', 9 | onOpen: '&?', 10 | onClose: '&?' 11 | } 12 | }) 13 | @View({ 14 | templateUrl: 'client/components/menu-toggle/menu-toggle.html' 15 | }) 16 | class MenuToggleComponent { 17 | constructor() { 18 | this.opened = false; 19 | } 20 | 21 | open() { 22 | this.opened = true; 23 | this.onOpen(); 24 | } 25 | 26 | close() { 27 | this.opened = false; 28 | this.onClose(); 29 | } 30 | 31 | toggle() { 32 | if (this.opened === true) { 33 | this.close(); 34 | } else { 35 | this.open(); 36 | } 37 | } 38 | 39 | isOpen() { 40 | return this.opened === true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /demo/client/components/demo-view-source/demo-view-source.js: -------------------------------------------------------------------------------- 1 | const { Component, View } = angular2now; 2 | 3 | @Component({ 4 | selector: 'demo-view-source', 5 | bind: { 6 | fields: '=', 7 | model: '=?', 8 | options: '=?', 9 | form: '=?' 10 | } 11 | }) 12 | @View({ 13 | templateUrl: 'client/components/demo-view-source/demo-view-source.html' 14 | }) 15 | class DemoViewSourceComponent { 16 | constructor() { 17 | this.sources = []; 18 | // make copy of fields 19 | this.fieldsInits = angular.copy(this.fields); 20 | 21 | this.add('fieldsInits', 'Initial fields'); 22 | this.add('fields', 'Fields'); 23 | this.add('model', 'Model'); 24 | this.add('form', 'Form'); 25 | this.add('options', 'Options'); 26 | } 27 | 28 | add(key, label) { 29 | this.sources.push({ 30 | label, 31 | key 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demo/client/data/examples/select.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | type: 'select', 8 | key: 'names', 9 | templateOptions: { 10 | label: 'Names', 11 | multiple: true, 12 | labelProp: 'firstName', 13 | valueProp: 'id', 14 | options: [ 15 | { 16 | firstName: 'Sarah', 17 | id: 1 18 | }, 19 | { 20 | firstName: 'Jessica', 21 | id: 2 22 | }, 23 | { 24 | firstName: 'Parker', 25 | id: 3 26 | } 27 | ] 28 | } 29 | }); 30 | 31 | // set example 32 | Examples.set('select', { 33 | fields 34 | }, 'types/select.md'); 35 | 36 | // add menu item to types 37 | Menu.addChild('types', 'select'); 38 | }]); 39 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "./node_modules/eslint-config-airbnb/rules/best-practices.js", 4 | "./node_modules/eslint-config-airbnb/rules/errors.js", 5 | "./node_modules/eslint-config-airbnb/rules/legacy.js", 6 | "./node_modules/eslint-config-airbnb/rules/node.js", 7 | "./node_modules/eslint-config-airbnb/rules/strict.js", 8 | "./node_modules/eslint-config-airbnb/rules/style.js", 9 | "./node_modules/eslint-config-airbnb/rules/variables.js", 10 | "./node_modules/eslint-config-airbnb/rules/es6.js" 11 | ], 12 | "parser": "babel-eslint", 13 | "env": { 14 | "browser": true, 15 | "node": true, 16 | "meteor": true, 17 | "mongo": true, 18 | "jasmine": true, 19 | "es6": true 20 | }, 21 | "globals": { 22 | "inject": false, 23 | "angular2now": false 24 | }, 25 | "rules": { 26 | "new-cap": 1, 27 | "no-unused-vars": 1, 28 | "comma-dangle": 0 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demo/client/data/examples/divider.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | key: 'terms1', 8 | type: 'switch', 9 | wrapper: ['divider'], 10 | templateOptions: { 11 | label: 'Terms #1' 12 | } 13 | }); 14 | 15 | fields.push({ 16 | key: 'terms2', 17 | type: 'switch', 18 | wrapper: ['divider'], 19 | templateOptions: { 20 | label: 'Terms #2' 21 | } 22 | }); 23 | 24 | fields.push({ 25 | key: 'terms3', 26 | type: 'switch', 27 | templateOptions: { 28 | label: 'Terms #3' 29 | } 30 | }); 31 | 32 | // set example 33 | Examples.set('divider', { 34 | fields, 35 | model: { 36 | terms2: true 37 | }, 38 | }, 'wrappers/divider.md'); 39 | 40 | // add menu item to wrappers 41 | Menu.addChild('wrappers', 'divider'); 42 | }]); 43 | -------------------------------------------------------------------------------- /docs/types/input.md: -------------------------------------------------------------------------------- 1 | input 2 | ===== 3 | 4 | md-input 5 | -------- 6 | 7 | Example 8 | ------- 9 | 10 | ```javascript 11 | { 12 | type: "input", 13 | key: "firstName", 14 | templateOptions: { 15 | type: "text", 16 | label: "First name", 17 | pattern: "[a-zA-Z]+" 18 | theme: "custom" 19 | } 20 | } 21 | ``` 22 | 23 | ### Configuration 24 | 25 | #### templateOptions.type *: string* 26 | 27 | #### templateOptions.label *: string* 28 | 29 | #### templateOptions.theme *: string* 30 | 31 | Value of md-theme directive 32 | 33 | #### templateOptions.className _: string | expression | array_ 34 | 35 | #### templateOptions.disabled _: boolean_ 36 | 37 | #### templateOptions.step *: number* 38 | 39 | only if templateOptions.type is 'number' 40 | 41 | #### templateOptions.min *: number* 42 | 43 | #### templateOptions.max *: number* 44 | 45 | #### templateOptions.pattern *: string | RegExp* 46 | 47 | Value of ng-pattern directive 48 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-formly-material", 3 | "description": "Material design templates for angular-formly", 4 | "main": "dist/formly-material.js", 5 | "authors": [ 6 | "Kamil Kisiela " 7 | ], 8 | "license": "MIT", 9 | "homepage": "https://github.com/formly-js/angular-formly-templates-material", 10 | "keywords": [ 11 | "AngularJs", 12 | "form", 13 | "formly", 14 | "json", 15 | "html", 16 | "material" 17 | ], 18 | "ignore": [ 19 | "**/.*", 20 | "coverage", 21 | "demo", 22 | "node_modules", 23 | "src", 24 | "tests", 25 | "webpack", 26 | "karma.conf.js", 27 | "package.js", 28 | "webpack.config.js" 29 | ], 30 | "dependencies": { 31 | "angular": "~1.4.0", 32 | "angular-messages": "~1.4.0", 33 | "angular-animate": "~1.4.0", 34 | "angular-aria": "~1.4.0", 35 | "angular-material": "~1.0.0", 36 | "angular-formly": "~7.3.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/types/slider.md: -------------------------------------------------------------------------------- 1 | # slider 2 | ## md-slider 3 | 4 | ### Example 5 | 6 | ```javascript 7 | { 8 | type: "slider", 9 | key: "rate", 10 | templateOptions: { 11 | theme: "custom", 12 | min: 1, 13 | max: 5, 14 | step: 0.5, 15 | discrete: true 16 | } 17 | } 18 | ``` 19 | 20 | ### Configuration 21 | 22 | #### templateOptions.label _: string_ 23 | 24 | #### templateOptions.theme _: string_ 25 | 26 | #### templateOptions.disabled _: boolean_ 27 | 28 | #### templateOptions.className _: string | expression | array_ 29 | 30 | #### templateOptions.min _: number (default: 0)_ 31 | The minimum value the user is allowed to pick. 32 | 33 | #### templateOptions.max _: number (default: 100)_ 34 | The maximum value the user is allowed to pick. 35 | 36 | #### templateOptions.step _: number (default: 1)_ 37 | The distance between values the user is allowed to pick. 38 | 39 | #### templateOptions.discrete _: boolean (default: false)_ 40 | Whether to enable discrete mode 41 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | // package metadata file for AtmosphereJS 2 | 3 | Package.describe({ 4 | name: 'formly:angular-formly-templates-material', 5 | summary: '(official): Material design templates for angular-formly', 6 | version: '0.14.3', 7 | documentation: 'README.md', 8 | git: 'https://github.com/formly-js/angular-formly-templates-material.git' 9 | }); 10 | 11 | Package.onUse(function formlyMaterialOnUse(api) { 12 | var packages = { 13 | use: [ 14 | 'angular@1.0.0', 15 | 'angular:angular-messages@1.4.7', 16 | 'angular:angular-material@1.0.0', 17 | 'formly:angular-formly@7.3.9_3' 18 | ], 19 | imply: [ 20 | 'angular:angular@1.4.7', 21 | 'angular:angular-messages', 22 | 'angular:angular-material', 23 | 'formly:angular-formly' 24 | ] 25 | }; 26 | 27 | api.versionsFrom(['METEOR@1.0']); 28 | 29 | api.use(packages.use); 30 | api.imply(packages.imply); 31 | 32 | api.addFiles([ 33 | 'dist/formly-material.js' 34 | ], 'client'); 35 | }); 36 | -------------------------------------------------------------------------------- /demo/client/components/demo/demo.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    5 |

    6 | formlyMaterial 7 |

    8 |
    9 |
    10 | 11 | 12 | 13 |
    14 | 15 | 16 |
    17 | 18 | 19 | 20 |

    21 | Demo 22 |

    23 |
    24 |
    25 | 26 |
    27 | 28 |
    29 | -------------------------------------------------------------------------------- /docs/types/radio.md: -------------------------------------------------------------------------------- 1 | # radio 2 | ## md-radio-group | md-radio-button 3 | 4 | ### Example 5 | 6 | ```javascript 7 | { 8 | type: "radio", 9 | key: "name", 10 | templateOptions: { 11 | label: "Name", 12 | theme: "custom", 13 | labelProp: "firstName", 14 | valueProp: "id", 15 | options: [ 16 | {firstName: "Sarah", id: 1}, 17 | {firstName: "Jessica", id: 2}, 18 | {firstName: "Parker", id: 3} 19 | ] 20 | } 21 | } 22 | ``` 23 | 24 | ### Configuration 25 | 26 | #### templateOptions.label _: string_ 27 | 28 | #### templateOptions.theme _: string_ 29 | 30 | #### templateOptions.disabled _: boolean_ 31 | 32 | #### templateOptions.className _: string | expression | array_ 33 | 34 | #### templateOptions.options _: array_ 35 | 36 | Array with available options 37 | 38 | #### templateOptions.labelProp _: string (default: name)_ 39 | 40 | Name of property with option's label 41 | 42 | #### templateOptions.valueProp _: string_ 43 | 44 | Name of property with option's value 45 | -------------------------------------------------------------------------------- /demo/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base # Packages every Meteor app needs to have 8 | mobile-experience # Packages for a great mobile UX 9 | mongo # The database Meteor supports right now 10 | session # Client-side reactive dictionary for your app 11 | jquery # Helpful client-side library 12 | tracker # Meteor's client-side reactive programming library 13 | 14 | standard-minifiers # JS/CSS minifiers run for production mode 15 | es5-shim # ECMAScript 5 compatibility for older browsers. 16 | 17 | angular 18 | pbastowski:angular2-now 19 | formly:angular-formly-templates-material 20 | angularui:angular-ui-router 21 | underscore 22 | planettraining:material-design-icons 23 | simple:highlight.js 24 | -------------------------------------------------------------------------------- /demo/client/data/examples/input.js: -------------------------------------------------------------------------------- 1 | const { SetModule } = angular2now; 2 | 3 | SetModule('demo').run(['Examples', 'Menu', (Examples, Menu) => { 4 | const fields = []; 5 | 6 | fields.push({ 7 | key: 'name', 8 | type: 'input', 9 | templateOptions: { 10 | label: 'Name', 11 | pattern: '[a-zA-Z]+' 12 | }, 13 | validation: { 14 | messages: { 15 | pattern: () => 'a-zA-Z only' 16 | } 17 | } 18 | }); 19 | 20 | fields.push({ 21 | key: 'sum', 22 | type: 'input', 23 | templateOptions: { 24 | type: 'number', 25 | label: 'Sum', 26 | step: 0.5, 27 | min: 0, 28 | max: 10 29 | }, 30 | validation: { 31 | messages: { 32 | max: () => '10 is max', 33 | min: () => '0 is min', 34 | number: () => 'Invalid format' 35 | } 36 | } 37 | }); 38 | 39 | // set example 40 | Examples.set('input', { 41 | fields 42 | }, 'types/input.md'); 43 | 44 | // add menu item to types 45 | Menu.addChild('types', 'input'); 46 | }]); 47 | -------------------------------------------------------------------------------- /src/types/slider/slider.js: -------------------------------------------------------------------------------- 1 | import template from './slider.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setType({ 5 | template, 6 | name: 'slider', 7 | wrapper: ['label'], 8 | defaultOptions: { 9 | templateOptions: { 10 | disabled: false 11 | }, 12 | ngModelAttrs: { 13 | disabled: { 14 | bound: 'ng-disabled' 15 | }, 16 | min: { 17 | attribute: 'min' 18 | }, 19 | max: { 20 | attribute: 'max' 21 | }, 22 | step: { 23 | attribute: 'step' 24 | }, 25 | discrete: { 26 | bound: 'md-discrete' 27 | } 28 | } 29 | }, 30 | apiCheck: (check) => ({ 31 | templateOptions: { 32 | disabled: check.bool.optional, 33 | min: check.number.optional, 34 | max: check.number.optional, 35 | step: check.number.optional, 36 | discrete: check.bool.optional, 37 | theme: check.string.optional 38 | } 39 | }) 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /docs/types/datepicker.md: -------------------------------------------------------------------------------- 1 | # datepicker 2 | ## md-datepicker 3 | 4 | ### Example 5 | 6 | ```javascript 7 | { 8 | type: "datepicker", 9 | key: "start", 10 | templateOptions: { 11 | theme: "custom", 12 | placeholder: "Start date", 13 | minDate: minDate, // instance of Date 14 | maxDate: maxDate, // instance of Date 15 | filterDate: function(date) { 16 | // only weekends 17 | var day = date.getDay(); 18 | return day === 0 || day === 6; 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | ### Configuration 25 | 26 | #### templateOptions.label _: string_ 27 | 28 | #### templateOptions.theme _: string_ 29 | 30 | #### templateOptions.disabled _: boolean_ 31 | 32 | #### templateOptions.className _: string | expression | array_ 33 | 34 | #### templateOptions.placeholder _: string_ 35 | The date input placeholder value 36 | 37 | #### templateOptions.minDate _: Date_ 38 | Min date of choice 39 | 40 | #### templateOptions.maxDate _: Date_ 41 | Max date of choice 42 | 43 | #### templateOptions.filterDate _: function_ 44 | Function expecting a date and returning a boolean whether it can be selected or not. 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kamil Kisiela 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 | -------------------------------------------------------------------------------- /demo/client/data/examples.js: -------------------------------------------------------------------------------- 1 | const { SetModule, Service } = angular2now; 2 | 3 | SetModule('demo'); 4 | 5 | @Service({ 6 | name: 'Examples' 7 | }) 8 | class Examples { 9 | constructor() { 10 | this.examples = new Map(); 11 | } 12 | 13 | /** 14 | * Add new example 15 | * 16 | * @param {String} id unique value for state `id` parameter 17 | * @param {Object} options formly configuration 18 | * @param {String} api it can be an absolute url or just `types/input.md` (see docs local variable) 19 | */ 20 | set(id, { fields, model, options }, api) { 21 | const docs = 'https://github.com/formly-js/angular-formly-templates-material/blob/master/docs/'; 22 | 23 | this.examples.set(id, { 24 | api: (typeof api === 'string' && !api.match('^http')) ? docs + api : api, 25 | formly: { 26 | fields, 27 | model, 28 | options 29 | } 30 | }); 31 | } 32 | 33 | /** 34 | * Get example 35 | * 36 | * @param {String} id example's identifier 37 | * @return {Object} example's data with API url and formly configuration 38 | */ 39 | get(id) { 40 | return this.examples.get(id); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/types/datepicker/datepicker.js: -------------------------------------------------------------------------------- 1 | import template from './datepicker.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setType({ 5 | template, 6 | name: 'datepicker', 7 | wrapper: ['label', 'messages'], 8 | defaultOptions: { 9 | templateOptions: { 10 | disabled: false 11 | }, 12 | ngModelAttrs: { 13 | disabled: { 14 | bound: 'ng-disabled' 15 | }, 16 | placeholder: { 17 | attribute: 'md-placeholder' 18 | }, 19 | minDate: { 20 | bound: 'md-min-date' 21 | }, 22 | maxDate: { 23 | bound: 'md-max-date' 24 | }, 25 | filterDate: { 26 | bound: 'md-date-filter' 27 | } 28 | } 29 | }, 30 | apiCheck: (check) => ({ 31 | templateOptions: { 32 | disabled: check.bool.optional, 33 | placeholder: check.string.optional, 34 | minDate: check.instanceOf(Date).optional, 35 | maxDate: check.instanceOf(Date).optional, 36 | filterDate: check.func.optional, 37 | theme: check.string.optional 38 | } 39 | }) 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /tests/wrappers/input-container-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | 3 | describe('formlyMaterial - inputContainer wrapper', () => { 4 | // 5 | // vars 6 | // 7 | let $compile; 8 | let $rootScope; 9 | let $scope; 10 | let element; 11 | 12 | // 13 | // helpers 14 | // 15 | 16 | function compile() { 17 | $scope = $rootScope.$new(); 18 | $scope.fields = [{ 19 | key: 'testField', 20 | type: 'checkbox', 21 | wrapper: ['inputContainer'], 22 | templateOptions: { 23 | label: 'test field' 24 | } 25 | }]; 26 | 27 | const form = $compile(testUtils.getFormTemplate())($scope); 28 | $scope.$digest(); 29 | element = form.find('md-input-container'); 30 | } 31 | 32 | // 33 | // tests 34 | // 35 | 36 | beforeEach(() => { 37 | window.module('formlyMaterial'); 38 | 39 | inject((_$compile_, _$rootScope_) => { 40 | $compile = _$compile_; 41 | $rootScope = _$rootScope_; 42 | }); 43 | 44 | compile(); 45 | }); 46 | 47 | it('should exist', () => { 48 | expect(element.length).toBe(1); 49 | }); 50 | 51 | it('should contain field', () => { 52 | expect(element.find('md-checkbox').length).toBe(1); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | notifications: 6 | email: false 7 | node_js: 8 | - iojs 9 | before_install: 10 | - npm install 11 | - export DISPLAY=:99.0 12 | - sh -e /etc/init.d/xvfb start 13 | before_script: 14 | - npm prune 15 | script: 16 | - npm run test 17 | - npm run lint 18 | after_success: 19 | - npm run coverage:codecov 20 | - npm run coverage:coveralls 21 | - npm run coverage:codacy 22 | deploy: 23 | provider: npm 24 | email: "kamil.kisiela@ster-project.pl" 25 | api_key: 26 | secure: in2pTjQm665cn37CW0Y4VuHkoXvZMmXRMRSmUCP2SUcTrbd69g7Sql+Y0o5RKv97cEGv53StZCWIcXwLMKxnTOTMvzztnvQjB4QhrVifS3/YmNTPTrFrjQDa+2SEpVqDk9lbl98KXwespN4lEG1OmooZrQjD7QXQjjQgkpG/zAzhn/8gaeGZgMumrg4/y5MDv2XhyJUnc4pwc6ZxsHKjjfhvsqPR87v5smxuCOXz3Pibtn88rohQE/Pjz/qqXyPvtzeEG84mTgqviP+r4R68IUWUT26uqp0l1QwwHbJspH7s/J+1VGinYJuA+AIafuoOII1Sm+wgM5hsl4MaF2AjCkYD8B03O80pDiSabwTJS14tXiFK5fF4uNVn8KQROjNkKAqNf49vjOQF2oo5G/GZCK4dLEnkU1Vj7WlRTm+0afD0qGsYfdCuKjU2PtFWt3adW5fBLmshsHQKNKd35tvRWiqvXkF3uGb2xqTUgRcgsvy1Gm3u/kzQncU4JtMh9EA0kyOhpaZstCu1tvkWvXhkQou5yo1aM1986ZsZKhqRkfaE3NCQTpd4cOn+o9H8BLWVaTvX5X2UBOmAjnjRQGSin8NG9ELRxZzj2M46G/R9sD1yqK5bW4lC8bHIp/5dE9YeGQ0DM9Ht/hnf7XoeI40ikKA699A9zq0MRXdsEcHEYSw= 27 | on: 28 | tags: true 29 | -------------------------------------------------------------------------------- /tests/wrappers/messages-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | 3 | describe('formlyMaterial - messages wrapper', () => { 4 | // 5 | // vars 6 | // 7 | let $compile; 8 | let $rootScope; 9 | let $scope; 10 | let element; 11 | 12 | // 13 | // helpers 14 | // 15 | 16 | function compile() { 17 | $scope = $rootScope.$new(); 18 | $scope.fields = [{ 19 | key: 'testField', 20 | type: 'checkbox', 21 | wrapper: ['messages'], 22 | templateOptions: { 23 | label: 'test field' 24 | } 25 | }]; 26 | 27 | const form = $compile(testUtils.getFormTemplate())($scope); 28 | 29 | $scope.$digest(); 30 | element = form.find('[ng-messages]'); 31 | } 32 | 33 | // 34 | // tests 35 | // 36 | 37 | beforeEach(() => { 38 | window.module('formlyMaterial'); 39 | 40 | inject((_$compile_, _$rootScope_) => { 41 | $compile = _$compile_; 42 | $rootScope = _$rootScope_; 43 | }); 44 | 45 | compile(); 46 | }); 47 | 48 | it('should exist', () => { 49 | expect(element.length).toBe(1); 50 | }); 51 | 52 | it('should be after the field', () => { 53 | expect(element.find('md-checkbox').length).toBe(0); 54 | expect(element.prev('.ng-scope').children('md-checkbox').length).toBe(1); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /src/types/textarea/textarea.js: -------------------------------------------------------------------------------- 1 | import template from './textarea.html'; 2 | import { ngModelAttrsTransformer } from './../../helpers'; 3 | 4 | export default (formlyConfigProvider) => { 5 | formlyConfigProvider.setType({ 6 | template, 7 | name: 'textarea', 8 | wrapper: ['label', 'messages', 'inputContainer'], 9 | defaultOptions: { 10 | ngModelAttrs: { 11 | disabled: { 12 | bound: 'ng-disabled' 13 | }, 14 | rows: { 15 | attribute: 'rows' 16 | }, 17 | cols: { 18 | attribute: 'cols' 19 | } 20 | }, 21 | templateOptions: { 22 | grow: true 23 | } 24 | }, 25 | apiCheck: (check) => ({ 26 | templateOptions: { 27 | disabled: check.bool.optional, 28 | rows: check.number.optional, 29 | cols: check.number.optional, 30 | grow: check.bool.optional, 31 | theme: check.string.optional 32 | } 33 | }) 34 | }); 35 | 36 | formlyConfigProvider.extras.fieldTransform.push((fields) => { 37 | return ngModelAttrsTransformer(fields, (field) => ( 38 | field.type === 'textarea' && 39 | field.templateOptions && 40 | field.templateOptions.grow === false 41 | ), 'grow', { 42 | attribute: 'md-no-autogrow' 43 | }); 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /demo/client/components/demo/demo.js: -------------------------------------------------------------------------------- 1 | const { 2 | SetModule, Component, View, State, Inject, bootstrap, options 3 | } = angular2now; 4 | 5 | options({ 6 | controllerAs: 'vm' 7 | }); 8 | 9 | SetModule('demo', [ 10 | 'angular-meteor', 11 | 'ui.router', 12 | 'formlyMaterial', 13 | 'hljs' 14 | ]).config(['$mdThemingProvider', '$mdIconProvider', ($mdThemingProvider, $mdIconProvider) => { 15 | $mdThemingProvider.theme('default') 16 | .primaryPalette('deep-purple') 17 | .accentPalette('green') 18 | .warnPalette('red') 19 | .backgroundPalette('grey'); 20 | 21 | $mdIconProvider.iconSet('navigation', 22 | `/packages/planettraining_material-design-icons/bower_components/material-design-icons/sprites/svg-sprite/svg-sprite-navigation.svg`); 23 | }]); 24 | 25 | @Component({ 26 | selector: 'demo' 27 | }) 28 | @View({ 29 | templateUrl: 'client/components/demo/demo.html' 30 | }) 31 | @State({ 32 | name: 'demo', 33 | url: '/demo', 34 | abstract: true, 35 | html5Mode: true 36 | }) 37 | @Inject(['Menu', '$mdSidenav']) 38 | class DemoComponent { 39 | constructor(Menu, $mdSidenav) { 40 | this.$mdSidenav = $mdSidenav; 41 | 42 | this.menu = Menu.get(); 43 | } 44 | 45 | toggleMenu() { 46 | this.$mdSidenav('left') 47 | .toggle(); 48 | } 49 | } 50 | 51 | bootstrap(DemoComponent); 52 | -------------------------------------------------------------------------------- /src/types/input/input.js: -------------------------------------------------------------------------------- 1 | import template from './input.html'; 2 | import { ngModelAttrsTransformer } from './../../helpers'; 3 | 4 | export default (formlyConfigProvider) => { 5 | formlyConfigProvider.setType({ 6 | template, 7 | name: 'input', 8 | wrapper: ['label', 'messages', 'inputContainer'], 9 | defaultOptions: { 10 | templateOptions: { 11 | type: 'text', 12 | disabled: false 13 | }, 14 | ngModelAttrs: { 15 | mdMaxlength: { 16 | bound: 'md-maxlength' 17 | }, 18 | // XXX angular-formly#8042d2a so we want to keep it compatible 19 | // with angular-formly releases before that commit 20 | step: { 21 | attribute: 'step' 22 | }, 23 | disabled: { 24 | bound: 'ng-disabled' 25 | }, 26 | pattern: { 27 | bound: 'ng-pattern' 28 | } 29 | } 30 | }, 31 | apiCheck: (check) => { 32 | return { 33 | templateOptions: { 34 | disabled: check.bool.optional, 35 | type: check.string, 36 | step: check.number.optional, 37 | pattern: check.oneOfType([ 38 | check.string, 39 | check.instanceOf(RegExp) 40 | ]).optional, 41 | theme: check.string.optional 42 | } 43 | }; 44 | } 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /webpack/common.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var webpack = require('webpack'); 3 | var pkg = require('../package.json'); 4 | 5 | function concatArrays(objValue, srcValue) { 6 | if (_.isArray(objValue)) { 7 | return _.uniq(objValue.concat(srcValue)); 8 | } 9 | } 10 | 11 | module.exports = function(cfg) { 12 | var common = { 13 | stats: { 14 | colors: true, 15 | reasons: true 16 | }, 17 | entry: { 18 | 'dist/formly-material': './src/index' 19 | }, 20 | output: { 21 | libraryTarget: 'umd', 22 | library: 'ngFormlyMaterial' 23 | }, 24 | resolve: { 25 | extensions: ['', '.js', '.html'] 26 | }, 27 | externals: { 28 | angular: 'angular' 29 | }, 30 | babel: { 31 | presets: ['es2015'] 32 | }, 33 | module: { 34 | loaders: [{ 35 | test: /\.js$/, 36 | include: [ 37 | /tests/, 38 | /src/ 39 | ], 40 | exclude: /node_modules/, 41 | loader: 'babel' 42 | }, { 43 | test: /\.html$/, 44 | exclude: /node_modules/, 45 | include: [/src/], 46 | loader: 'html' 47 | }] 48 | }, 49 | plugins: [ 50 | new webpack.BannerPlugin(pkg.name + ' v' + pkg.version + ' | MIT | built with ♥ by ' + pkg.author) 51 | ] 52 | }; 53 | 54 | return _.merge({}, common, cfg, concatArrays); 55 | }; 56 | -------------------------------------------------------------------------------- /src/types/chips/chips.js: -------------------------------------------------------------------------------- 1 | import template from './chips.html'; 2 | 3 | export default (formlyConfigProvider) => { 4 | formlyConfigProvider.setType({ 5 | template, 6 | name: 'chips', 7 | wrapper: ['label'], 8 | defaultOptions: { 9 | defaultValue: [], 10 | ngModelAttrs: { 11 | placeholder: { 12 | attribute: 'placeholder' 13 | }, 14 | secondaryPlaceholder: { 15 | attribute: 'secondary-placeholder' 16 | }, 17 | deleteButtonLabel: { 18 | attribute: 'delete-button-label' 19 | }, 20 | deleteHint: { 21 | attribute: 'delete-hint' 22 | }, 23 | onAdd: { 24 | statement: 'md-on-add' 25 | }, 26 | onRemove: { 27 | statement: 'md-on-remove' 28 | }, 29 | onSelect: { 30 | statement: 'md-on-select' 31 | } 32 | } 33 | }, 34 | apiCheck: (check) => ({ 35 | templateOptions: { 36 | placeholder: check.string.optional, 37 | secondaryPlaceholder: check.string.optional, 38 | deleteButtonLabel: check.string.optional, 39 | deleteHint: check.string.optional, 40 | onAdd: check.func.optional, 41 | onRemove: check.func.optional, 42 | onSelect: check.func.optional, 43 | theme: check.string.optional 44 | } 45 | }) 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /src/types/select/select.js: -------------------------------------------------------------------------------- 1 | import template from './select.html'; 2 | import { ngModelAttrsManipulator } from './../../helpers'; 3 | 4 | export default (formlyConfigProvider) => { 5 | formlyConfigProvider.setType({ 6 | template, 7 | name: 'select', 8 | wrapper: ['label', 'messages', 'inputContainer'], 9 | defaultOptions: { 10 | templateOptions: { 11 | disabled: false 12 | }, 13 | ngModelAttrs: { 14 | disabled: { 15 | bound: 'ng-disabled' 16 | }, 17 | onClose: { 18 | statement: 'md-on-close' 19 | }, 20 | onOpen: { 21 | statement: 'md-on-open' 22 | } 23 | } 24 | }, 25 | apiCheck: (check) => ({ 26 | templateOptions: { 27 | disabled: check.bool.optional, 28 | options: check.arrayOf(check.object), 29 | multiple: check.bool.optional, 30 | labelProp: check.string.optional, 31 | valueProp: check.string.optional, 32 | onClose: check.func.optional, 33 | onOpen: check.func.optional, 34 | theme: check.string.optional 35 | } 36 | }) 37 | }); 38 | 39 | formlyConfigProvider.templateManipulators.preWrapper.push((tpl, options) => { 40 | const to = options.templateOptions || {}; 41 | // adds multiple only when: 42 | // templateOptions.multiple equals true 43 | return to.multiple === true ? ngModelAttrsManipulator(tpl, options, 'multiple') : tpl; 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /docs/types/select.md: -------------------------------------------------------------------------------- 1 | select 2 | ====== 3 | 4 | md-select 5 | --------- 6 | 7 | ### Example 8 | 9 | ```javascript 10 | { 11 | type: "select", 12 | key: "name", 13 | templateOptions: { 14 | label: "Name", 15 | theme: "custom", 16 | multiple: true, 17 | labelProp: "firstName", 18 | valueProp: "id", 19 | options: [ 20 | {firstName: "Sarah", id: 1}, 21 | {firstName: "Jessica", id: 2}, 22 | {firstName: "Parker", id: 3} 23 | ], 24 | onOpen: () => { 25 | console.log('select is opened'); 26 | }, 27 | onClose: () => { 28 | console.log('select is closed'); 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | ### Configuration 35 | 36 | #### templateOptions.label *: string* 37 | 38 | #### templateOptions.theme *: string* 39 | 40 | #### templateOptions.disabled _: boolean_ 41 | 42 | #### templateOptions.className _: string | expression | array_ 43 | 44 | #### templateOptions.options *: array* 45 | 46 | Array with available options 47 | 48 | #### templateOptions.labelProp *: string (default: name)* 49 | 50 | Name of property with option's label 51 | 52 | #### templateOptions.valueProp *: string* 53 | 54 | Name of property with option's value 55 | 56 | #### templateOptions.multiple *: boolean* 57 | 58 | Multiple choice 59 | 60 | #### templateOptions.onOpen *: function* 61 | 62 | Bound to md-on-open. 63 | 64 | Expression to be evaluated when the select is opened. 65 | 66 | If expression returns a promise, it will display a loading indicator while it is being resolved. 67 | 68 | #### templateOptions.onClose *: function* 69 | 70 | Bound to md-on-close. 71 | 72 | Expression to be evaluated when the select is closed. 73 | -------------------------------------------------------------------------------- /docs/types/chips.md: -------------------------------------------------------------------------------- 1 | chips 2 | ===== 3 | 4 | md-chips 5 | -------- 6 | 7 | ### Example 8 | 9 | ```javascript 10 | { 11 | type: "chips", 12 | key: "tags", 13 | templateOptions: { 14 | theme: "custom", 15 | placeholder: "+tags", 16 | secondaryPlaceholder: "Add tag", 17 | deleteButtonLabel: "Remove", 18 | deleteHint: "Remove tag", 19 | onAdd: function() { 20 | console.log('new chip'); 21 | }, 22 | onRemove: function() { 23 | console.log('chip removed'); 24 | } 25 | } 26 | } 27 | ``` 28 | 29 | ### Configuration 30 | 31 | #### templateOptions.label *: string* 32 | 33 | #### templateOptions.theme *: string* 34 | 35 | #### templateOptions.disabled _: boolean_ 36 | 37 | #### templateOptions.className _: string | expression | array_ 38 | 39 | #### templateOptions.placeholder *: string* 40 | 41 | Placeholder text that will be forwarded to the input. 42 | 43 | #### templateOptions.secondaryPlaceholder *: string* 44 | 45 | Placeholder text that will be forwarded to the input, displayed when there is at least on item in the list. 46 | 47 | #### templateOptions.deleteButtonLabel *: string* 48 | 49 | A label for the delete button. Also hidden and read by screen readers. 50 | 51 | #### templateOptions.deleteHint *: string* 52 | 53 | A string read by screen readers instructing users that pressing the delete key will remove the chip. 54 | 55 | #### templateOptions.onAdd *: function* 56 | 57 | An expression which will be called when a chip has been added. 58 | 59 | #### templateOptions.onRemove *: function* 60 | 61 | An expression which will be called when a chip has been removed. 62 | 63 | #### templateOptions.onSelect *: function* 64 | 65 | An expression which will be called when a chip has been selected. 66 | -------------------------------------------------------------------------------- /tests/types/checkbox-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - checkbox type', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let element; 12 | let elementScope; 13 | 14 | // 15 | // helpers 16 | // 17 | 18 | function compile(options) { 19 | $scope = $rootScope.$new(); 20 | $scope.fields = [angular.merge({}, { 21 | key: 'testField', 22 | type: 'checkbox', 23 | templateOptions: { 24 | label: 'test field', 25 | theme: 'custom', 26 | disabled: true 27 | } 28 | }, options)]; 29 | 30 | const form = $compile(testUtils.getFormTemplate())($scope); 31 | 32 | $scope.$digest(); 33 | element = form.find('[ng-model]'); 34 | elementScope = angular.element(element).scope(); 35 | } 36 | 37 | // 38 | // tests 39 | // 40 | 41 | beforeEach(() => { 42 | window.module('formlyMaterial'); 43 | 44 | inject((_$compile_, _$rootScope_) => { 45 | $compile = _$compile_; 46 | $rootScope = _$rootScope_; 47 | }); 48 | 49 | compile(); 50 | }); 51 | 52 | it('should be md-checkbox element', () => { 53 | expect(element[0].nodeName).toBe('MD-CHECKBOX'); 54 | }); 55 | 56 | it('should have label', () => { 57 | expect(element.html()).toContain('test field'); 58 | }); 59 | 60 | it('should be disabled', () => { 61 | expect(element.attr('ng-disabled')).toBe(`options.templateOptions['disabled']`); 62 | expect(elementScope.options.templateOptions.disabled).toBe(true); 63 | }); 64 | 65 | it('should have proper theme when defined', () => { 66 | expect(element.is('.md-custom-theme')).toBe(true); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /tests/wrappers/divider-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - divider wrapper', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let element; 12 | 13 | // 14 | // helpers 15 | // 16 | 17 | function compile(options) { 18 | $scope = $rootScope.$new(); 19 | $scope.fields = [angular.merge({}, { 20 | key: 'testField', 21 | type: 'checkbox', 22 | wrapper: ['divider'] 23 | }, options)]; 24 | 25 | const form = $compile(testUtils.getFormTemplate())($scope); 26 | $scope.$digest(); 27 | element = form.find('md-divider'); 28 | } 29 | 30 | // 31 | // tests 32 | // 33 | 34 | beforeEach(() => { 35 | window.module('formlyMaterial'); 36 | 37 | inject((_$compile_, _$rootScope_) => { 38 | $compile = _$compile_; 39 | $rootScope = _$rootScope_; 40 | }); 41 | }); 42 | 43 | it('should exist', () => { 44 | compile(); 45 | expect(element.length).toBe(1); 46 | }); 47 | 48 | it('should be after the field by default', () => { 49 | compile(); 50 | // not transcluded inner 51 | expect(element.find('md-checkbox').length).toBe(0); 52 | // checkbox transcluded before 53 | expect(element.prev('.ng-scope').children('md-checkbox').length).toBe(1); 54 | }); 55 | 56 | it('should be before the field when divider option equals before', () => { 57 | compile({ 58 | templateOptions: { 59 | divider: 'before' 60 | } 61 | }); 62 | // not transcluded inner 63 | expect(element.find('md-checkbox').length).toBe(0); 64 | // checkbox transcluded before 65 | expect(element.next('.ng-scope').children('md-checkbox').length).toBe(1); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /tests/types/switch-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - switch type', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let element; 12 | let elementScope; 13 | let field; 14 | const theme = 'custom'; 15 | 16 | // 17 | // helpers 18 | // 19 | 20 | function compile(options) { 21 | $scope = $rootScope.$new(); 22 | $scope.fields = [angular.merge({}, { 23 | key: 'testField', 24 | type: 'switch', 25 | templateOptions: { 26 | theme, 27 | label: 'test field', 28 | disabled: true 29 | } 30 | }, options)]; 31 | 32 | const form = $compile(testUtils.getFormTemplate())($scope); 33 | 34 | $scope.$digest(); 35 | element = form.find('[ng-model]'); 36 | elementScope = element.scope(); 37 | field = $scope.fields[0]; 38 | } 39 | 40 | // 41 | // tests 42 | // 43 | 44 | beforeEach(() => { 45 | window.module('formlyMaterial'); 46 | 47 | inject((_$compile_, _$rootScope_) => { 48 | $compile = _$compile_; 49 | $rootScope = _$rootScope_; 50 | }); 51 | 52 | compile(); 53 | }); 54 | 55 | it('should be md-switch element', () => { 56 | expect(element[0].nodeName).toBe('MD-SWITCH'); 57 | }); 58 | 59 | it('should have proper theme when defined', () => { 60 | expect(element.is(`.md-${theme}-theme`)).toBe(true); 61 | }); 62 | 63 | it('should have label', () => { 64 | expect(element.find('.md-label > span').html()).toContain(field.templateOptions.label); 65 | }); 66 | 67 | it('should be able to be disabled', () => { 68 | expect(element.attr('ng-disabled')).toBe(`options.templateOptions['disabled']`); 69 | expect(elementScope.options.templateOptions.disabled).toBe(true); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /tests/runs/class-name-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - className manipulator', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let element; 12 | let elementScope; 13 | 14 | // 15 | // helpers 16 | // 17 | 18 | function compile(options) { 19 | $scope = $rootScope.$new(); 20 | $scope.fields = [angular.merge({}, { 21 | key: 'testField', 22 | type: 'switch', 23 | templateOptions: { 24 | className: 'custom', 25 | label: 'test field' 26 | } 27 | }, options)]; 28 | 29 | const form = $compile(angular.element(testUtils.getFormTemplate()))($scope); 30 | 31 | $scope.$digest(); 32 | element = form.find('[ng-model]'); 33 | elementScope = element.scope(); 34 | } 35 | 36 | // 37 | // tests 38 | // 39 | 40 | beforeEach(() => { 41 | window.module('formlyMaterial'); 42 | 43 | inject((_$compile_, _$rootScope_) => { 44 | $compile = _$compile_; 45 | $rootScope = _$rootScope_; 46 | }); 47 | }); 48 | 49 | it('should be able to add ng-class directive', () => { 50 | compile(); 51 | expect(element.attr('ng-class')).toBe(`options.templateOptions['className']`); 52 | }); 53 | 54 | it('should pass one class as a string', () => { 55 | compile(); 56 | expect(elementScope.options.templateOptions.className).toBe('custom'); 57 | expect(element.attr('class')).toContain('custom'); 58 | }); 59 | 60 | it('should pass few classes as an array', () => { 61 | const classes = ['foo', 'bar', 'baz']; 62 | 63 | compile({ 64 | templateOptions: { 65 | className: classes 66 | } 67 | }); 68 | 69 | expect(elementScope.options.templateOptions.className).toEqual(classes); 70 | 71 | classes.forEach((clss) => { 72 | expect(element.attr('class')).toContain(clss); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen### Node template 10 | # Logs 11 | logs 12 | *.log 13 | npm-debug.log* 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directory 36 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 37 | node_modules 38 | bower_components 39 | 40 | ### JetBrains template 41 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 42 | 43 | *.iml 44 | 45 | ## Directory-based project format: 46 | .idea/ 47 | # if you remove the above rule, at least ignore the following: 48 | 49 | # User-specific stuff: 50 | # .idea/workspace.xml 51 | # .idea/tasks.xml 52 | # .idea/dictionaries 53 | 54 | # Sensitive or high-churn files: 55 | # .idea/dataSources.ids 56 | # .idea/dataSources.xml 57 | # .idea/sqlDataSources.xml 58 | # .idea/dynamic.xml 59 | # .idea/uiDesigner.xml 60 | 61 | # Gradle: 62 | # .idea/gradle.xml 63 | # .idea/libraries 64 | 65 | # Mongo Explorer plugin: 66 | # .idea/mongoSettings.xml 67 | 68 | ## File-based project format: 69 | *.ipr 70 | *.iws 71 | 72 | ## Plugin-specific files: 73 | 74 | # IntelliJ 75 | /out/ 76 | 77 | # mpeltonen/sbt-idea plugin 78 | .idea_modules/ 79 | 80 | # JIRA plugin 81 | atlassian-ide-plugin.xml 82 | 83 | # Crashlytics plugin (for Android Studio and IntelliJ) 84 | com_crashlytics_export_strings.xml 85 | crashlytics.properties 86 | crashlytics-build.properties 87 | 88 | -------------------------------------------------------------------------------- /demo/.meteor/versions: -------------------------------------------------------------------------------- 1 | angular@1.3.4 2 | angular-meteor-data@0.0.9 3 | angular-templates@0.0.3 4 | angular:angular@1.4.8 5 | angular:angular-animate@1.4.8 6 | angular:angular-aria@1.4.8 7 | angular:angular-material@1.0.1 8 | angular:angular-messages@1.4.8 9 | angularui:angular-ui-router@0.2.15 10 | autoupdate@1.2.4 11 | babel-compiler@5.8.24_1 12 | babel-runtime@0.1.4 13 | base64@1.0.4 14 | benjamine:jsondiffpatch@0.1.38_1 15 | binary-heap@1.0.4 16 | blaze@2.1.3 17 | blaze-tools@1.0.4 18 | boilerplate-generator@1.0.4 19 | caching-compiler@1.0.0 20 | caching-html-compiler@1.0.2 21 | callback-hook@1.0.4 22 | check@1.1.0 23 | dburles:mongo-collection-instances@0.3.4 24 | ddp@1.2.2 25 | ddp-client@1.2.1 26 | ddp-common@1.2.2 27 | ddp-server@1.2.2 28 | deps@1.0.9 29 | diff-sequence@1.0.1 30 | ecmascript@0.1.6 31 | ecmascript-runtime@0.2.6 32 | ejson@1.0.7 33 | es5-shim@4.1.14 34 | fastclick@1.0.7 35 | formly:angular-formly@7.3.9_3 36 | formly:angular-formly-templates-material@0.14.0 37 | geojson-utils@1.0.4 38 | hot-code-push@1.0.0 39 | html-tools@1.0.5 40 | htmljs@1.0.5 41 | http@1.1.1 42 | id-map@1.0.4 43 | jquery@1.11.4 44 | lai:collection-extensions@0.1.4 45 | launch-screen@1.0.4 46 | livedata@1.0.15 47 | logging@1.0.8 48 | meteor@1.1.10 49 | meteor-base@1.0.1 50 | minifiers@1.1.7 51 | minimongo@1.0.10 52 | mobile-experience@1.0.1 53 | mobile-status-bar@1.0.6 54 | mongo@1.1.3 55 | mongo-id@1.0.1 56 | npm-mongo@1.4.39_1 57 | observe-sequence@1.0.7 58 | ordered-dict@1.0.4 59 | pbastowski:angular-babel@1.0.8 60 | pbastowski:angular2-now@1.0.2 61 | planettraining:material-design-icons@2.1.1 62 | promise@0.5.1 63 | random@1.0.5 64 | reactive-dict@1.1.3 65 | reactive-var@1.0.6 66 | reload@1.1.4 67 | retry@1.0.4 68 | routepolicy@1.0.6 69 | session@1.1.1 70 | simple:highlight.js@1.2.0 71 | spacebars@1.0.7 72 | spacebars-compiler@1.0.7 73 | standard-minifiers@1.0.2 74 | templating-tools@1.0.0 75 | tracker@1.0.9 76 | ui@1.0.8 77 | underscore@1.0.4 78 | url@1.0.5 79 | webapp@1.2.3 80 | webapp-hashing@1.0.5 81 | wieldo:api-check@7.5.5 82 | -------------------------------------------------------------------------------- /tests/helpers/ng-model-attrs-manipulator-spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | ngModelAttrsManipulator 3 | } 4 | from './../../src/helpers'; 5 | 6 | import angular from 'angular'; 7 | 8 | describe('formlyMaterial - ngModelAttrsManipulator', () => { 9 | it('should skip on skipNgModelAttrsManipulator', () => { 10 | const tpl = 'test'; 11 | const result = ngModelAttrsManipulator(tpl, { 12 | extras: { 13 | skipNgModelAttrsManipulator: true 14 | } 15 | }); 16 | 17 | expect(result).toBe(tpl); 18 | }); 19 | 20 | it('should not modify on missing ngModel', () => { 21 | const tpl = 'test'; 22 | const result = ngModelAttrsManipulator(tpl, {}); 23 | 24 | expect(result).toBe(tpl); 25 | }); 26 | 27 | it('should add attribute with value to ngModel', () => { 28 | const tpl = `
    `; 29 | const attr = { 30 | name: 'foo', 31 | value: 'bar' 32 | }; 33 | const result = ngModelAttrsManipulator(tpl, {}, attr.name, attr.value); 34 | const element = angular.element(result); 35 | 36 | expect(result).not.toBe(tpl); 37 | expect(element.find('[ng-model]').attr(attr.name)).toBe(attr.value); 38 | }); 39 | 40 | it('should add attribute with value to few elements with ngModel', () => { 41 | const tpl = `
    `; 42 | const attr = { 43 | name: 'foo', 44 | value: 'bar' 45 | }; 46 | const result = ngModelAttrsManipulator(tpl, {}, attr.name, attr.value); 47 | const element = angular.element(result); 48 | 49 | expect(result).not.toBe(tpl); 50 | expect(element.find('[ng-model]').length).toBe(2); 51 | expect(element.find('[ng-model]:eq(0)').attr(attr.name)).toBe(attr.value); 52 | expect(element.find('[ng-model]:eq(1)').attr(attr.name)).toBe(attr.value); 53 | }); 54 | 55 | it('should not overwrite attribute on ngModel', () => { 56 | const name = 'baz'; 57 | const tpl = `
    `; 58 | const attr = { 59 | name: 'foo', 60 | value: 'bar' 61 | }; 62 | const result = ngModelAttrsManipulator(tpl, {}, attr.name, attr.value); 63 | const element = angular.element(result); 64 | 65 | expect(result).not.toBe(tpl); 66 | expect(element.find('[ng-model]').attr(attr.name)).toBe(name); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /tests/wrappers/label-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - label wrapper', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let element; 12 | let field; 13 | const color = 'rgb(117, 117, 117)'; 14 | 15 | // 16 | // helpers 17 | // 18 | 19 | function compile(options) { 20 | $scope = $rootScope.$new(); 21 | $scope.fields = [angular.merge({}, { 22 | key: 'testField', 23 | type: 'checkbox', 24 | wrapper: ['label'], 25 | templateOptions: { 26 | label: 'test field' 27 | } 28 | }, options)]; 29 | 30 | const form = $compile(testUtils.getFormTemplate())($scope); 31 | 32 | $scope.$digest(); 33 | element = form.find('label'); 34 | field = $scope.fields[0]; 35 | } 36 | 37 | // 38 | // tests 39 | // 40 | 41 | beforeEach(() => { 42 | window.module('formlyMaterial'); 43 | 44 | inject((_$compile_, _$rootScope_) => { 45 | $compile = _$compile_; 46 | $rootScope = _$rootScope_; 47 | }); 48 | }); 49 | 50 | it('should exist', () => { 51 | compile(); 52 | expect(element.length).toBe(1); 53 | }); 54 | 55 | it('should have proper value', () => { 56 | compile(); 57 | expect(element.html()).toContain(field.templateOptions.label); 58 | }); 59 | 60 | it('should be before the field', () => { 61 | compile(); 62 | expect(element.find('md-checkbox').length).toBe(0); 63 | expect(element.next().children()[0].nodeName).toBe('MD-CHECKBOX'); 64 | }); 65 | 66 | it('should have styles added', () => { 67 | compile(); 68 | expect(element.css('color')).toBe(color); 69 | }); 70 | 71 | it('should not have styles added when used with input', () => { 72 | compile({ 73 | type: 'input' 74 | }); 75 | expect(element.css('color')).not.toBe(color); 76 | }); 77 | 78 | it('should not have styles added when used with select', () => { 79 | compile({ 80 | type: 'select', 81 | templateOptions: { 82 | options: [{ 83 | name: 'foo', 84 | value: 'bar' 85 | }] 86 | } 87 | }); 88 | expect(element.css('color')).not.toBe(color); 89 | }); 90 | 91 | it('should not have styles added when used with textarea', () => { 92 | compile({ 93 | type: 'textarea' 94 | }); 95 | expect(element.css('color')).not.toBe(color); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /tests/types/slider-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - slider type', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let form; 12 | let element; 13 | let elementScope; 14 | const theme = 'custom'; 15 | const fieldConfig = { 16 | key: 'testField', 17 | type: 'slider', 18 | templateOptions: { 19 | theme, 20 | label: 'test field', 21 | disabled: true, 22 | min: 1, 23 | max: 5, 24 | step: 0.5, 25 | discrete: true 26 | } 27 | }; 28 | // 29 | // helpers 30 | // 31 | 32 | function compile(options) { 33 | $scope = $rootScope.$new(); 34 | $scope.fields = [angular.merge({}, fieldConfig, options)]; 35 | 36 | form = $compile(testUtils.getFormTemplate())($scope); 37 | $scope.$digest(); 38 | element = form.find('[ng-model]'); 39 | elementScope = element.scope(); 40 | } 41 | 42 | // 43 | // tests 44 | // 45 | 46 | beforeEach(() => { 47 | window.module('formlyMaterial'); 48 | 49 | inject((_$compile_, _$rootScope_) => { 50 | $compile = _$compile_; 51 | $rootScope = _$rootScope_; 52 | }); 53 | 54 | compile(); 55 | }); 56 | 57 | it('should be md-slider element', () => { 58 | expect(element[0].nodeName).toBe('MD-SLIDER'); 59 | }); 60 | 61 | it('should have proper theme when defined', () => { 62 | expect(element.is(`.md-${theme}-theme`)).toBe(true); 63 | }); 64 | 65 | it('should support min option', () => { 66 | expect(parseFloat(element.attr('min'))).toEqual(fieldConfig.templateOptions.min); 67 | }); 68 | 69 | it('should support max option', () => { 70 | expect(parseFloat(element.attr('max'))).toEqual(fieldConfig.templateOptions.max); 71 | }); 72 | 73 | it('should support step option', () => { 74 | expect(parseFloat(element.attr('step'))).toEqual(fieldConfig.templateOptions.step); 75 | }); 76 | 77 | it('should support discrete option', () => { 78 | expect(element.attr('md-discrete')).toEqual(`options.templateOptions['discrete']`); 79 | expect(elementScope.options.templateOptions.discrete).toBe(fieldConfig.templateOptions.discrete); 80 | }); 81 | 82 | it('should be able to be disabled', () => { 83 | expect(element.attr('ng-disabled')).toBe(`options.templateOptions['disabled']`); 84 | expect(elementScope.options.templateOptions.disabled).toBe(true); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /src/helpers/index.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | 3 | /** 4 | * Sets attribute with optional value. 5 | * Does not owerwrite. 6 | * @param {Array} nodes nodes 7 | * @param {String} attr attribute name 8 | * @param {String} val atrtibute value 9 | */ 10 | function addIfNotPresent(nodes, attr, val) { 11 | angular.forEach(nodes, (node) => { 12 | if (!node.getAttribute(attr)) { 13 | node.setAttribute(attr, val); 14 | } 15 | }); 16 | } 17 | 18 | /** 19 | * Gets all ngModels from node 20 | */ 21 | function getNgModelNodes(node) { 22 | const query = '[ng-model], [data-ng-model]'; 23 | 24 | return node.querySelectorAll(query); 25 | } 26 | 27 | /** 28 | * Adds attribute with optional value to all elements using ngModel directive. 29 | * Handles extras.skipNgModelAttrsManipulator 30 | * And does not overwrite attriutes 31 | * 32 | * @param {String} template Template provided by formly template manipulator 33 | * @param {Object} options Options provided by formly template manipulator 34 | * @param {String} attrName Attribute's name 35 | * @param {String|undefined} Attribute's value (optional) 36 | * @return {String} result 37 | */ 38 | export function ngModelAttrsManipulator(template, options, attrName, attrValue) { 39 | const node = document.createElement('div'); 40 | const skip = options.extras && options.extras.skipNgModelAttrsManipulator; 41 | 42 | if (skip === true) { 43 | return template; 44 | } 45 | node.innerHTML = template; 46 | const modelNodes = getNgModelNodes(node); 47 | 48 | if (!modelNodes || !modelNodes.length) { 49 | return template; 50 | } 51 | 52 | addIfNotPresent(modelNodes, attrName, attrValue); 53 | 54 | return node.innerHTML; 55 | } 56 | 57 | /** 58 | * Adds ngModelAttr to the field when specified condition is true. 59 | * @param {Array} fields fields provided by formly's fieldTranform 60 | * @param {Funcion} condition with field as only parameter 61 | * @param {String} name ngModelAttr's name 62 | * @param {Object} settings ngModelAttr's settings 63 | * @return {Array} returns fields 64 | */ 65 | export function ngModelAttrsTransformer(fields, condition, name, settings) { 66 | (fields || []).forEach((field) => { 67 | if (condition(field) === true) { 68 | if (!field.ngModelAttrs) { 69 | field.ngModelAttrs = {}; 70 | } 71 | 72 | if (field.templateOptions && typeof field.templateOptions[name] !== 'undefined') { 73 | field.ngModelAttrs[name] = settings; 74 | } 75 | } 76 | }); 77 | 78 | return fields; 79 | } 80 | -------------------------------------------------------------------------------- /demo/client/data/menu.js: -------------------------------------------------------------------------------- 1 | const { SetModule, Service, Inject } = angular2now; 2 | 3 | SetModule('demo'); 4 | 5 | @Service({ 6 | name: 'Menu' 7 | }) 8 | @Inject(['Examples']) 9 | class Menu { 10 | constructor(Examples) { 11 | this.menu = []; 12 | 13 | // move it from here 14 | this.addHeading('basic'); 15 | this.addToggle('types'); 16 | this.addToggle('wrappers'); 17 | this.addHeading('advanced'); 18 | } 19 | 20 | /** 21 | * Add new menu item 22 | * @param {String} name Link's label 23 | * @param {String} type Can be heading, toggle or link 24 | * @param {String|undefined} id Used in /demo/:id 25 | */ 26 | add(name, type, id) { 27 | if (typeof name !== 'string' || name.length === 0) { 28 | throw new Error(`[Menu] name has to be a string`); 29 | } 30 | 31 | if (typeof type === 'undefined' || ['toggle', 'link', 'heading'].indexOf(type) === -1) { 32 | throw new Error(`[Menu] type has to be one of: heading, link, toggle`); 33 | } 34 | 35 | this.menu.push({ 36 | name, 37 | type, 38 | id 39 | }); 40 | } 41 | 42 | /** 43 | * Shorthand method to add menu-link 44 | * @param {String} name 45 | * @param {String} id 46 | */ 47 | addLink(name, id) { 48 | this.add(name, 'link', id || name); 49 | } 50 | 51 | /** 52 | * Shorthand method to add menu-heading 53 | * @param {String} name 54 | */ 55 | addHeading(name) { 56 | this.add(name, 'heading'); 57 | } 58 | 59 | /** 60 | * Shorthand method to add menu-toggle 61 | * @param {String} name 62 | */ 63 | addToggle(name) { 64 | this.add(name, 'toggle'); 65 | } 66 | 67 | /** 68 | * Add a child to menu-toggle 69 | * @param {String} parent Parent menu item id 70 | * @param {String} name item's label 71 | * @param {String} id item's identifier used as value of id parameter 72 | */ 73 | addChild(parent, name, id) { 74 | const found = _.find(this.menu, (item) => item.name === parent); 75 | 76 | if (!found) { 77 | throw new Error(`[Menu] There is no ${parent} available`); 78 | } 79 | 80 | found.children = found.children || []; 81 | 82 | if (_.find(found.children, (child) => child.name === name)) { 83 | throw new Error(`[Menu] There is a child ${name} already`); 84 | } 85 | 86 | found.children.push({ 87 | name, 88 | id: id || name 89 | }); 90 | 91 | found.children = _.sortBy(found.children, 'name'); 92 | } 93 | 94 | /** 95 | * Get menu 96 | * @return {Object} 97 | */ 98 | get() { 99 | return { 100 | sections: this.menu 101 | }; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/types/datepicker-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - datepicker type', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let form; 12 | let element; 13 | let elementScope; 14 | const theme = 'custom'; 15 | const minDate = new Date(2015, 10, 19); 16 | const maxDate = new Date(2015, 11, 20); 17 | const filterDate = () => true; 18 | 19 | // 20 | // helpers 21 | // 22 | 23 | function compile(options) { 24 | $scope = $rootScope.$new(); 25 | $scope.fields = [angular.merge({}, { 26 | key: 'testField', 27 | type: 'datepicker', 28 | templateOptions: { 29 | minDate, 30 | maxDate, 31 | filterDate, 32 | theme, 33 | disabled: true, 34 | label: 'test field', 35 | placeholder: 'Pick a date' 36 | } 37 | }, options)]; 38 | 39 | form = $compile(testUtils.getFormTemplate())($scope); 40 | $scope.$digest(); 41 | element = form.find('[ng-model]'); 42 | elementScope = angular.element(element).scope(); 43 | } 44 | 45 | // 46 | // tests 47 | // 48 | 49 | beforeEach(() => { 50 | window.module('formlyMaterial'); 51 | 52 | inject((_$compile_, _$rootScope_) => { 53 | $compile = _$compile_; 54 | $rootScope = _$rootScope_; 55 | }); 56 | 57 | compile(); 58 | }); 59 | 60 | it('should be md-chips element', () => { 61 | expect(element[0].nodeName).toBe('MD-DATEPICKER'); 62 | }); 63 | 64 | it('should have messages', () => { 65 | expect(form.find('[ng-messages]').length).toBe(1); 66 | }); 67 | 68 | it('should have proper theme when defined', () => { 69 | expect(element.attr('md-theme')).toBe(theme); 70 | }); 71 | 72 | it('should have min date', () => { 73 | expect(element.attr('md-min-date')).toBe(`options.templateOptions['minDate']`); 74 | expect(elementScope.options.templateOptions.minDate.toDateString()).toBe(minDate.toDateString()); 75 | }); 76 | 77 | it('should have max date', () => { 78 | expect(element.attr('md-max-date')).toBe(`options.templateOptions['maxDate']`); 79 | expect(elementScope.options.templateOptions.maxDate.toDateString()).toBe(maxDate.toDateString()); 80 | }); 81 | 82 | it('should have date filter', () => { 83 | expect(element.attr('md-date-filter')).toBe(`options.templateOptions['filterDate']`); 84 | expect(elementScope.options.templateOptions.filterDate).toBe(filterDate); 85 | }); 86 | 87 | it('should be disabled', () => { 88 | expect(element.attr('ng-disabled')).toBe(`options.templateOptions['disabled']`); 89 | expect(elementScope.options.templateOptions.disabled).toBe(true); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | require('argv-set-env')(); 2 | var webpack = require('./webpack')('test'); 3 | 4 | var testFile = 'tests/index-spec.js'; 5 | var ciEnv = process.env.NODE_ENV === 'ci'; 6 | 7 | var preprocessors = {}; 8 | preprocessors[testFile] = ['webpack']; 9 | 10 | module.exports = function(config) { 11 | var _config = { 12 | 13 | // base path that will be used to resolve all patterns (eg. files, exclude) 14 | basePath: '', 15 | 16 | 17 | // frameworks to use 18 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 19 | frameworks: ['jasmine'], 20 | 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | 'node_modules/jquery/dist/jquery.js', 25 | 'node_modules/api-check/dist/api-check.js', 26 | 'node_modules/angular/angular.js', 27 | 'node_modules/angular-mocks/angular-mocks.js', 28 | 'node_modules/angular-animate/angular-animate.js', 29 | 'node_modules/angular-aria/angular-aria.js', 30 | 'node_modules/angular-messages/angular-messages.js', 31 | 'node_modules/angular-material/angular-material.js', 32 | 'node_modules/angular-material/angular-material-mocks.js', 33 | 'node_modules/angular-formly/dist/formly.js', 34 | testFile 35 | ], 36 | 37 | 38 | // list of files to exclude 39 | exclude: [], 40 | 41 | 42 | // preprocess matching files before serving them to the browser 43 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 44 | preprocessors: preprocessors, 45 | 46 | webpack: webpack, 47 | 48 | coverageReporter: { 49 | reporters: [{ 50 | type: 'lcov', 51 | dir: 'coverage/', 52 | subdir: '.' 53 | }, { 54 | type: 'json', 55 | dir: 'coverage/', 56 | subdir: '.' 57 | }, { 58 | type: 'text-summary' 59 | }] 60 | }, 61 | 62 | // test results reporter to use 63 | // possible values: 'dots', 'progress' 64 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 65 | reporters: ['progress', 'coverage', 'kjhtml'], 66 | 67 | // web server port 68 | port: 9876, 69 | 70 | // enable / disable colors in the output (reporters and logs) 71 | colors: true, 72 | 73 | // level of logging 74 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 75 | logLevel: config.LOG_INFO, 76 | 77 | // enable / disable watching file and executing tests whenever any file changes 78 | autoWatch: !ciEnv, 79 | 80 | // start these browsers 81 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 82 | browsers: [(ciEnv ? 'Firefox' : 'Chrome')], 83 | 84 | // Continuous Integration mode 85 | // if true, Karma captures browsers, runs the tests and exits 86 | singleRun: ciEnv 87 | }; 88 | config.set(_config); 89 | }; 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-formly-material", 3 | "version": "0.14.3", 4 | "description": "Material design templates for angular-formly", 5 | "main": "dist/formly-material.js", 6 | "scripts": { 7 | "build:dist": "webpack --progress", 8 | "build:prod": "webpack --progress --set-env-NODE_ENV=production", 9 | "build": "npm run build:dist && npm run build:prod", 10 | "lint:src": "eslint src/**/*.js", 11 | "lint:tests": "eslint tests/**/*.js --no-ignore", 12 | "lint": "npm run lint:src && npm run lint:tests", 13 | "coverage:codecov": "cat ./coverage/lcov.info | node_modules/.bin/codecov", 14 | "coverage:coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 15 | "coverage:codacy": "cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage", 16 | "test": "karma start karma.conf.js --set-env-NODE_ENV=ci", 17 | "test:watch": "karma start karma.conf.js" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/formly-js/angular-formly-templates-material.git" 22 | }, 23 | "keywords": [ 24 | "AngularJs", 25 | "form", 26 | "formly", 27 | "json", 28 | "html", 29 | "material" 30 | ], 31 | "author": "Kamil Kisiela ", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/formly-js/angular-formly-templates-material/issues" 35 | }, 36 | "homepage": "https://github.com/formly-js/angular-formly-templates-material#readme", 37 | "dependencies": { 38 | "angular": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0", 39 | "angular-messages": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0", 40 | "angular-animate": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0", 41 | "angular-aria": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0", 42 | "angular-material": "^1.0.0", 43 | "angular-formly": ">=7.3.0", 44 | "api-check": ">=7.0.0" 45 | }, 46 | "devDependencies": { 47 | "angular-mocks": "^1.5.0", 48 | "argv-set-env": "^1.0.0", 49 | "babel": "^6.3.26", 50 | "babel-core": "^6.4.5", 51 | "babel-eslint": "^5.0.0--beta", 52 | "babel-loader": "^6.2.1", 53 | "babel-preset-es2015": "^6.3.13", 54 | "codacy-coverage": "^1.1.3", 55 | "codecov.io": "^0.1.6", 56 | "coveralls": "^2.11.4", 57 | "eslint": "^1.10.3", 58 | "eslint-config-airbnb": "^2.0.0", 59 | "html-loader": "^0.4.0", 60 | "isparta": "^4.0.0", 61 | "isparta-loader": "2.0.0", 62 | "jasmine-core": "^2.3.4", 63 | "jquery": "^2.1.4", 64 | "karma": "^0.13.19", 65 | "karma-babel-preprocessor": "^6.0.1", 66 | "karma-chrome-launcher": "^0.2.1", 67 | "karma-coverage": "^0.5.3", 68 | "karma-firefox-launcher": "^0.1.7", 69 | "karma-jasmine": "^0.3.6", 70 | "karma-jasmine-html-reporter": "^0.2.0", 71 | "karma-sourcemap-loader": "^0.3.6", 72 | "karma-webpack": "^1.7.0", 73 | "lodash": "^3.10.1", 74 | "mys-common-tools": "latest", 75 | "webpack": "^1.12.9" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/types/textarea-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - textarea type', () => { 5 | // 6 | // vars 7 | // 8 | 9 | let $compile; 10 | let $rootScope; 11 | let $scope; 12 | let form; 13 | let element; 14 | let elementScope; 15 | let field; 16 | const theme = 'custom'; 17 | 18 | // 19 | // helpers 20 | // 21 | 22 | function compile(options) { 23 | $scope = $rootScope.$new(); 24 | $scope.fields = [angular.merge({}, { 25 | key: 'testField', 26 | type: 'textarea', 27 | templateOptions: { 28 | theme, 29 | label: 'test field', 30 | disabled: true, 31 | rows: 5, 32 | cols: 6 33 | } 34 | }, options)]; 35 | 36 | form = $compile(testUtils.getFormTemplate())($scope); 37 | $scope.$digest(); 38 | field = $scope.fields[0]; 39 | element = form.find('[ng-model]'); 40 | elementScope = element.scope(); 41 | } 42 | 43 | // 44 | // tests 45 | // 46 | 47 | beforeEach(() => { 48 | window.module('formlyMaterial'); 49 | 50 | inject((_$compile_, _$rootScope_) => { 51 | $compile = _$compile_; 52 | $rootScope = _$rootScope_; 53 | }); 54 | 55 | compile(); 56 | }); 57 | 58 | it('should be input element', () => { 59 | expect(element[0].nodeName).toBe('TEXTAREA'); 60 | }); 61 | 62 | it('should have messages wrapper', () => { 63 | expect(form.find('[ng-messages]').length).toBe(1); 64 | }); 65 | 66 | it('should have label wrapper', () => { 67 | const label = form.find('label'); 68 | 69 | expect(label.length).toBe(1); 70 | expect(label.html()).toContain(field.templateOptions.label); 71 | }); 72 | 73 | it('should have proper theme when defined', () => { 74 | expect(element.parent().is(`.md-${theme}-theme`)).toBe(true); 75 | }); 76 | 77 | it('should not auto grow', () => { 78 | expect(element.attr('md-no-autogrow')).toBeUndefined(); 79 | }); 80 | 81 | it('should be able to disable autogrowing', () => { 82 | compile({ 83 | templateOptions: { 84 | grow: false 85 | } 86 | }); 87 | expect(element.attr('md-no-autogrow')).toBeDefined(); 88 | }); 89 | 90 | it('should have inputContainer wrapper', () => { 91 | expect(form.find('md-input-container').length).toBe(1); 92 | }); 93 | 94 | it('should be able to set rows', () => { 95 | expect(parseInt(element.attr('rows'), 10)).toEqual(field.templateOptions.rows); 96 | }); 97 | 98 | it('should be able to set cols', () => { 99 | expect(parseInt(element.attr('cols'), 10)).toEqual(field.templateOptions.cols); 100 | }); 101 | 102 | it('should be able to be disabled', () => { 103 | expect(element.attr('ng-disabled')).toBe(`options.templateOptions['disabled']`); 104 | expect(elementScope.options.templateOptions.disabled).toBe(true); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /demo/client/data/codepen.js: -------------------------------------------------------------------------------- 1 | const { SetModule, Service, Inject } = angular2now; 2 | 3 | SetModule('demo'); 4 | 5 | @Service({ 6 | name: 'Codepen' 7 | }) 8 | @Inject(['$document']) 9 | class Codepen { 10 | constructor($document, Examples) { 11 | this.$document = $document; 12 | this.Examples = Examples; 13 | this.address = 'http://codepen.io/pen/define/'; 14 | } 15 | 16 | open(exampleId) { 17 | // const data = codepenDataAdapter.translate(demo, $demoAngularScripts.all()); 18 | // const example = this.Examples.get(exampleId); 19 | const data = { 20 | css_external: [ 21 | '//npmcdn.com/angular-material@1.0.0/angular-material.min.css' 22 | ].join(';'), 23 | js_external: [ 24 | '//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js', 25 | '//npmcdn.com/angular-animate@1.4.8/angular-animate.min.js', 26 | '//npmcdn.com/angular-aria@1.4.8/angular-aria.min.js', 27 | '//npmcdn.com/angular-material@latest/angular-material.min.js', 28 | '//npmcdn.com/angular-messages@1.4.8/angular-messages.min.js', 29 | '//npmcdn.com/api-check@latest/dist/api-check.js', 30 | '//npmcdn.com/angular-formly@latest/dist/formly.js', 31 | '//npmcdn.com/angular-formly-material@latest/dist/formly-material.js' 32 | ].join(';'), 33 | html: this._getHTML().trim() 34 | }; 35 | const form = this._buildForm(data); 36 | 37 | this.$document.find('body').append(form); 38 | form[0].submit(); 39 | form.remove(); 40 | } 41 | 42 | _buildForm(data) { 43 | const form = angular.element( 44 | `
    ` 45 | ); 46 | const input = ``; 47 | form.append(input); 48 | return form; 49 | } 50 | 51 | _escapeJsonQuotes(json) { 52 | return JSON.stringify(json) 53 | .replace(/'/g, '&apos;') 54 | .replace(/"/g, '&quot;'); 55 | } 56 | 57 | _getHTML() { 58 | return ` 59 |
    60 |
    61 | 62 | Submit 63 | 64 |
    65 |
    66 | `; 67 | } 68 | 69 | _getJS() { 70 | return ` 71 | (function () { 72 | 'use strict'; 73 | angular 74 | .module('demo', ['formlyMaterial']) 75 | .controller('DemoCtrl', DemoCtrl); 76 | 77 | function DemoCtrl () { 78 | var self = this; 79 | 80 | self.fields = ::field; 81 | self.model = ::model; 82 | self.form = ::form; 83 | self.options = ::options; 84 | self.submit = function() {} 85 | } 86 | })(); 87 | `; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/types/chips-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - chips type', () => { 5 | // 6 | // vars 7 | // 8 | let $compile; 9 | let $rootScope; 10 | let $scope; 11 | let element; 12 | let field; 13 | let fieldScope; 14 | const theme = 'custom'; 15 | 16 | function onAdd(/* $modelValue, $inputValue, scope, $event */) { 17 | return true; 18 | } 19 | 20 | function onRemove(/* $modelValue, $inputValue, scope, $event */) { 21 | return true; 22 | } 23 | 24 | function onSelect(/* $modelValue, $inputValue, scope, $event */) { 25 | return true; 26 | } 27 | // 28 | // helpers 29 | // 30 | 31 | function compile(options) { 32 | $scope = $rootScope.$new(); 33 | $scope.fields = [angular.merge({}, { 34 | key: 'testField', 35 | type: 'chips', 36 | templateOptions: { 37 | onAdd, 38 | onRemove, 39 | onSelect, 40 | theme, 41 | label: 'test field', 42 | placeholder: '+tags', 43 | secondaryPlaceholder: 'Add tag', 44 | deleteButtonLabel: 'Remove', 45 | deleteHint: 'Remove tag', 46 | disabled: true 47 | } 48 | }, options)]; 49 | 50 | const form = $compile(testUtils.getFormTemplate())($scope); 51 | 52 | $scope.$digest(); 53 | field = $scope.fields[0]; 54 | element = form.find('[ng-model]'); 55 | fieldScope = angular.element(element).scope(); 56 | } 57 | 58 | // 59 | // tests 60 | // 61 | 62 | beforeEach(() => { 63 | window.module('formlyMaterial'); 64 | 65 | inject((_$compile_, _$rootScope_) => { 66 | $compile = _$compile_; 67 | $rootScope = _$rootScope_; 68 | }); 69 | 70 | compile(); 71 | }); 72 | 73 | it('should be md-chips element', () => { 74 | expect(element[0].nodeName).toBe('MD-CHIPS'); 75 | }); 76 | 77 | it('should have placeholder', () => { 78 | expect(element.attr('placeholder')).toBe(field.templateOptions.placeholder); 79 | }); 80 | 81 | it('should have proper theme when defined', () => { 82 | expect(element.is(`.md-${theme}-theme`)).toBe(true); 83 | }); 84 | 85 | it('should have secondary placeholder', () => { 86 | expect(element.attr('secondary-placeholder')).toBe(field.templateOptions.secondaryPlaceholder); 87 | }); 88 | 89 | it('should have delete button label', () => { 90 | expect(element.attr('delete-button-label')).toBe(field.templateOptions.deleteButtonLabel); 91 | }); 92 | 93 | it('should have delete hint', () => { 94 | expect(element.attr('delete-hint')).toBe(field.templateOptions.deleteHint); 95 | }); 96 | 97 | it('should have onAdd callback', () => { 98 | expect(element.attr('md-on-add')).toBe(`options.templateOptions['onAdd'](model[options.key], options, this, $event)`); 99 | expect(fieldScope.options.templateOptions.onAdd).toBe(onAdd); 100 | }); 101 | 102 | it('should have onRemove callback', () => { 103 | expect(element.attr('md-on-remove')).toBe(`options.templateOptions['onRemove'](model[options.key], options, this, $event)`); 104 | expect(fieldScope.options.templateOptions.onRemove).toBe(onRemove); 105 | }); 106 | 107 | it('should have onSelect callback', () => { 108 | expect(element.attr('md-on-select')).toBe(`options.templateOptions['onSelect'](model[options.key], options, this, $event)`); 109 | expect(fieldScope.options.templateOptions.onSelect).toBe(onSelect); 110 | }); 111 | 112 | it('should be disabled and have readonly attribute', () => { 113 | expect(element[0].attributes.readonly.value).toBe('to.disabled'); 114 | expect(fieldScope.options.templateOptions.disabled).toBe(true); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FormlyMaterial 2 | 3 | Material Design Templates for [Angular-Formly](http://angular-formly.com). Modern & flexible forms configured easily in a JSON object. 4 | 5 | --- 6 | 7 | **Chat** 8 | 9 | [![Join the chat at https://gitter.im/formly-js/angular-formly-templates-material](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/formly-js/angular-formly-templates-material?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 10 | 11 | **Versions** 12 | 13 | [![GitHub version](https://badge.fury.io/gh/formly-js%2Fangular-formly-templates-material.svg)](https://badge.fury.io/gh/formly-js%2Fangular-formly-templates-material) 14 | [![npm version](https://badge.fury.io/js/angular-formly-material.svg)](https://badge.fury.io/js/angular-formly-material) 15 | [![Bower version](https://badge.fury.io/bo/angular-formly-material.svg)](https://badge.fury.io/bo/angular-formly-material) 16 | 17 | **Code** 18 | 19 | [![Build Status](https://travis-ci.org/formly-js/angular-formly-templates-material.svg)](https://travis-ci.org/formly-js/angular-formly-templates-material) 20 | [![Coverage Status](https://coveralls.io/repos/formly-js/angular-formly-templates-material/badge.svg?branch=master&service=github)](https://coveralls.io/github/formly-js/angular-formly-templates-material?branch=master) 21 | [![Codacy Badge](https://api.codacy.com/project/badge/grade/a2cd4c7c2d74467281e309a65be49e8f)](https://www.codacy.com/app/mys-sterowiec/angular-formly-templates-material) 22 | 23 | --- 24 | 25 | ## Table of contents 26 | 27 | - [Install](#install) 28 | - [Getting Started](#getting-started) 29 | - [Demo](#demo) 30 | - [Requests?](#requests) 31 | - [Requirements](#requirements) 32 | - [Components](#components) 33 | - [Fields](#fields) 34 | - [Wrappers](#wrappers) 35 | - [Common settings](#common-settings) 36 | - [Theme](#theme-string) 37 | 38 | --- 39 | 40 | ## Install 41 | 42 | ``` 43 | npm install angular-formly-material 44 | ``` 45 | 46 | ``` 47 | bower install angular-formly-material 48 | ``` 49 | 50 | ``` 51 | meteor add formly:angular-formly-templates-material 52 | ``` 53 | 54 | ## Getting Started 55 | 56 | 1. Add package using one of methods above 57 | 2. Add the following dependencies to your AngularJS module: 58 | 59 | ```javascript 60 | angular.module('myAppName', [ 61 | 'formlyMaterial' 62 | ]) 63 | ``` 64 | 65 | ## Demo 66 | 67 | Visit [formly-material.meteor.com](http://formly-material.meteor.com/demo/input) 68 | 69 | ## Requests? 70 | 71 | Maybe you need some new feature? Go here: 72 | 73 | https://github.com/formly-js/angular-formly-templates-material/issues/4 74 | 75 | ## Requirements 76 | 77 | - angular ~ 1.4.0 78 | - angular-messages ~ 1.4.0 79 | - angular-material ~ 1.0.0 80 | - angular-formly ~ 7.3.0 81 | 82 | ## Components 83 | 84 | Any requests? Add issue! 85 | 86 | ### Fields 87 | 88 | - [checkbox](docs/types/checkbox.md) 89 | - [chips](docs/types/chips.md) 90 | - [datepicker](docs/types/datepicker.md) 91 | - [input](docs/types/input.md) 92 | - [radio](docs/types/radio.md) 93 | - [select](docs/types/select.md) 94 | - [slider](docs/types/slider.md) 95 | - [switch](docs/types/switch.md) 96 | - [textarea](docs/types/textarea.md) 97 | 98 | ### Wrappers 99 | 100 | - [divider](docs/wrappers/divider.md) 101 | - [inputContainer](docs/wrappers/input-container.md) 102 | - [label](docs/wrappers/label.md) 103 | - [messages](docs/wrappers/messages.md) 104 | 105 | ### Common settings 106 | 107 | #### templateOptions.label *: string* 108 | 109 | #### templateOptions.theme *: string* 110 | 111 | Value of md-theme used on field 112 | 113 | #### templateOptions.disabled _: boolean_ 114 | 115 | #### templateOptions.className _: expression_ 116 | 117 | equivalent to ng-class on ng-model 118 | 119 | --- 120 | 121 | Requests (?). Post an issue. 122 | -------------------------------------------------------------------------------- /tests/types/radio-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - radio type', () => { 5 | // 6 | // vars 7 | // 8 | 9 | let $compile; 10 | let $rootScope; 11 | let $scope; 12 | let form; 13 | let element; 14 | let optionsElements; 15 | let field; 16 | const theme = 'custom'; 17 | 18 | // 19 | // helpers 20 | // 21 | 22 | function compile(options) { 23 | $scope = $rootScope.$new(); 24 | $scope.fields = [angular.merge({}, { 25 | key: 'testField', 26 | type: 'radio', 27 | templateOptions: { 28 | theme, 29 | label: 'test field', 30 | options: [{ 31 | name: 'first', 32 | nameUp: 'FIRST', 33 | value: 'f', 34 | valueUp: 'F' 35 | }, { 36 | name: 'second', 37 | nameUp: 'SECOND', 38 | value: 's', 39 | valueUp: 'S' 40 | }] 41 | } 42 | }, options)]; 43 | 44 | form = $compile(testUtils.getFormTemplate())($scope); 45 | $scope.$digest(); 46 | field = $scope.fields[0]; 47 | element = form.find('[ng-model]'); 48 | optionsElements = element.find('md-radio-button'); 49 | } 50 | 51 | // 52 | // tests 53 | // 54 | 55 | beforeEach(() => { 56 | window.module('formlyMaterial'); 57 | 58 | inject((_$compile_, _$rootScope_) => { 59 | $compile = _$compile_; 60 | $rootScope = _$rootScope_; 61 | }); 62 | }); 63 | 64 | it('should be md-radio-group element', () => { 65 | compile(); 66 | expect(element[0].nodeName).toBe('MD-RADIO-GROUP'); 67 | }); 68 | 69 | it('should have proper theme when defined', () => { 70 | compile(); 71 | expect(element.is(`.md-${theme}-theme`)).toBe(true); 72 | }); 73 | 74 | it('should have options with default properties for name and value', () => { 75 | compile(); 76 | expect(optionsElements.length).toBe(field.templateOptions.options.length); 77 | 78 | field.templateOptions.options.forEach((option, key) => { 79 | const el = angular.element(optionsElements[key]); 80 | 81 | expect(el.attr('value')).toBe(option.value); 82 | expect(el.find('.md-label > span').html()).toContain(option.name); 83 | }); 84 | }); 85 | 86 | it('should handle valueProp', () => { 87 | compile({ 88 | templateOptions: { 89 | valueProp: 'valueUp' 90 | } 91 | }); 92 | expect(optionsElements.length).toBe(field.templateOptions.options.length); 93 | 94 | field.templateOptions.options.forEach((option, key) => { 95 | const el = angular.element(optionsElements[key]); 96 | 97 | expect(el.attr('value')).toBe(option.valueUp); 98 | expect(el.find('.md-label > span').html()).toContain(option.name); 99 | }); 100 | }); 101 | 102 | it('should handle labelProp', () => { 103 | compile({ 104 | templateOptions: { 105 | labelProp: 'nameUp' 106 | } 107 | }); 108 | expect(optionsElements.length).toBe(field.templateOptions.options.length); 109 | 110 | field.templateOptions.options.forEach((option, key) => { 111 | const el = angular.element(optionsElements[key]); 112 | 113 | expect(el.attr('value')).toBe(option.value); 114 | expect(el.find('.md-label > span').html()).toContain(option.nameUp); 115 | }); 116 | }); 117 | 118 | it('should has disabled options', () => { 119 | compile({ 120 | templateOptions: { 121 | disabled: true 122 | } 123 | }); 124 | 125 | expect(optionsElements.length).toBe(field.templateOptions.options.length); 126 | 127 | field.templateOptions.options.forEach((option, key) => { 128 | const el = angular.element(optionsElements[key]); 129 | 130 | expect(el.attr('ng-disabled')).toBe('to.disabled'); 131 | expect(el.scope().to.disabled).toBe(true); 132 | }); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /tests/types/input-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - input type', () => { 5 | // 6 | // vars 7 | // 8 | 9 | let $compile; 10 | let $rootScope; 11 | let $scope; 12 | let form; 13 | let element; 14 | let field; 15 | const theme = 'custom'; 16 | 17 | // 18 | // helpers 19 | // 20 | 21 | function compile(options) { 22 | $scope = $rootScope.$new(); 23 | $scope.fields = [angular.merge({}, { 24 | key: 'testField', 25 | type: 'input', 26 | templateOptions: { 27 | theme, 28 | disabled: true, 29 | type: 'email', 30 | label: 'test field' 31 | } 32 | }, options)]; 33 | 34 | form = $compile(testUtils.getFormTemplate())($scope); 35 | $scope.$digest(); 36 | field = $scope.fields[0]; 37 | element = form.find('[ng-model]'); 38 | } 39 | 40 | // 41 | // tests 42 | // 43 | 44 | beforeEach(() => { 45 | window.module('formlyMaterial'); 46 | 47 | inject((_$compile_, _$rootScope_) => { 48 | $compile = _$compile_; 49 | $rootScope = _$rootScope_; 50 | }); 51 | }); 52 | 53 | describe('basics', () => { 54 | beforeEach(() => { 55 | compile(); 56 | }); 57 | 58 | it('should be input element', () => { 59 | expect(element[0].nodeName).toBe('INPUT'); 60 | }); 61 | 62 | it('should have proper type attribute', () => { 63 | expect(element.attr('type')).toBe(field.templateOptions.type); 64 | }); 65 | 66 | it('should have proper theme when defined', () => { 67 | expect(element.parent().is(`.md-${theme}-theme`)).toBe(true); 68 | }); 69 | 70 | it('should have messages wrapper', () => { 71 | expect(form.find('[ng-messages]').length).toBe(1); 72 | }); 73 | 74 | it('should have label wrapper', () => { 75 | const label = form.find('label'); 76 | 77 | expect(label.length).toBe(1); 78 | expect(label.html()).toContain(field.templateOptions.label); 79 | }); 80 | 81 | it('should have inputContainer wrapper', () => { 82 | expect(form.find('md-input-container').length).toBe(1); 83 | }); 84 | }); 85 | 86 | it('should be disabled', () => { 87 | const scope = element.scope(); 88 | 89 | expect(element.attr('ng-disabled')).toBe(`options.templateOptions['disabled']`); 90 | expect(scope.options.templateOptions.disabled).toBe(true); 91 | }); 92 | 93 | describe('pattern', () => { 94 | it('should accept string', () => { 95 | const pattern = '[a-z]+'; 96 | 97 | compile({ 98 | templateOptions: { 99 | pattern 100 | } 101 | }); 102 | 103 | expect(element.attr('ng-pattern')).toEqual(field.templateOptions.pattern); 104 | }); 105 | 106 | it('should accept RegExp', () => { 107 | const pattern = /[a-z]+/; 108 | 109 | compile({ 110 | templateOptions: { 111 | pattern 112 | } 113 | }); 114 | 115 | expect(element.attr('ng-pattern')).toEqual(`${field.templateOptions.pattern}`); 116 | }); 117 | }); 118 | 119 | describe('number type specific', () => { 120 | describe('step attribute', () => { 121 | // XXX BE instead of NOT BE because `step` attr is now fully supported 122 | // angular-formly#8042d2a 123 | it('should BE available on non number type', () => { 124 | compile({ 125 | templateOptions: { 126 | type: 'text', 127 | step: 2 128 | } 129 | }); 130 | 131 | expect(parseInt(element.attr('step'), 10)).toBe(field.templateOptions.step); 132 | }); 133 | 134 | it('should be available on number type', () => { 135 | compile({ 136 | templateOptions: { 137 | type: 'number', 138 | step: 2 139 | } 140 | }); 141 | 142 | expect(parseInt(element.attr('step'), 10)).toEqual(field.templateOptions.step); 143 | }); 144 | }); 145 | 146 | describe('min attribute', () => { 147 | it('should be available on number type', () => { 148 | compile({ 149 | templateOptions: { 150 | type: 'number', 151 | min: 2 152 | } 153 | }); 154 | 155 | expect(parseInt(element.attr('min'), 10)).toEqual(field.templateOptions.min); 156 | }); 157 | }); 158 | 159 | describe('max attribute', () => { 160 | it('should be available on number type', () => { 161 | compile({ 162 | templateOptions: { 163 | type: 'number', 164 | max: 2 165 | } 166 | }); 167 | 168 | expect(parseInt(element.attr('max'), 10)).toEqual(field.templateOptions.max); 169 | }); 170 | }); 171 | }); 172 | }); 173 | -------------------------------------------------------------------------------- /tests/types/select-spec.js: -------------------------------------------------------------------------------- 1 | import testUtils from './../test-utils'; 2 | import angular from 'angular'; 3 | 4 | describe('formlyMaterial - select type', () => { 5 | // 6 | // vars 7 | // 8 | 9 | let $compile; 10 | let $rootScope; 11 | let $scope; 12 | let $document; 13 | let $material; 14 | let form; 15 | let element; 16 | let elementScope; 17 | let field; 18 | const theme = 'custom'; 19 | 20 | function onClose(/* $modelValue, $inputValue, scope, $event */) { 21 | return true; 22 | } 23 | 24 | function onOpen(/* $modelValue, $inputValue, scope, $event */) { 25 | return true; 26 | } 27 | // 28 | // helpers 29 | // 30 | 31 | function compile(options) { 32 | $scope = $rootScope.$new(); 33 | $scope.fields = [angular.merge({}, { 34 | key: 'testField', 35 | type: 'select', 36 | templateOptions: { 37 | theme, 38 | label: 'test field', 39 | multiple: true, 40 | onClose, 41 | onOpen, 42 | options: [{ 43 | name: 'first', 44 | nameUp: 'FIRST', 45 | value: 'f', 46 | valueUp: 'F' 47 | }, { 48 | name: 'second', 49 | nameUp: 'SECOND', 50 | value: 's', 51 | valueUp: 'S' 52 | }] 53 | } 54 | }, options)]; 55 | 56 | form = $compile(testUtils.getFormTemplate())($scope); 57 | $scope.$digest(); 58 | field = $scope.fields[0]; 59 | element = form.find('[ng-model]'); 60 | elementScope = element.scope(); 61 | } 62 | 63 | function waitForSelectOpen() { 64 | $material.flushInterimElement(); 65 | } 66 | 67 | function openSelect() { 68 | element.triggerHandler('click'); 69 | } 70 | 71 | function closeSelect() { 72 | $document.find('md-select-menu').parent().remove(); 73 | $document.find('body').attr('style', ''); 74 | $document.find('md-backdrop').remove(); 75 | } 76 | 77 | function selectOptions() { 78 | openSelect(); 79 | waitForSelectOpen(); 80 | const selectMenu = $document.find('md-select-menu'); 81 | 82 | selectMenu.hide(); 83 | $document.find('.md-scroll-mask').remove(); 84 | return selectMenu.find('md-option'); 85 | } 86 | 87 | // 88 | // tests 89 | // 90 | 91 | beforeEach(() => { 92 | window.angular.module('testMod', ['formlyMaterial', 'ngMaterial-mock']); 93 | window.module('testMod'); 94 | 95 | inject((_$compile_, _$rootScope_, _$document_, _$material_) => { 96 | $compile = _$compile_; 97 | $rootScope = _$rootScope_; 98 | $document = _$document_; 99 | $material = _$material_; 100 | }); 101 | }); 102 | 103 | it('should be md-select element', () => { 104 | compile(); 105 | expect(element[0].nodeName).toBe('MD-SELECT'); 106 | }); 107 | 108 | it('should be able to pick multiple options', () => { 109 | compile(); 110 | expect(element.attr('multiple')).toBeDefined(); 111 | }); 112 | 113 | it('should have proper theme when defined', () => { 114 | compile(); 115 | expect(element.is(`.md-${theme}-theme`)).toBe(true); 116 | }); 117 | 118 | it('should not add multiple directive when does not equal true', () => { 119 | compile({ 120 | templateOptions: { 121 | multiple: false 122 | } 123 | }); 124 | expect(element.attr('multiple')).toBeUndefined(); 125 | }); 126 | 127 | it('should be able to bind md-on-close', () => { 128 | compile(); 129 | expect(element.attr('md-on-close')).toBe(`options.templateOptions['onClose'](model[options.key], options, this, $event)`); 130 | expect(elementScope.options.templateOptions.onClose).toBe(onClose); 131 | }); 132 | 133 | it('should be able to bind md-on-open', () => { 134 | compile(); 135 | expect(element.attr('md-on-open')).toBe(`options.templateOptions['onOpen'](model[options.key], options, this, $event)`); 136 | expect(elementScope.options.templateOptions.onOpen).toBe(onOpen); 137 | }); 138 | 139 | it('should be able to be disabled', () => { 140 | compile({ 141 | templateOptions: { 142 | disabled: true 143 | } 144 | }); 145 | 146 | expect(element.attr('ng-disabled')).toBe(`options.templateOptions['disabled']`); 147 | expect(elementScope.options.templateOptions.disabled).toBe(true); 148 | }); 149 | 150 | describe('check options', () => { 151 | afterEach(() => { 152 | closeSelect(); 153 | }); 154 | 155 | it('should have options with default properties for name and value', () => { 156 | compile(); 157 | const optionsEl = selectOptions(); 158 | 159 | expect(optionsEl.length).toBe(field.templateOptions.options.length); 160 | field.templateOptions.options.forEach((option, key) => { 161 | const el = angular.element(optionsEl[key]); 162 | 163 | expect(el.attr('value')).toBe(option.value); 164 | expect(el.find('.md-text').html()).toContain(option.name); 165 | }); 166 | }); 167 | 168 | it('should have options with custom properties for name and value', () => { 169 | compile({ 170 | templateOptions: { 171 | labelProp: 'nameUp', 172 | valueProp: 'valueUp' 173 | } 174 | }); 175 | const optionsEl = selectOptions(); 176 | 177 | expect(optionsEl.length).toBe(field.templateOptions.options.length); 178 | field.templateOptions.options.forEach((option, key) => { 179 | const el = angular.element(optionsEl[key]); 180 | 181 | expect(el.attr('value')).toBe(option.valueUp); 182 | expect(el.find('.md-text').html()).toContain(option.nameUp); 183 | }); 184 | }); 185 | }); 186 | }); 187 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [0.14.3](https://github.com/formly-js/angular-formly-templates-material/compare/v0.14.2...v0.14.3) - 2016-06-23 6 | 7 | ### Added 8 | 9 | - Allow to add step attribute on every field (6bc6989) 10 | 11 | ### Fixed 12 | 13 | - (chips, select) Bind callback functions by using statement (#45) 14 | 15 | ## [0.14.2](https://github.com/formly-js/angular-formly-templates-material/compare/v0.14.1...v0.14.2) - 2016-05-30 16 | 17 | ### Added 18 | 19 | - Support for higher versions of angular (#34) 20 | 21 | ### Fixed 22 | 23 | - (label) missing left padding (#29) 24 | - assumption of existence of `templateOptions` (#32) 25 | 26 | ## [0.14.1](https://github.com/formly-js/angular-formly-templates-material/compare/v0.14.0...v0.14.1) - 2016-01-27 27 | 28 | ### Added 29 | 30 | - Demo (formly-material.meteor.com) 31 | 32 | ### Fixed 33 | 34 | - #22 35 | - #23 36 | 37 | ## [0.14.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.13.0...v0.14.0) 38 | 39 | ### Added 40 | 41 | - (chips) support for **templateOptions.disabled** (#14) (eea2fcf, f2d2e40) 42 | - (radio) support for **templateOptions.disabled** (#13) (e6104ea, c04f63d) 43 | - (checkbox) support for **templateOptions.disabled** (bdbe651, aec4e60) 44 | - (datepicker) support for **templateOptions.disabled** (41b99d6, 7ee864a) 45 | - (input) support for **templateOptions.disabled** (2b528c3, 18c819f) 46 | - (select) support for **templateOptions.disabled** (2bb10e0, 9f76f0e) 47 | - (slider) support for **templateOptions.disabled** (27d1d2a, f6b5890) 48 | - (switch) support for **templateOptions.disabled** (7c1865c, 178ac76) 49 | - (textarea) support for **templateOptions.disabled** (858f9fa, 8a246f5) 50 | - **templateOptions.className** option (equivalent to ng-class) (#15, #16) (718c172, d656b4c) 51 | 52 | ## [0.13.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.12.0...v0.13.0) 53 | 54 | ### Added 55 | 56 | - (input) support for **ng-pattern** as **templateOptions.pattern** (#10) 57 | - (chips) has now **label** wrapper 58 | - (datepicker) has now **label** wrapper 59 | - (radio) has now **label** wrapper (#9) 60 | - (slider) has now **label** wrapper 61 | 62 | ## [0.12.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.11.0...v0.12.0) - 2015-12-30 63 | 64 | ### Added 65 | 66 | - (messages) support for `errorExistsAndShouldBeVisible` (#7) 67 | - (input) support for **min** attribute of 'number' type (#6) 68 | - (input) support for **max** attribute of 'number' type (#6) 69 | - (input) support for **step** attribute of 'number' type (#6) 70 | 71 | ## [0.11.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.10.0...v0.11.0) - 2015-12-18 72 | 73 | ### Added 74 | 75 | - (chips) onAdd callback (equivalent to md-on-add) 76 | - (chips) onRemove callback (equivalent to md-on-remove) 77 | - (chips) onSelect callback (equivalent to md-on-select) 78 | 79 | ## [0.10.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.9.0...v0.10.0) - 2015-12-17 80 | 81 | ### Added 82 | 83 | - (textarea) **grow** option (equivalent to md-no-autogrow) 84 | - **divider** wrapper ([see documentation](docs/wrappers/divider.md)\) 85 | 86 | ## [0.9.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.8.0...v0.9.0) - 2015-12-17 87 | 88 | ### Added 89 | 90 | - (select) **onClose** and **onOpen** options (equivalent to md-on-close and md-on-open) 91 | 92 | ## [0.8.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.7.3...v0.8.0) - 2015-12-16 93 | 94 | ### Changed 95 | 96 | - BREAKING CHANGE: requires now **angular-material v1.0.0** 97 | - BREAKING CHANGE: for meteor package uses now **formly:angular-formly** instead of wieldo:angular-formly 98 | 99 | ## [0.7.3](https://github.com/formly-js/angular-formly-templates-material/compare/v0.7.2...v0.7.3) - 2015-12-14 100 | 101 | ### Changed 102 | 103 | - Package name in AtmosphereJS (formly:angular-formly-templates-material) 104 | 105 | ## [0.7.2](https://github.com/formly-js/angular-formly-templates-material/compare/v0.7.1...v0.7.2) - 2015-12-08 106 | 107 | ### Added 108 | 109 | - Documentation for all fields and wrappers 110 | - Banner with version in dist files 111 | 112 | ### Changed 113 | 114 | - Links to repository after transfer from wieldo to formly-js 115 | 116 | ## [0.7.1](https://github.com/formly-js/angular-formly-templates-material/compare/v0.7.0...v0.7.1) - 2015-12-01 117 | 118 | ### Changed 119 | 120 | - use bound for datepicker's minDate, maxDate and filterDate instead of template manipulator 121 | - remove bound from slider's min, max and step attributes 122 | 123 | ## [0.7.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.6.0...v0.7.0) - 2015-11-29 124 | 125 | ### Added 126 | 127 | - support for npm and bower (angular-formly-material). 128 | 129 | ### Changed 130 | 131 | - Unit testing using karma instead of velocity (with coverage reports) 132 | 133 | ## [0.6.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.5.2...v0.6.0) - 2015-11-27 134 | 135 | ### Added 136 | 137 | - compatibility with Meteor releases lower then 1.2 138 | 139 | ## [0.5.2](https://github.com/formly-js/angular-formly-templates-material/compare/v0.5.1...v0.5.2) - 2015-11-19 140 | 141 | ### Fixed 142 | 143 | - missing md-theme attribute when using templateOptions.theme 144 | 145 | ### Added 146 | 147 | - Tests of all currently available functionality 148 | 149 | ### Removed 150 | 151 | - deprecated **mdInputContainer** wrapper. Use **inputContainer** 152 | - deprecated **mdMessages** wrapper. Use **messages** 153 | 154 | ## [0.5.1](https://github.com/formly-js/angular-formly-templates-material/compare/v0.5.0...v0.5.1) - 2015-11-18 155 | 156 | ### Changed 157 | 158 | - Refactor all files to use Strict Dependency Injection 159 | 160 | ## [0.5.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.4.0...v0.5.0) - 2015-11-18 161 | 162 | ### Added 163 | 164 | - multiple support for select field 165 | - slider field with min, max, step and discrete options 166 | 167 | ## [0.4.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.3.0...v0.4.0) - 2015-11-17 168 | 169 | ### Added 170 | 171 | - **md-chip** with placeholder, secondary-placeholder, delete-button-label and delete-hint 172 | 173 | ### Deprecated 174 | 175 | - **mdInputContainer** wrapper. Use **inputContainer** 176 | - **mdMessages** wrapper. Use **messages** 177 | 178 | ## [0.3.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.2.0...v0.3.0) - 2015-11-17 179 | 180 | ### Added 181 | 182 | - datepicker with date range and filtering *(currently in angular-material 1.0_RC4)* 183 | - Tests of formlyMaterial provider 184 | 185 | ## [0.2.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.1.0...v0.2.0) - 2015-11-17 186 | 187 | ### Added 188 | 189 | - Support for textarea with cols and rows 190 | - ApiCheck for label, radio and select 191 | - Support for md-theme 192 | 193 | ## [0.1.0](https://github.com/formly-js/angular-formly-templates-material/compare/v0.0.4...v0.1.0) - 2015-11-17 194 | 195 | ### Added 196 | 197 | - Support for md-select with valueProp and labelProp 198 | - Support for md-radio with valueProp and labelProp options 199 | 200 | ## [0.0.4](https://github.com/formly-js/angular-formly-templates-material/compare/v0.0.3...v0.0.4) - 2015-11-13 201 | 202 | ### Removed 203 | 204 | - es5-shim package 205 | - standard-minifier package 206 | 207 | ## [0.0.3](https://github.com/formly-js/angular-formly-templates-material/compare/v0.0.2...v0.0.3) - 2015-11-10 208 | 209 | ### Fixed 210 | 211 | - Invalid components templateUrl 212 | 213 | ### Changed 214 | 215 | - Package name: wieldo:angular-formly-templates-material 216 | 217 | ## [0.0.2](https://github.com/formly-js/angular-formly-templates-material/compare/v0.0.1...v0.0.2) - 2015-11-07 218 | 219 | ### Fixed 220 | 221 | - Add missing ngMessages 222 | 223 | ### Added 224 | 225 | - showError in Messages wrapper 226 | - md-checkbox 227 | - mdTheme to checkbox, switch 228 | - md-switch 229 | 230 | ## 0.0.1 - 2015-11-06 231 | -------------------------------------------------------------------------------- /dist/formly-material.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-formly-material v0.14.2 | MIT | built with ♥ by Kamil Kisiela */ 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("angular")):"function"==typeof define&&define.amd?define(["angular"],t):"object"==typeof exports?exports.ngFormlyMaterial=t(require("angular")):e.ngFormlyMaterial=t(e.angular)}(this,function(e){return function(e){function t(o){if(n[o])return n[o].exports;var a=n[o]={exports:{},id:o,loaded:!1};return e[o].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(1),l=o(a),r=n(2),i=o(r),u=n(5),d=o(u),s=n(14),p=o(s),f="formlyMaterial";l["default"].module(f,["ngMessages","ngMaterial","formly"]).config(["formlyConfigProvider",function(e){var t=[i["default"],d["default"],p["default"]];t.forEach(function(t){for(var n=0;n "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(9),l=o(a);t["default"]=function(e){e.setWrapper({template:l["default"],name:"label",apiCheck:function(e){return{templateOptions:{label:e.string}}}})}},function(e,t){e.exports=" "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(11),l=o(a);t["default"]=function(e){e.setWrapper({template:l["default"],name:"messages"})}},function(e,t){e.exports='
    {{message(fc.$viewValue, fc.$modelValue, this)}}
    '},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(13),l=o(a);t["default"]=function(e){e.setWrapper({template:l["default"],name:"divider",apiCheck:function(e){return{templateOptions:{divider:e.oneOf(["before","after"]).optional}}}})}},function(e,t){e.exports=" "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(15),l=o(a),r=n(17),i=o(r),u=n(19),d=o(u),s=n(21),p=o(s),f=n(23),c=o(f),m=n(25),b=o(m),g=n(27),v=o(g),_=n(29),y=o(_),h=n(31),M=o(h);t["default"]=[l["default"],i["default"],d["default"],p["default"],c["default"],b["default"],v["default"],y["default"],M["default"]]},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(16),l=o(a);t["default"]=function(e){e.setType({template:l["default"],name:"checkbox",defaultOptions:{ngModelAttrs:{disabled:{bound:"ng-disabled"}}},apiCheck:function(e){return{templateOptions:{disabled:e.bool.optional,theme:e.string.optional}}}})}},function(e,t){e.exports="
    {{to.label}}
    "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(18),l=o(a);t["default"]=function(e){e.setType({template:l["default"],name:"chips",wrapper:["label"],defaultOptions:{defaultValue:[],ngModelAttrs:{placeholder:{attribute:"placeholder"},secondaryPlaceholder:{attribute:"secondary-placeholder"},deleteButtonLabel:{attribute:"delete-button-label"},deleteHint:{attribute:"delete-hint"},onAdd:{bound:"md-on-add"},onRemove:{bound:"md-on-remove"},onSelect:{bound:"md-on-select"}}},apiCheck:function(e){return{templateOptions:{placeholder:e.string.optional,secondaryPlaceholder:e.string.optional,deleteButtonLabel:e.string.optional,deleteHint:e.string.optional,onAdd:e.func.optional,onRemove:e.func.optional,onSelect:e.func.optional,theme:e.string.optional}}}})}},function(e,t){e.exports=""},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(20),l=o(a);t["default"]=function(e){e.setType({template:l["default"],name:"datepicker",wrapper:["label","messages"],defaultOptions:{templateOptions:{disabled:!1},ngModelAttrs:{disabled:{bound:"ng-disabled"},placeholder:{attribute:"md-placeholder"},minDate:{bound:"md-min-date"},maxDate:{bound:"md-max-date"},filterDate:{bound:"md-date-filter"}}},apiCheck:function(e){return{templateOptions:{disabled:e.bool.optional,placeholder:e.string.optional,minDate:e.instanceOf(Date).optional,maxDate:e.instanceOf(Date).optional,filterDate:e.func.optional,theme:e.string.optional}}}})}},function(e,t){e.exports="
    "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(22),l=o(a),r=n(4);t["default"]=function(e){e.setType({template:l["default"],name:"input",wrapper:["label","messages","inputContainer"],defaultOptions:{templateOptions:{type:"text",disabled:!1},ngModelAttrs:{mdMaxlength:{bound:"md-maxlength"},disabled:{bound:"ng-disabled"},pattern:{bound:"ng-pattern"}}},apiCheck:function(e){return{templateOptions:{disabled:e.bool.optional,type:e.string,step:e.number.optional,pattern:e.oneOfType([e.string,e.instanceOf(RegExp)]).optional,theme:e.string.optional}}}}),e.extras.fieldTransform.push(function(e){return(0,r.ngModelAttrsTransformer)(e,function(e){return"input"===e.type&&e.templateOptions&&"number"===e.templateOptions.type},"step",{attribute:"step"})})}},function(e,t){e.exports=""},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(24),l=o(a);t["default"]=function(e){e.setType({template:l["default"],name:"radio",wrapper:["label"],apiCheck:function(e){return{templateOptions:{options:e.arrayOf(e.object),labelProp:e.string.optional,valueProp:e.string.optional,theme:e.string.optional}}}})}},function(e,t){e.exports=" {{option[to.labelProp || 'name']}} "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(26),l=o(a),r=n(4);t["default"]=function(e){e.setType({template:l["default"],name:"select",wrapper:["label","messages","inputContainer"],defaultOptions:{templateOptions:{disabled:!1},ngModelAttrs:{disabled:{bound:"ng-disabled"},onClose:{bound:"md-on-close"},onOpen:{bound:"md-on-open"}}},apiCheck:function(e){return{templateOptions:{disabled:e.bool.optional,options:e.arrayOf(e.object),multiple:e.bool.optional,labelProp:e.string.optional,valueProp:e.string.optional,onClose:e.func.optional,onOpen:e.func.optional,theme:e.string.optional}}}}),e.templateManipulators.preWrapper.push(function(e,t){var n=t.templateOptions||{};return n.multiple===!0?(0,r.ngModelAttrsManipulator)(e,t,"multiple"):e})}},function(e,t){e.exports=" {{ option[to.labelProp || 'name'] }} "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(28),l=o(a);t["default"]=function(e){e.setType({template:l["default"],name:"slider",wrapper:["label"],defaultOptions:{templateOptions:{disabled:!1},ngModelAttrs:{disabled:{bound:"ng-disabled"},min:{attribute:"min"},max:{attribute:"max"},step:{attribute:"step"},discrete:{bound:"md-discrete"}}},apiCheck:function(e){return{templateOptions:{disabled:e.bool.optional,min:e.number.optional,max:e.number.optional,step:e.number.optional,discrete:e.bool.optional,theme:e.string.optional}}}})}},function(e,t){e.exports=""},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(30),l=o(a);t["default"]=function(e){e.setType({template:l["default"],name:"switch",defaultOptions:{templateOptions:{disabled:!1},ngModelAttrs:{disabled:{bound:"ng-disabled"}}},apiCheck:function(e){return{templateOptions:{disabled:e.bool.optional,theme:e.string.optional}}}})}},function(e,t){e.exports=" {{to.label}} "},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(32),l=o(a),r=n(4);t["default"]=function(e){e.setType({template:l["default"],name:"textarea",wrapper:["label","messages","inputContainer"],defaultOptions:{ngModelAttrs:{disabled:{bound:"ng-disabled"},rows:{attribute:"rows"},cols:{attribute:"cols"}},templateOptions:{grow:!0}},apiCheck:function(e){return{templateOptions:{disabled:e.bool.optional,rows:e.number.optional,cols:e.number.optional,grow:e.bool.optional,theme:e.string.optional}}}}),e.extras.fieldTransform.push(function(e){return(0,r.ngModelAttrsTransformer)(e,function(e){return"textarea"===e.type&&e.templateOptions&&e.templateOptions.grow===!1},"grow",{attribute:"md-no-autogrow"})})}},function(e,t){e.exports=""}])}); 3 | //# sourceMappingURL=formly-material.min.js.map -------------------------------------------------------------------------------- /dist/formly-material.js: -------------------------------------------------------------------------------- 1 | /*! angular-formly-material v0.14.2 | MIT | built with ♥ by Kamil Kisiela */ 2 | (function webpackUniversalModuleDefinition(root, factory) { 3 | if(typeof exports === 'object' && typeof module === 'object') 4 | module.exports = factory(require("angular")); 5 | else if(typeof define === 'function' && define.amd) 6 | define(["angular"], factory); 7 | else if(typeof exports === 'object') 8 | exports["ngFormlyMaterial"] = factory(require("angular")); 9 | else 10 | root["ngFormlyMaterial"] = factory(root["angular"]); 11 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { 12 | return /******/ (function(modules) { // webpackBootstrap 13 | /******/ // The module cache 14 | /******/ var installedModules = {}; 15 | 16 | /******/ // The require function 17 | /******/ function __webpack_require__(moduleId) { 18 | 19 | /******/ // Check if module is in cache 20 | /******/ if(installedModules[moduleId]) 21 | /******/ return installedModules[moduleId].exports; 22 | 23 | /******/ // Create a new module (and put it into the cache) 24 | /******/ var module = installedModules[moduleId] = { 25 | /******/ exports: {}, 26 | /******/ id: moduleId, 27 | /******/ loaded: false 28 | /******/ }; 29 | 30 | /******/ // Execute the module function 31 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 32 | 33 | /******/ // Flag the module as loaded 34 | /******/ module.loaded = true; 35 | 36 | /******/ // Return the exports of the module 37 | /******/ return module.exports; 38 | /******/ } 39 | 40 | 41 | /******/ // expose the modules object (__webpack_modules__) 42 | /******/ __webpack_require__.m = modules; 43 | 44 | /******/ // expose the module cache 45 | /******/ __webpack_require__.c = installedModules; 46 | 47 | /******/ // __webpack_public_path__ 48 | /******/ __webpack_require__.p = ""; 49 | 50 | /******/ // Load entry module and return exports 51 | /******/ return __webpack_require__(0); 52 | /******/ }) 53 | /************************************************************************/ 54 | /******/ ([ 55 | /* 0 */ 56 | /***/ function(module, exports, __webpack_require__) { 57 | 58 | 'use strict'; 59 | 60 | Object.defineProperty(exports, "__esModule", { 61 | value: true 62 | }); 63 | 64 | var _angular = __webpack_require__(1); 65 | 66 | var _angular2 = _interopRequireDefault(_angular); 67 | 68 | var _runs = __webpack_require__(2); 69 | 70 | var _runs2 = _interopRequireDefault(_runs); 71 | 72 | var _wrappers = __webpack_require__(5); 73 | 74 | var _wrappers2 = _interopRequireDefault(_wrappers); 75 | 76 | var _types = __webpack_require__(14); 77 | 78 | var _types2 = _interopRequireDefault(_types); 79 | 80 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 81 | 82 | var ngModuleName = 'formlyMaterial'; 83 | 84 | _angular2.default.module(ngModuleName, ['ngMessages', 'ngMaterial', 'formly']).config(['formlyConfigProvider', function (formlyConfigProvider) { 85 | var configs = [_runs2.default, _wrappers2.default, _types2.default]; 86 | 87 | configs.forEach(function (config) { 88 | var i = 0; 89 | for (; i < config.length; i++) { 90 | config[i](formlyConfigProvider); 91 | } 92 | }); 93 | }]); 94 | 95 | exports.default = ngModuleName; 96 | 97 | /***/ }, 98 | /* 1 */ 99 | /***/ function(module, exports) { 100 | 101 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 102 | 103 | /***/ }, 104 | /* 2 */ 105 | /***/ function(module, exports, __webpack_require__) { 106 | 107 | 'use strict'; 108 | 109 | Object.defineProperty(exports, "__esModule", { 110 | value: true 111 | }); 112 | 113 | var _className = __webpack_require__(3); 114 | 115 | var _className2 = _interopRequireDefault(_className); 116 | 117 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 118 | 119 | exports.default = [_className2.default]; 120 | 121 | /***/ }, 122 | /* 3 */ 123 | /***/ function(module, exports, __webpack_require__) { 124 | 125 | 'use strict'; 126 | 127 | Object.defineProperty(exports, "__esModule", { 128 | value: true 129 | }); 130 | 131 | var _helpers = __webpack_require__(4); 132 | 133 | exports.default = function (formlyConfigProvider) { 134 | // add only step attribute because min and max are both built-in 135 | formlyConfigProvider.extras.fieldTransform.push(function (fields) { 136 | return (0, _helpers.ngModelAttrsTransformer)(fields, function (field) { 137 | return field.templateOptions && typeof field.templateOptions.className !== 'undefined'; 138 | }, 'className', { 139 | bound: 'ng-class' 140 | }); 141 | }); 142 | }; 143 | 144 | /***/ }, 145 | /* 4 */ 146 | /***/ function(module, exports, __webpack_require__) { 147 | 148 | 'use strict'; 149 | 150 | Object.defineProperty(exports, "__esModule", { 151 | value: true 152 | }); 153 | exports.ngModelAttrsManipulator = ngModelAttrsManipulator; 154 | exports.ngModelAttrsTransformer = ngModelAttrsTransformer; 155 | 156 | var _angular = __webpack_require__(1); 157 | 158 | var _angular2 = _interopRequireDefault(_angular); 159 | 160 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 161 | 162 | /** 163 | * Sets attribute with optional value. 164 | * Does not owerwrite. 165 | * @param {Array} nodes nodes 166 | * @param {String} attr attribute name 167 | * @param {String} val atrtibute value 168 | */ 169 | function addIfNotPresent(nodes, attr, val) { 170 | _angular2.default.forEach(nodes, function (node) { 171 | if (!node.getAttribute(attr)) { 172 | node.setAttribute(attr, val); 173 | } 174 | }); 175 | } 176 | 177 | /** 178 | * Gets all ngModels from node 179 | */ 180 | function getNgModelNodes(node) { 181 | var query = '[ng-model], [data-ng-model]'; 182 | 183 | return node.querySelectorAll(query); 184 | } 185 | 186 | /** 187 | * Adds attribute with optional value to all elements using ngModel directive. 188 | * Handles extras.skipNgModelAttrsManipulator 189 | * And does not overwrite attriutes 190 | * 191 | * @param {String} template Template provided by formly template manipulator 192 | * @param {Object} options Options provided by formly template manipulator 193 | * @param {String} attrName Attribute's name 194 | * @param {String|undefined} Attribute's value (optional) 195 | * @return {String} result 196 | */ 197 | function ngModelAttrsManipulator(template, options, attrName, attrValue) { 198 | var node = document.createElement('div'); 199 | var skip = options.extras && options.extras.skipNgModelAttrsManipulator; 200 | 201 | if (skip === true) { 202 | return template; 203 | } 204 | node.innerHTML = template; 205 | var modelNodes = getNgModelNodes(node); 206 | 207 | if (!modelNodes || !modelNodes.length) { 208 | return template; 209 | } 210 | 211 | addIfNotPresent(modelNodes, attrName, attrValue); 212 | 213 | return node.innerHTML; 214 | } 215 | 216 | /** 217 | * Adds ngModelAttr to the field when specified condition is true. 218 | * @param {Array} fields fields provided by formly's fieldTranform 219 | * @param {Funcion} condition with field as only parameter 220 | * @param {String} name ngModelAttr's name 221 | * @param {Object} settings ngModelAttr's settings 222 | * @return {Array} returns fields 223 | */ 224 | function ngModelAttrsTransformer(fields, condition, name, settings) { 225 | (fields || []).forEach(function (field) { 226 | if (condition(field) === true) { 227 | if (!field.ngModelAttrs) { 228 | field.ngModelAttrs = {}; 229 | } 230 | 231 | if (field.templateOptions && typeof field.templateOptions[name] !== 'undefined') { 232 | field.ngModelAttrs[name] = settings; 233 | } 234 | } 235 | }); 236 | 237 | return fields; 238 | } 239 | 240 | /***/ }, 241 | /* 5 */ 242 | /***/ function(module, exports, __webpack_require__) { 243 | 244 | 'use strict'; 245 | 246 | Object.defineProperty(exports, "__esModule", { 247 | value: true 248 | }); 249 | 250 | var _inputContainer = __webpack_require__(6); 251 | 252 | var _inputContainer2 = _interopRequireDefault(_inputContainer); 253 | 254 | var _label = __webpack_require__(8); 255 | 256 | var _label2 = _interopRequireDefault(_label); 257 | 258 | var _messages = __webpack_require__(10); 259 | 260 | var _messages2 = _interopRequireDefault(_messages); 261 | 262 | var _divider = __webpack_require__(12); 263 | 264 | var _divider2 = _interopRequireDefault(_divider); 265 | 266 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 267 | 268 | exports.default = [_inputContainer2.default, _label2.default, _messages2.default, _divider2.default]; 269 | 270 | /***/ }, 271 | /* 6 */ 272 | /***/ function(module, exports, __webpack_require__) { 273 | 274 | 'use strict'; 275 | 276 | Object.defineProperty(exports, "__esModule", { 277 | value: true 278 | }); 279 | 280 | var _inputContainer = __webpack_require__(7); 281 | 282 | var _inputContainer2 = _interopRequireDefault(_inputContainer); 283 | 284 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 285 | 286 | exports.default = function (formlyConfigProvider) { 287 | formlyConfigProvider.setWrapper({ 288 | template: _inputContainer2.default, 289 | name: 'inputContainer' 290 | }); 291 | }; 292 | 293 | /***/ }, 294 | /* 7 */ 295 | /***/ function(module, exports) { 296 | 297 | module.exports = "\n \n\n"; 298 | 299 | /***/ }, 300 | /* 8 */ 301 | /***/ function(module, exports, __webpack_require__) { 302 | 303 | 'use strict'; 304 | 305 | Object.defineProperty(exports, "__esModule", { 306 | value: true 307 | }); 308 | 309 | var _label = __webpack_require__(9); 310 | 311 | var _label2 = _interopRequireDefault(_label); 312 | 313 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 314 | 315 | exports.default = function (formlyConfigProvider) { 316 | formlyConfigProvider.setWrapper({ 317 | template: _label2.default, 318 | name: 'label', 319 | apiCheck: function apiCheck(check) { 320 | return { 321 | templateOptions: { 322 | label: check.string 323 | } 324 | }; 325 | } 326 | }); 327 | }; 328 | 329 | /***/ }, 330 | /* 9 */ 331 | /***/ function(module, exports) { 332 | 333 | module.exports = "\n\n"; 334 | 335 | /***/ }, 336 | /* 10 */ 337 | /***/ function(module, exports, __webpack_require__) { 338 | 339 | 'use strict'; 340 | 341 | Object.defineProperty(exports, "__esModule", { 342 | value: true 343 | }); 344 | 345 | var _messages = __webpack_require__(11); 346 | 347 | var _messages2 = _interopRequireDefault(_messages); 348 | 349 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 350 | 351 | exports.default = function (formlyConfigProvider) { 352 | formlyConfigProvider.setWrapper({ 353 | template: _messages2.default, 354 | name: 'messages' 355 | }); 356 | }; 357 | 358 | /***/ }, 359 | /* 11 */ 360 | /***/ function(module, exports) { 361 | 362 | module.exports = "\n
    \n
    \n {{message(fc.$viewValue, fc.$modelValue, this)}}\n
    \n
    \n"; 363 | 364 | /***/ }, 365 | /* 12 */ 366 | /***/ function(module, exports, __webpack_require__) { 367 | 368 | 'use strict'; 369 | 370 | Object.defineProperty(exports, "__esModule", { 371 | value: true 372 | }); 373 | 374 | var _divider = __webpack_require__(13); 375 | 376 | var _divider2 = _interopRequireDefault(_divider); 377 | 378 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 379 | 380 | exports.default = function (formlyConfigProvider) { 381 | formlyConfigProvider.setWrapper({ 382 | template: _divider2.default, 383 | name: 'divider', 384 | apiCheck: function apiCheck(check) { 385 | return { 386 | templateOptions: { 387 | divider: check.oneOf(['before', 'after']).optional 388 | } 389 | }; 390 | } 391 | }); 392 | }; 393 | 394 | /***/ }, 395 | /* 13 */ 396 | /***/ function(module, exports) { 397 | 398 | module.exports = "\n\n\n"; 399 | 400 | /***/ }, 401 | /* 14 */ 402 | /***/ function(module, exports, __webpack_require__) { 403 | 404 | 'use strict'; 405 | 406 | Object.defineProperty(exports, "__esModule", { 407 | value: true 408 | }); 409 | 410 | var _checkbox = __webpack_require__(15); 411 | 412 | var _checkbox2 = _interopRequireDefault(_checkbox); 413 | 414 | var _chips = __webpack_require__(17); 415 | 416 | var _chips2 = _interopRequireDefault(_chips); 417 | 418 | var _datepicker = __webpack_require__(19); 419 | 420 | var _datepicker2 = _interopRequireDefault(_datepicker); 421 | 422 | var _input = __webpack_require__(21); 423 | 424 | var _input2 = _interopRequireDefault(_input); 425 | 426 | var _radio = __webpack_require__(23); 427 | 428 | var _radio2 = _interopRequireDefault(_radio); 429 | 430 | var _select = __webpack_require__(25); 431 | 432 | var _select2 = _interopRequireDefault(_select); 433 | 434 | var _slider = __webpack_require__(27); 435 | 436 | var _slider2 = _interopRequireDefault(_slider); 437 | 438 | var _switch = __webpack_require__(29); 439 | 440 | var _switch2 = _interopRequireDefault(_switch); 441 | 442 | var _textarea = __webpack_require__(31); 443 | 444 | var _textarea2 = _interopRequireDefault(_textarea); 445 | 446 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 447 | 448 | exports.default = [_checkbox2.default, _chips2.default, _datepicker2.default, _input2.default, _radio2.default, _select2.default, _slider2.default, _switch2.default, _textarea2.default]; 449 | 450 | /***/ }, 451 | /* 15 */ 452 | /***/ function(module, exports, __webpack_require__) { 453 | 454 | 'use strict'; 455 | 456 | Object.defineProperty(exports, "__esModule", { 457 | value: true 458 | }); 459 | 460 | var _checkbox = __webpack_require__(16); 461 | 462 | var _checkbox2 = _interopRequireDefault(_checkbox); 463 | 464 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 465 | 466 | exports.default = function (formlyConfigProvider) { 467 | formlyConfigProvider.setType({ 468 | template: _checkbox2.default, 469 | name: 'checkbox', 470 | defaultOptions: { 471 | ngModelAttrs: { 472 | disabled: { 473 | bound: 'ng-disabled' 474 | } 475 | } 476 | }, 477 | apiCheck: function apiCheck(check) { 478 | return { 479 | templateOptions: { 480 | disabled: check.bool.optional, 481 | theme: check.string.optional 482 | } 483 | }; 484 | } 485 | }); 486 | }; 487 | 488 | /***/ }, 489 | /* 16 */ 490 | /***/ function(module, exports) { 491 | 492 | module.exports = "
    \n \n {{to.label}}\n \n
    \n"; 493 | 494 | /***/ }, 495 | /* 17 */ 496 | /***/ function(module, exports, __webpack_require__) { 497 | 498 | 'use strict'; 499 | 500 | Object.defineProperty(exports, "__esModule", { 501 | value: true 502 | }); 503 | 504 | var _chips = __webpack_require__(18); 505 | 506 | var _chips2 = _interopRequireDefault(_chips); 507 | 508 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 509 | 510 | exports.default = function (formlyConfigProvider) { 511 | formlyConfigProvider.setType({ 512 | template: _chips2.default, 513 | name: 'chips', 514 | wrapper: ['label'], 515 | defaultOptions: { 516 | defaultValue: [], 517 | ngModelAttrs: { 518 | placeholder: { 519 | attribute: 'placeholder' 520 | }, 521 | secondaryPlaceholder: { 522 | attribute: 'secondary-placeholder' 523 | }, 524 | deleteButtonLabel: { 525 | attribute: 'delete-button-label' 526 | }, 527 | deleteHint: { 528 | attribute: 'delete-hint' 529 | }, 530 | onAdd: { 531 | bound: 'md-on-add' 532 | }, 533 | onRemove: { 534 | bound: 'md-on-remove' 535 | }, 536 | onSelect: { 537 | bound: 'md-on-select' 538 | } 539 | } 540 | }, 541 | apiCheck: function apiCheck(check) { 542 | return { 543 | templateOptions: { 544 | placeholder: check.string.optional, 545 | secondaryPlaceholder: check.string.optional, 546 | deleteButtonLabel: check.string.optional, 547 | deleteHint: check.string.optional, 548 | onAdd: check.func.optional, 549 | onRemove: check.func.optional, 550 | onSelect: check.func.optional, 551 | theme: check.string.optional 552 | } 553 | }; 554 | } 555 | }); 556 | }; 557 | 558 | /***/ }, 559 | /* 18 */ 560 | /***/ function(module, exports) { 561 | 562 | module.exports = "\n"; 563 | 564 | /***/ }, 565 | /* 19 */ 566 | /***/ function(module, exports, __webpack_require__) { 567 | 568 | 'use strict'; 569 | 570 | Object.defineProperty(exports, "__esModule", { 571 | value: true 572 | }); 573 | 574 | var _datepicker = __webpack_require__(20); 575 | 576 | var _datepicker2 = _interopRequireDefault(_datepicker); 577 | 578 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 579 | 580 | exports.default = function (formlyConfigProvider) { 581 | formlyConfigProvider.setType({ 582 | template: _datepicker2.default, 583 | name: 'datepicker', 584 | wrapper: ['label', 'messages'], 585 | defaultOptions: { 586 | templateOptions: { 587 | disabled: false 588 | }, 589 | ngModelAttrs: { 590 | disabled: { 591 | bound: 'ng-disabled' 592 | }, 593 | placeholder: { 594 | attribute: 'md-placeholder' 595 | }, 596 | minDate: { 597 | bound: 'md-min-date' 598 | }, 599 | maxDate: { 600 | bound: 'md-max-date' 601 | }, 602 | filterDate: { 603 | bound: 'md-date-filter' 604 | } 605 | } 606 | }, 607 | apiCheck: function apiCheck(check) { 608 | return { 609 | templateOptions: { 610 | disabled: check.bool.optional, 611 | placeholder: check.string.optional, 612 | minDate: check.instanceOf(Date).optional, 613 | maxDate: check.instanceOf(Date).optional, 614 | filterDate: check.func.optional, 615 | theme: check.string.optional 616 | } 617 | }; 618 | } 619 | }); 620 | }; 621 | 622 | /***/ }, 623 | /* 20 */ 624 | /***/ function(module, exports) { 625 | 626 | module.exports = "
    \n \n
    \n"; 627 | 628 | /***/ }, 629 | /* 21 */ 630 | /***/ function(module, exports, __webpack_require__) { 631 | 632 | 'use strict'; 633 | 634 | Object.defineProperty(exports, "__esModule", { 635 | value: true 636 | }); 637 | 638 | var _input = __webpack_require__(22); 639 | 640 | var _input2 = _interopRequireDefault(_input); 641 | 642 | var _helpers = __webpack_require__(4); 643 | 644 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 645 | 646 | exports.default = function (formlyConfigProvider) { 647 | formlyConfigProvider.setType({ 648 | template: _input2.default, 649 | name: 'input', 650 | wrapper: ['label', 'messages', 'inputContainer'], 651 | defaultOptions: { 652 | templateOptions: { 653 | type: 'text', 654 | disabled: false 655 | }, 656 | ngModelAttrs: { 657 | mdMaxlength: { 658 | bound: 'md-maxlength' 659 | }, 660 | disabled: { 661 | bound: 'ng-disabled' 662 | }, 663 | pattern: { 664 | bound: 'ng-pattern' 665 | } 666 | } 667 | }, 668 | apiCheck: function apiCheck(check) { 669 | return { 670 | templateOptions: { 671 | disabled: check.bool.optional, 672 | type: check.string, 673 | step: check.number.optional, 674 | pattern: check.oneOfType([check.string, check.instanceOf(RegExp)]).optional, 675 | theme: check.string.optional 676 | } 677 | }; 678 | } 679 | }); 680 | 681 | // add only step attribute because min and max are both built-in 682 | formlyConfigProvider.extras.fieldTransform.push(function (fields) { 683 | return (0, _helpers.ngModelAttrsTransformer)(fields, function (field) { 684 | return field.type === 'input' && field.templateOptions && field.templateOptions.type === 'number'; 685 | }, 'step', { 686 | attribute: 'step' 687 | }); 688 | }); 689 | }; 690 | 691 | /***/ }, 692 | /* 22 */ 693 | /***/ function(module, exports) { 694 | 695 | module.exports = ""; 696 | 697 | /***/ }, 698 | /* 23 */ 699 | /***/ function(module, exports, __webpack_require__) { 700 | 701 | 'use strict'; 702 | 703 | Object.defineProperty(exports, "__esModule", { 704 | value: true 705 | }); 706 | 707 | var _radio = __webpack_require__(24); 708 | 709 | var _radio2 = _interopRequireDefault(_radio); 710 | 711 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 712 | 713 | exports.default = function (formlyConfigProvider) { 714 | formlyConfigProvider.setType({ 715 | template: _radio2.default, 716 | name: 'radio', 717 | wrapper: ['label'], 718 | apiCheck: function apiCheck(check) { 719 | return { 720 | templateOptions: { 721 | options: check.arrayOf(check.object), 722 | labelProp: check.string.optional, 723 | valueProp: check.string.optional, 724 | theme: check.string.optional 725 | } 726 | }; 727 | } 728 | }); 729 | }; 730 | 731 | /***/ }, 732 | /* 24 */ 733 | /***/ function(module, exports) { 734 | 735 | module.exports = "\n \n {{option[to.labelProp || 'name']}}\n \n\n"; 736 | 737 | /***/ }, 738 | /* 25 */ 739 | /***/ function(module, exports, __webpack_require__) { 740 | 741 | 'use strict'; 742 | 743 | Object.defineProperty(exports, "__esModule", { 744 | value: true 745 | }); 746 | 747 | var _select = __webpack_require__(26); 748 | 749 | var _select2 = _interopRequireDefault(_select); 750 | 751 | var _helpers = __webpack_require__(4); 752 | 753 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 754 | 755 | exports.default = function (formlyConfigProvider) { 756 | formlyConfigProvider.setType({ 757 | template: _select2.default, 758 | name: 'select', 759 | wrapper: ['label', 'messages', 'inputContainer'], 760 | defaultOptions: { 761 | templateOptions: { 762 | disabled: false 763 | }, 764 | ngModelAttrs: { 765 | disabled: { 766 | bound: 'ng-disabled' 767 | }, 768 | onClose: { 769 | bound: 'md-on-close' 770 | }, 771 | onOpen: { 772 | bound: 'md-on-open' 773 | } 774 | } 775 | }, 776 | apiCheck: function apiCheck(check) { 777 | return { 778 | templateOptions: { 779 | disabled: check.bool.optional, 780 | options: check.arrayOf(check.object), 781 | multiple: check.bool.optional, 782 | labelProp: check.string.optional, 783 | valueProp: check.string.optional, 784 | onClose: check.func.optional, 785 | onOpen: check.func.optional, 786 | theme: check.string.optional 787 | } 788 | }; 789 | } 790 | }); 791 | 792 | formlyConfigProvider.templateManipulators.preWrapper.push(function (tpl, options) { 793 | var to = options.templateOptions || {}; 794 | // adds multiple only when: 795 | // templateOptions.multiple equals true 796 | return to.multiple === true ? (0, _helpers.ngModelAttrsManipulator)(tpl, options, 'multiple') : tpl; 797 | }); 798 | }; 799 | 800 | /***/ }, 801 | /* 26 */ 802 | /***/ function(module, exports) { 803 | 804 | module.exports = "\n \n {{ option[to.labelProp || 'name'] }}\n \n\n"; 805 | 806 | /***/ }, 807 | /* 27 */ 808 | /***/ function(module, exports, __webpack_require__) { 809 | 810 | 'use strict'; 811 | 812 | Object.defineProperty(exports, "__esModule", { 813 | value: true 814 | }); 815 | 816 | var _slider = __webpack_require__(28); 817 | 818 | var _slider2 = _interopRequireDefault(_slider); 819 | 820 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 821 | 822 | exports.default = function (formlyConfigProvider) { 823 | formlyConfigProvider.setType({ 824 | template: _slider2.default, 825 | name: 'slider', 826 | wrapper: ['label'], 827 | defaultOptions: { 828 | templateOptions: { 829 | disabled: false 830 | }, 831 | ngModelAttrs: { 832 | disabled: { 833 | bound: 'ng-disabled' 834 | }, 835 | min: { 836 | attribute: 'min' 837 | }, 838 | max: { 839 | attribute: 'max' 840 | }, 841 | step: { 842 | attribute: 'step' 843 | }, 844 | discrete: { 845 | bound: 'md-discrete' 846 | } 847 | } 848 | }, 849 | apiCheck: function apiCheck(check) { 850 | return { 851 | templateOptions: { 852 | disabled: check.bool.optional, 853 | min: check.number.optional, 854 | max: check.number.optional, 855 | step: check.number.optional, 856 | discrete: check.bool.optional, 857 | theme: check.string.optional 858 | } 859 | }; 860 | } 861 | }); 862 | }; 863 | 864 | /***/ }, 865 | /* 28 */ 866 | /***/ function(module, exports) { 867 | 868 | module.exports = "\n"; 869 | 870 | /***/ }, 871 | /* 29 */ 872 | /***/ function(module, exports, __webpack_require__) { 873 | 874 | 'use strict'; 875 | 876 | Object.defineProperty(exports, "__esModule", { 877 | value: true 878 | }); 879 | 880 | var _switch = __webpack_require__(30); 881 | 882 | var _switch2 = _interopRequireDefault(_switch); 883 | 884 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 885 | 886 | exports.default = function (formlyConfigProvider) { 887 | formlyConfigProvider.setType({ 888 | template: _switch2.default, 889 | name: 'switch', 890 | defaultOptions: { 891 | templateOptions: { 892 | disabled: false 893 | }, 894 | ngModelAttrs: { 895 | disabled: { 896 | bound: 'ng-disabled' 897 | } 898 | } 899 | }, 900 | apiCheck: function apiCheck(check) { 901 | return { 902 | templateOptions: { 903 | disabled: check.bool.optional, 904 | theme: check.string.optional 905 | } 906 | }; 907 | } 908 | }); 909 | }; 910 | 911 | /***/ }, 912 | /* 30 */ 913 | /***/ function(module, exports) { 914 | 915 | module.exports = "\n {{to.label}}\n\n"; 916 | 917 | /***/ }, 918 | /* 31 */ 919 | /***/ function(module, exports, __webpack_require__) { 920 | 921 | 'use strict'; 922 | 923 | Object.defineProperty(exports, "__esModule", { 924 | value: true 925 | }); 926 | 927 | var _textarea = __webpack_require__(32); 928 | 929 | var _textarea2 = _interopRequireDefault(_textarea); 930 | 931 | var _helpers = __webpack_require__(4); 932 | 933 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 934 | 935 | exports.default = function (formlyConfigProvider) { 936 | formlyConfigProvider.setType({ 937 | template: _textarea2.default, 938 | name: 'textarea', 939 | wrapper: ['label', 'messages', 'inputContainer'], 940 | defaultOptions: { 941 | ngModelAttrs: { 942 | disabled: { 943 | bound: 'ng-disabled' 944 | }, 945 | rows: { 946 | attribute: 'rows' 947 | }, 948 | cols: { 949 | attribute: 'cols' 950 | } 951 | }, 952 | templateOptions: { 953 | grow: true 954 | } 955 | }, 956 | apiCheck: function apiCheck(check) { 957 | return { 958 | templateOptions: { 959 | disabled: check.bool.optional, 960 | rows: check.number.optional, 961 | cols: check.number.optional, 962 | grow: check.bool.optional, 963 | theme: check.string.optional 964 | } 965 | }; 966 | } 967 | }); 968 | 969 | formlyConfigProvider.extras.fieldTransform.push(function (fields) { 970 | return (0, _helpers.ngModelAttrsTransformer)(fields, function (field) { 971 | return field.type === 'textarea' && field.templateOptions && field.templateOptions.grow === false; 972 | }, 'grow', { 973 | attribute: 'md-no-autogrow' 974 | }); 975 | }); 976 | }; 977 | 978 | /***/ }, 979 | /* 32 */ 980 | /***/ function(module, exports) { 981 | 982 | module.exports = ""; 983 | 984 | /***/ } 985 | /******/ ]) 986 | }); 987 | ; --------------------------------------------------------------------------------