fix(textInputs): register components, don't use Query

This commit is contained in:
Adam Bradley
2015-10-06 22:20:40 -05:00
parent d0a077c535
commit f7bed3e098
4 changed files with 77 additions and 330 deletions

View File

@ -1,6 +1,7 @@
import {Directive} from 'angular2/angular2'; import {Directive, Optional} from 'angular2/angular2';
import {IonicConfig} from '../../config/config'; import {IonicConfig} from '../../config/config';
import {TextInput} from './text-input';
import {pointerCoord, hasPointerMoved} from '../../util/dom'; import {pointerCoord, hasPointerMoved} from '../../util/dom';
/** /**
@ -25,8 +26,9 @@ export class Label {
* TODO * TODO
* @param {IonicConfig} config * @param {IonicConfig} config
*/ */
constructor(config: IonicConfig) { constructor(config: IonicConfig, @Optional() textInput: TextInput) {
this.scrollAssist = config.get('keyboardScrollAssist'); this.scrollAssist = config.get('keyboardScrollAssist');
textInput && textInput.registerLabel(this);
} }
/** /**

View File

@ -11,61 +11,6 @@ import * as dom from '../../util/dom';
import {IonicPlatform} from '../../platform/platform'; import {IonicPlatform} from '../../platform/platform';
/**
* TODO
*/
@Directive({
selector: 'textarea,input[type=text],input[type=password],input[type=number],input[type=search],input[type=email],input[type=url],input[type=tel]',
property: [
'tabIndex'
],
host: {
'[tabIndex]': 'tabIndex',
'[attr.aria-labelledby]': 'labelledBy',
'class': 'text-input input'
}
})
export class TextInputElement {
/**
* TODO
* @param {string} type The value of the underlying element's type attribute.
* @param {ElementRef} elementRef TODO
* @param {IonicConfig} config TODO
*/
constructor(
@Attribute('type') type: string,
elementRef: ElementRef,
config: IonicConfig
) {
this.type = type;
this.elementRef = elementRef;
this.tabIndex = this.tabIndex || '';
}
/**
* Focus the input.
*/
setFocus() {
this.elementRef.nativeElement.focus();
}
/**
* Whether the input has focus or not.
* @returns {boolean} true if the input has focus, otherwise false.
*/
get hasFocus() {
return dom.hasFocus(this.elementRef);
}
/**
* Whether the input has a value.
* @returns {boolean} true if the input has a value, otherwise false.
*/
get hasValue() {
return (this.elementRef.nativeElement.value !== '');
}
}
/** /**
* TODO * TODO
*/ */
@ -104,9 +49,7 @@ export class TextInput extends Ion {
app: IonicApp, app: IonicApp,
ngZone: NgZone, ngZone: NgZone,
platform: IonicPlatform, platform: IonicPlatform,
@Optional() @Host() scrollView: Content, @Optional() @Host() scrollView: Content
@Query(TextInputElement) inputQry: QueryList<TextInputElement>,
@Query(Label) labelQry: QueryList<Label>
) { ) {
super(elementRef, config); super(elementRef, config);
@ -118,33 +61,31 @@ export class TextInput extends Ion {
this.app = app; this.app = app;
this.zone = ngZone; this.zone = ngZone;
this.platform = platform; this.platform = platform;
this.inputQry = inputQry;
this.labelQry = labelQry;
this.keyboardHeight = this.config.get('keyboardHeight'); this.keyboardHeight = this.config.get('keyboardHeight');
} }
registerInputElement(textInputElement) {
this.input = textInputElement;
this.type = textInputElement.type;
textInputElement.tabIndex = -1;
}
registerLabel(label) {
this.label = label;
}
/** /**
* TODO * TODO
*/ */
onInit() { onInit() {
super.onInit(); super.onInit();
let label = this.labelQry.first; if (this.input && this.label) {
this.input = this.inputQry.first; this.input.labelledBy = this.label.id = (this.label.id || 'label-' + this.id);
if (this.input) {
this.type = this.input.type;
this.input.tabIndex = -1;
if (label) {
label.id = (label.id || 'label-' + this.id);
this.input.labelledBy = label.id;
}
} }
let self = this; let self = this;
self.scrollMove = (ev) => { self.scrollMove = (ev) => {
self.deregListeners(); self.deregListeners();
@ -440,4 +381,62 @@ export class TextInput extends Ion {
} }
/**
* TODO
*/
@Directive({
selector: 'textarea,input[type=text],input[type=password],input[type=number],input[type=search],input[type=email],input[type=url],input[type=tel]',
inputs: [
'tabIndex'
],
host: {
'[tabIndex]': 'tabIndex',
'[attr.aria-labelledby]': 'labelledBy',
'class': 'text-input input'
}
})
export class TextInputElement {
/**
* TODO
* @param {string} type The value of the underlying element's type attribute.
* @param {ElementRef} elementRef TODO
* @param {IonicConfig} config TODO
*/
constructor(
@Attribute('type') type: string,
elementRef: ElementRef,
@Optional() textInput: TextInput
) {
this.type = type;
this.elementRef = elementRef;
this.tabIndex = this.tabIndex || '';
textInput && textInput.registerInputElement(this);
}
/**
* Focus the input.
*/
setFocus() {
this.elementRef.nativeElement.focus();
}
/**
* Whether the input has focus or not.
* @returns {boolean} true if the input has focus, otherwise false.
*/
get hasFocus() {
return dom.hasFocus(this.elementRef);
}
/**
* Whether the input has a value.
* @returns {boolean} true if the input has a value, otherwise false.
*/
get hasValue() {
return (this.elementRef.nativeElement.value !== '');
}
}
const SCROLL_INTO_VIEW_DURATION = 400; const SCROLL_INTO_VIEW_DURATION = 400;

View File

@ -1,256 +0,0 @@
import {Component, Directive, View, bootstrap} from 'angular2/angular2'
import * as util from 'ionic/util';
<<<<<<< HEAD
import {ionicBindings} from './bootstrap';
import {IONIC_DIRECTIVES} from './directives';
=======
import {IonicConfig} from './config';
import {ionicBootstrap} from '../components/app/app';
import {
Menu, MenuToggle, MenuClose,
Button, Content, Scroll, Refresher,
Slides, Slide, SlideLazy,
Tabs, Tab,
Card, List, ListHeader, Item, ItemGroup, ItemGroupTitle, ItemSliding,
Toolbar, ToolbarTitle, ToolbarItem,
Icon,
Checkbox, Switch,
TextInput, TextInputElement, Label,
Segment, SegmentButton, SegmentControlValueAccessor,
RadioGroup, RadioButton, SearchBar,
Nav, NavbarTemplate, Navbar,
NavPush, NavPop, NavRouter,
IdRef,
ShowWhen, HideWhen
} from '../ionic';
/**
* The core Ionic directives. Automatically available in every IonicView
* template.
*/
export const IONIC_DIRECTIVES = [
// Angular
CORE_DIRECTIVES,
FORM_DIRECTIVES,
NgStyle,
// Content
forwardRef(() => Menu),
forwardRef(() => MenuToggle),
forwardRef(() => MenuClose),
forwardRef(() => Button),
forwardRef(() => Content),
forwardRef(() => Scroll),
forwardRef(() => Refresher),
// Lists
forwardRef(() => Card),
forwardRef(() => List),
forwardRef(() => ListHeader),
forwardRef(() => Item),
forwardRef(() => ItemGroup),
forwardRef(() => ItemGroupTitle),
forwardRef(() => ItemSliding),
// Slides
forwardRef(() => Slides),
forwardRef(() => Slide),
forwardRef(() => SlideLazy),
// Tabs
forwardRef(() => Tabs),
forwardRef(() => Tab),
// Toolbar
forwardRef(() => Toolbar),
forwardRef(() => ToolbarTitle),
forwardRef(() => ToolbarItem),
// Media
forwardRef(() => Icon),
// Forms
forwardRef(() => SearchBar),
forwardRef(() => Segment),
forwardRef(() => SegmentButton),
forwardRef(() => SegmentControlValueAccessor),
forwardRef(() => Checkbox),
forwardRef(() => RadioGroup),
forwardRef(() => RadioButton),
forwardRef(() => Switch),
forwardRef(() => TextInput),
forwardRef(() => TextInputElement),
forwardRef(() => Label),
// Nav
forwardRef(() => Nav),
forwardRef(() => NavbarTemplate),
forwardRef(() => Navbar),
forwardRef(() => NavPush),
forwardRef(() => NavPop),
forwardRef(() => NavRouter),
forwardRef(() => IdRef),
forwardRef(() => ShowWhen),
forwardRef(() => HideWhen)
];
>>>>>>> master
/**
* @private
*/
class IonicViewImpl extends View {
constructor(args = {}) {
args.directives = (args.directives || []).concat(IONIC_DIRECTIVES);
super(args);
}
}
/**
* The IonicView decorator indicates that the decorated class is an Ionic
* navigation view, meaning it can be navigated to using a [NavController](../../Nav/NavController/#creating_views)
*
* Ionic views have all [IONIC_DIRECTIVES](../IONIC_DIRECTIVES/), which include
* all Ionic components, as well as Angular's [CORE_DIRECTIVES](https://angular.io/docs/js/latest/api/core/CORE_DIRECTIVES-const.html)
* and [FORM_DIRECTIVES](https://angular.io/docs/js/latest/api/core/FORM_DIRECTIVES-const.html),
* already provided to them, so you only need to supply custom directives to
* your Ionic views:
*
* ```ts
* @IonicView({
* template: '<ion-checkbox my-custom-dir>' +
* '</ion-checkbox>'
* directives: [MyCustomDirective]
* })
* class MyPage {}
* ```
* Here [Checkbox](../../../components/checkbox/Checkbox/) will load because
* it is in IONIC_DIRECTIVES, so there is no need to add it to the `directives`
* array.
*
* For custom components that use Ionic components, you will need to include
* IONIC_DIRECTIVES in the `directives` array:
*
* ```ts
* import {IONIC_DIRECTIVES} from 'ionic/ionic';
* @Component({
* template: `<div class="customStyle">
* <ion-checkbox></ion-checkbox>
* </div>`
* })
* @View({
* directives: [IONIC_DIRECTIVES]
* })
* class MyCustomCheckbox {}
*```
* Alternatively, you could:
* ```ts
* import {Checkbox} from 'ionic/ionic'
* ```
* along with any other components and add them individually:
* ```
* @View({
* directives: [Checkbox]
* })
* ```
* However, using IONIC_DIRECTIVES will always Just Work :tm: with no
* performance overhead, so there is really no reason to not always use it.
*
* Ionic views are also automatically wrapped in `<ion-view>`, so although you
* may see these tags if you inspect your markup, you don't need to include them
* in your templates.
*
*/
export function IonicView(args) {
return function(cls) {
var annotations = Reflect.getMetadata('annotations', cls) || [];
annotations.push(new IonicViewImpl(args));
Reflect.defineMetadata('annotations', annotations, cls);
return cls;
}
}
/**
* TODO
*/
export function IonicDirective(config) {
return function(cls) {
var annotations = Reflect.getMetadata('annotations', cls) || [];
annotations.push(new Directive(appendConfig(cls, config)));
Reflect.defineMetadata('annotations', annotations, cls);
return cls;
}
}
/**
* TODO
*/
export function IonicComponent(config) {
return function(cls) {
return makeComponent(cls, appendConfig(cls, config));
}
}
export function makeComponent(cls, config) {
var annotations = Reflect.getMetadata('annotations', cls) || [];
annotations.push(new Component(config));
Reflect.defineMetadata('annotations', annotations, cls);
return cls;
}
function appendConfig(cls, config) {
config.host = config.host || {};
cls.defaultProperties = config.defaultProperties || {};
config.properties = config.properties || [];
for (let prop in cls.defaultProperties) {
// add the property to the component "properties"
config.properties.push(prop);
// set the component "hostProperties", so the instance's
// property value will be used to set the element's attribute
config.host['[attr.' + util.pascalCaseToDashCase(prop) + ']'] = prop;
}
cls.delegates = config.delegates;
let componentId = config.classId || (config.selector && config.selector.replace('ion-', ''));
config.host['class'] = ((config.host['class'] || '') + ' ' + componentId).trim();
return config;
}
/**
* TODO
*/
export function App(args={}) {
return function(cls) {
// get current annotations
let annotations = Reflect.getMetadata('annotations', cls) || [];
// create @Component
args.selector = args.selector || 'ion-app';
annotations.push(new Component(args));
// create @View
// if no template was provided, default so it has a root ion-nav
if (!args.templateUrl && !args.template) {
args.template = '<ion-nav></ion-nav>';
}
annotations.push(new IonicViewImpl(args));
// redefine with added annotations
Reflect.defineMetadata('annotations', annotations, cls);
bootstrap(cls, ionicBindings(cls, args.config));
return cls;
}
}

View File

@ -15,6 +15,7 @@ import {Card} from '../components/card/card';
import {List, ListHeader} from '../components/list/list'; import {List, ListHeader} from '../components/list/list';
import {Item} from '../components/item/item'; import {Item} from '../components/item/item';
import {ItemGroup, ItemGroupTitle} from '../components/item/item-group'; import {ItemGroup, ItemGroupTitle} from '../components/item/item-group';
import {ItemSliding} from '../components/item/item-sliding';
import {Toolbar, ToolbarTitle, ToolbarItem} from '../components/toolbar/toolbar'; import {Toolbar, ToolbarTitle, ToolbarItem} from '../components/toolbar/toolbar';
import {Icon} from '../components/icon/icon'; import {Icon} from '../components/icon/icon';
import {Checkbox} from '../components/checkbox/checkbox'; import {Checkbox} from '../components/checkbox/checkbox';
@ -58,6 +59,7 @@ export const IONIC_DIRECTIVES = [
Item, Item,
ItemGroup, ItemGroup,
ItemGroupTitle, ItemGroupTitle,
ItemSliding,
// Slides // Slides
Slides, Slides,