mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Merge branch 'master' into v4
# Conflicts: # src/components/toggle/toggle.ts
This commit is contained in:
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,3 +1,24 @@
|
||||
<a name="3.1.1"></a>
|
||||
## [3.1.1](https://github.com/driftyco/ionic/compare/v3.1.0...v3.1.1) (2017-04-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** return the correct value ([af394b5](https://github.com/driftyco/ionic/commit/af394b5))
|
||||
* **datetime:** always returns a string ([6677d80](https://github.com/driftyco/ionic/commit/6677d80))
|
||||
* **input:** don't dispatch first ngModel update ([505d27a](https://github.com/driftyco/ionic/commit/505d27a))
|
||||
* **input:** initialize in ngAfterContentInit ([239b559](https://github.com/driftyco/ionic/commit/239b559))
|
||||
* **menu:** missing output events in interface ([7eb2f0b](https://github.com/driftyco/ionic/commit/7eb2f0b)), closes [#11383](https://github.com/driftyco/ionic/issues/11383)
|
||||
* **range:** fix label reference ([ee6c481](https://github.com/driftyco/ionic/commit/ee6c481))
|
||||
* **searchbar:** add setFocus() back ([9264a04](https://github.com/driftyco/ionic/commit/9264a04)), closes [#11397](https://github.com/driftyco/ionic/issues/11397)
|
||||
* **select:** use correct assert condition ([ec2a34d](https://github.com/driftyco/ionic/commit/ec2a34d))
|
||||
* **select:** return the value in ionChange instead of this ([d24c4f4](https://github.com/driftyco/ionic/commit/d24c4f4))
|
||||
* **select:** allow object as value ([4c8efc2](https://github.com/driftyco/ionic/commit/4c8efc2))
|
||||
* **select:** remove private reference ([c0276d5](https://github.com/driftyco/ionic/commit/c0276d5))
|
||||
* **toggle:** initialize in ngAfterContentInit ([539901d](https://github.com/driftyco/ionic/commit/539901d))
|
||||
|
||||
|
||||
|
||||
<a name="3.1.0"></a>
|
||||
# [3.1.0](https://github.com/driftyco/ionic/compare/v3.0.1...v3.1.0) (2017-04-26)
|
||||
|
||||
@@ -20,7 +41,7 @@ Update your package.json to match the following dependencies, remove the existin
|
||||
"@ionic-native/splash-screen": "3.4.2",
|
||||
"@ionic-native/status-bar": "3.4.2",
|
||||
"@ionic/storage": "2.0.1",
|
||||
"ionic-angular": "3.1.0",
|
||||
"ionic-angular": "3.1.1",
|
||||
"ionicons": "3.0.0",
|
||||
"rxjs": "5.1.1",
|
||||
"sw-toolbox": "3.4.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "ionic2",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "A powerful framework for building mobile and progressive web apps with JavaScript and Angular 2",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { accessSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { dirname, join, relative } from 'path';
|
||||
|
||||
@@ -9,7 +10,7 @@ import * as runSequence from 'run-sequence';
|
||||
import { argv } from 'yargs';
|
||||
|
||||
|
||||
import { ES_2015, PROJECT_ROOT, SRC_ROOT, SRC_COMPONENTS_ROOT, SCRIPTS_ROOT } from '../constants';
|
||||
import { DIST_E2E_COMPONENTS_ROOT, ES_2015, PROJECT_ROOT, SRC_ROOT, SRC_COMPONENTS_ROOT, SCRIPTS_ROOT } from '../constants';
|
||||
import { createTempTsConfig, createTimestamp, getFolderInfo, readFileAsync, runAppScriptsBuild, writeFileAsync, writePolyfills } from '../util';
|
||||
|
||||
import * as pAll from 'p-all';
|
||||
@@ -240,3 +241,22 @@ task('e2e.polyfill', (done: Function) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
task('e2e.openProd', (done: Function) => {
|
||||
runSequence('e2e.prod', 'e2e.open', (err: any) => done(err));
|
||||
});
|
||||
|
||||
task('e2e.open', (done: Function) => {
|
||||
const folderInfo = getFolderInfo();
|
||||
if (folderInfo && folderInfo.componentName && folderInfo.componentTest) {
|
||||
const filePath = `${folderInfo.componentName}/test/${folderInfo.componentTest}/www/index.html`;
|
||||
const fullPath = join(DIST_E2E_COMPONENTS_ROOT, filePath);
|
||||
const spawnedCommand = spawn('open', [fullPath]);
|
||||
|
||||
spawnedCommand.on('close', (code: number) => {
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
console.log(`Can't open without folder argument.`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import { Animation } from '../../animations/animation';
|
||||
import { Side } from '../../util/util';
|
||||
|
||||
export interface Menu {
|
||||
|
||||
setOpen(shouldOpen: boolean, animated: boolean): Promise<boolean>;
|
||||
open(): Promise<boolean>;
|
||||
close(): Promise<boolean>;
|
||||
@@ -13,6 +15,11 @@ export interface Menu {
|
||||
side: Side;
|
||||
id: string;
|
||||
isRightSide: boolean;
|
||||
|
||||
ionDrag: EventEmitter<number>;
|
||||
ionOpen: EventEmitter<boolean>;
|
||||
ionClose: EventEmitter<boolean>;
|
||||
|
||||
isAnimating(): boolean;
|
||||
width(): number;
|
||||
getContentElement(): HTMLElement;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnDestroy, Optional, Renderer, ViewEncapsulation } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnDestroy, Optional, Renderer, ViewEncapsulation } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
@@ -66,7 +66,7 @@ import { Item } from '../item/item';
|
||||
providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: Checkbox, multi: true } ],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Checkbox extends BaseInput<boolean> implements IonicTapInput, AfterViewInit, OnDestroy {
|
||||
export class Checkbox extends BaseInput<boolean> implements IonicTapInput, OnDestroy {
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, the element is selected.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, Component, ElementRef, EventEmitter, HostListener, Input, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core';
|
||||
import { AfterContentInit, Component, ElementRef, EventEmitter, HostListener, Input, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
@@ -9,7 +9,7 @@ import { Form } from '../../util/form';
|
||||
import { BaseInput } from '../../util/base-input';
|
||||
import { Item } from '../item/item';
|
||||
import { deepCopy, isBlank, isPresent, isArray, isString, assert, clamp } from '../../util/util';
|
||||
import { dateValueRange, renderDateTime, renderTextFormat, convertFormatToKey, getValueFromFormat, parseTemplate, parseDate, updateDate, DateTimeData, daysInMonth, dateSortValue, dateDataSortValue, LocaleData } from '../../util/datetime-util';
|
||||
import { dateValueRange, renderDateTime, renderTextFormat, convertDataToISO, convertFormatToKey, getValueFromFormat, parseTemplate, parseDate, updateDate, DateTimeData, daysInMonth, dateSortValue, dateDataSortValue, LocaleData } from '../../util/datetime-util';
|
||||
|
||||
/**
|
||||
* @name DateTime
|
||||
@@ -267,7 +267,7 @@ import { dateValueRange, renderDateTime, renderTextFormat, convertFormatToKey, g
|
||||
providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: DateTime, multi: true } ],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class DateTime extends BaseInput<DateTimeData> implements AfterViewInit, ControlValueAccessor, OnDestroy {
|
||||
export class DateTime extends BaseInput<DateTimeData> implements AfterContentInit, ControlValueAccessor, OnDestroy {
|
||||
|
||||
_text: string = '';
|
||||
_min: DateTimeData;
|
||||
@@ -425,7 +425,7 @@ export class DateTime extends BaseInput<DateTimeData> implements AfterViewInit,
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
ngAfterContentInit() {
|
||||
// first see if locale names were provided in the inputs
|
||||
// then check to see if they're in the config
|
||||
// if neither were provided then it will use default English names
|
||||
@@ -436,13 +436,6 @@ export class DateTime extends BaseInput<DateTimeData> implements AfterViewInit,
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputUpdated() {
|
||||
this.updateText();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@@ -451,6 +444,13 @@ export class DateTime extends BaseInput<DateTimeData> implements AfterViewInit,
|
||||
return this._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputUpdated() {
|
||||
this.updateText();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@@ -458,6 +458,21 @@ export class DateTime extends BaseInput<DateTimeData> implements AfterViewInit,
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: REMOVE THIS
|
||||
* @hidden
|
||||
*/
|
||||
_inputChangeEvent(): any {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputNgModelEvent(): any {
|
||||
return convertDataToISO(this.value);
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
_click(ev: UIEvent) {
|
||||
// do not continue if the click event came from a form submit
|
||||
|
||||
@@ -109,4 +109,9 @@
|
||||
<ion-datetime displayFormat="HH:mm:ss" [(ngModel)]="time"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>No displayFormat: {{noFormatDate}}</ion-label>
|
||||
<ion-datetime [(ngModel)]="noFormatDate"></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
</ion-content>
|
||||
|
||||
@@ -139,12 +139,45 @@ describe('DateTime', () => {
|
||||
|
||||
expect(columns[1].options[12].disabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('should always return a string', () => {
|
||||
datetime.monthValues = '6,7,8';
|
||||
datetime.dayValues = '01,02,03,04,05,06,08,09,10, 11, 12, 13, 31';
|
||||
datetime.yearValues = '2014,2015';
|
||||
|
||||
datetime.registerOnChange((value: string) => {
|
||||
expect(value).toEqual(jasmine.any(String));
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a string when setValue is passed an object', zoned(() => {
|
||||
const dateTimeData = {
|
||||
hour: {
|
||||
text: '12',
|
||||
value: 12,
|
||||
},
|
||||
minute: {
|
||||
text: '09',
|
||||
value: 9,
|
||||
},
|
||||
ampm: {
|
||||
text: 'pm',
|
||||
value: 'pm',
|
||||
},
|
||||
};
|
||||
|
||||
datetime.setValue(dateTimeData);
|
||||
|
||||
datetime.registerOnChange((value: string) => {
|
||||
expect(value).toEqual(jasmine.any(String));
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('writeValue', () => {
|
||||
|
||||
it('should updateText with default MMM D, YYYY when no displayFormat or pickerFormat', zoned(() => {
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.writeValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
expect(datetime._text).toEqual('Dec 15, 1994');
|
||||
@@ -152,7 +185,7 @@ describe('DateTime', () => {
|
||||
|
||||
it('should updateText with pickerFormat when no displayFormat', zoned(() => {
|
||||
datetime.pickerFormat = 'YYYY';
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.writeValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
expect(datetime._text).toEqual('1994');
|
||||
@@ -160,7 +193,7 @@ describe('DateTime', () => {
|
||||
|
||||
it('should updateText with displayFormat when no pickerFormat', zoned(() => {
|
||||
datetime.displayFormat = 'YYYY';
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.writeValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
expect(datetime._text).toEqual('1994');
|
||||
@@ -172,7 +205,7 @@ describe('DateTime', () => {
|
||||
|
||||
it('should generate with default MMM D, YYYY when no displayFormat or pickerFormat', zoned(() => {
|
||||
datetime.monthShortNames = customLocale.monthShortNames;
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.setValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
datetime.generate();
|
||||
@@ -186,7 +219,7 @@ describe('DateTime', () => {
|
||||
|
||||
it('should generate with only displayFormat', zoned(() => {
|
||||
datetime.monthShortNames = customLocale.monthShortNames;
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.displayFormat = 'YYYY';
|
||||
datetime.setValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
@@ -199,7 +232,7 @@ describe('DateTime', () => {
|
||||
|
||||
it('should generate with only pickerFormat', zoned(() => {
|
||||
datetime.monthShortNames = customLocale.monthShortNames;
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.pickerFormat = 'YYYY';
|
||||
datetime.setValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
@@ -212,7 +245,7 @@ describe('DateTime', () => {
|
||||
|
||||
it('should generate with custom locale short month names from input property', zoned(() => {
|
||||
datetime.monthShortNames = customLocale.monthShortNames;
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.pickerFormat = 'MMM YYYY';
|
||||
datetime.setValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
@@ -227,7 +260,7 @@ describe('DateTime', () => {
|
||||
|
||||
it('should generate with custom locale full month names from input property', zoned(() => {
|
||||
datetime.monthNames = customLocale.monthNames;
|
||||
datetime.ngAfterViewInit();
|
||||
datetime.ngAfterContentInit();
|
||||
datetime.pickerFormat = 'MMMM YYYY';
|
||||
datetime.setValue('1994-12-15T13:47:20.789Z');
|
||||
|
||||
|
||||
10
src/components/input/test/basic-form/app/app.component.ts
Normal file
10
src/components/input/test/basic-form/app/app.component.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { RootPage } from '../pages/root-page/root-page';
|
||||
|
||||
@Component({
|
||||
template: '<ion-nav [root]="root"></ion-nav>'
|
||||
})
|
||||
export class AppComponent {
|
||||
root = RootPage;
|
||||
}
|
||||
19
src/components/input/test/basic-form/app/app.module.ts
Normal file
19
src/components/input/test/basic-form/app/app.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { IonicApp, IonicModule } from '../../../../..';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { RootPageModule } from '../pages/root-page/root-page.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
IonicModule.forRoot(AppComponent),
|
||||
RootPageModule
|
||||
],
|
||||
bootstrap: [IonicApp]
|
||||
})
|
||||
export class AppModule {}
|
||||
5
src/components/input/test/basic-form/app/main.ts
Normal file
5
src/components/input/test/basic-form/app/main.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
@@ -0,0 +1,59 @@
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>Basic Form</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<form>
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input type="text"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Password</ion-label>
|
||||
<ion-input type="pass"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input type="text"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Password</ion-label>
|
||||
<ion-input type="pass"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input type="text"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Password</ion-label>
|
||||
<ion-input type="pass"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input type="text"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Password</ion-label>
|
||||
<ion-input type="pass"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input type="text"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Password</ion-label>
|
||||
<ion-input type="pass"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input type="text"></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Password</ion-label>
|
||||
<ion-input type="pass"></ion-input>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</form>
|
||||
</ion-content>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { IonicPageModule } from '../../../../../..';
|
||||
|
||||
import { RootPage } from './root-page';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
RootPage,
|
||||
],
|
||||
imports: [
|
||||
IonicPageModule.forChild(RootPage)
|
||||
]
|
||||
})
|
||||
export class RootPageModule {}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'root-page.html'
|
||||
})
|
||||
export class RootPage {
|
||||
myParam = '';
|
||||
minValue = 8;
|
||||
maxValue = 12;
|
||||
stepValue = 2;
|
||||
|
||||
myValues = {
|
||||
value1: 'Dynamic Input',
|
||||
value2: 'Dynamic Textarea'
|
||||
};
|
||||
|
||||
toggleValues() {
|
||||
this.minValue === 8 ? this.minValue = 4 : this.minValue = 8;
|
||||
this.maxValue === 12 ? this.maxValue = 20 : this.maxValue = 12;
|
||||
this.stepValue === 2 ? this.stepValue = 4 : this.stepValue = 2;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,14 @@
|
||||
// iOS Label
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Text color of the label by an input, select, or datetime
|
||||
$label-ios-text-color: unset !default;
|
||||
|
||||
/// @prop - Text color of the stacked/floating label when it is focused
|
||||
$label-ios-text-color-focused: unset !default;
|
||||
|
||||
/// @prop - Margin of the label
|
||||
$label-ios-margin: $item-ios-padding-top ($item-ios-padding-right / 2) $item-ios-padding-bottom 0 !default;
|
||||
$label-ios-margin: $item-ios-padding-top ($item-ios-padding-right / 2) $item-ios-padding-bottom 0 !default;
|
||||
|
||||
|
||||
// iOS Default Label
|
||||
@@ -18,6 +24,16 @@ $label-ios-margin: $item-ios-padding-top ($item-ios-padding-right /
|
||||
// iOS Default Label Inside An Input/Select Item
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-input .label-ios,
|
||||
.item-select .label-ios,
|
||||
.item-datetime .label-ios {
|
||||
color: $label-ios-text-color;
|
||||
}
|
||||
|
||||
|
||||
// iOS Input By Label
|
||||
// --------------------------------------------------
|
||||
|
||||
.label-ios + ion-input .text-input,
|
||||
.label-ios + ion-textarea .text-input,
|
||||
.label-ios + .input + .cloned-input {
|
||||
@@ -59,6 +75,11 @@ $label-ios-margin: $item-ios-padding-top ($item-ios-padding-right /
|
||||
margin-bottom: $item-ios-padding-media-bottom - 2;
|
||||
}
|
||||
|
||||
.input-has-focus .label-ios[stacked],
|
||||
.input-has-focus .label-ios[floating] {
|
||||
color: $label-ios-text-color-focused;
|
||||
}
|
||||
|
||||
|
||||
// Generate iOS Label colors
|
||||
// --------------------------------------------------
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
// Material Design Label
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Text color of the label
|
||||
/// @prop - Text color of the label by an input, select, or datetime
|
||||
$label-md-text-color: #999 !default;
|
||||
|
||||
/// @prop - Text color of the label when it has focused
|
||||
/// @prop - Text color of the stacked/floating label when it is focused
|
||||
$label-md-text-color-focused: color($colors-md, primary) !default;
|
||||
|
||||
/// @prop - Margin of the label
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
// Windows Label
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Text color of the label
|
||||
/// @prop - Text color of the label by an input, select, or datetime
|
||||
$label-wp-text-color: #999 !default;
|
||||
|
||||
/// @prop - Text color of the label when it has focused
|
||||
/// @prop - Text color of the stacked/floating label when it is focused
|
||||
$label-wp-text-color-focused: color($colors-wp, primary) !default;
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, Optional, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { AfterContentInit, ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, Optional, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { clamp, isTrueProperty } from '../../util/util';
|
||||
@@ -104,7 +104,7 @@ import { UIEventManager } from '../../gestures/ui-event-manager';
|
||||
providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: Range, multi: true } ],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Range extends BaseInput<any> implements AfterViewInit, ControlValueAccessor, OnDestroy {
|
||||
export class Range extends BaseInput<any> implements AfterContentInit, ControlValueAccessor, OnDestroy {
|
||||
|
||||
_dual: boolean;
|
||||
_pin: boolean;
|
||||
@@ -266,7 +266,7 @@ export class Range extends BaseInput<any> implements AfterViewInit, ControlValue
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
ngAfterContentInit() {
|
||||
this._initialize();
|
||||
|
||||
// add touchstart/mousedown listeners
|
||||
|
||||
@@ -172,15 +172,6 @@ export class Searchbar extends BaseInput<string> {
|
||||
|
||||
@ViewChild('cancelButton', {read: ElementRef}) _cancelButton: ElementRef;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* After View Checked position the elements
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
this._initialize();
|
||||
this.positionElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* On Initialization check for attributes
|
||||
@@ -196,11 +187,9 @@ export class Searchbar extends BaseInput<string> {
|
||||
* @hidden
|
||||
*/
|
||||
_inputUpdated() {
|
||||
if (this._searchbarInput) {
|
||||
var ele = this._searchbarInput.nativeElement;
|
||||
if (ele) {
|
||||
ele.value = this.value;
|
||||
}
|
||||
const ele = this._searchbarInput.nativeElement;
|
||||
if (ele) {
|
||||
ele.value = this.value;
|
||||
}
|
||||
this.positionElements();
|
||||
}
|
||||
@@ -229,9 +218,6 @@ export class Searchbar extends BaseInput<string> {
|
||||
}
|
||||
|
||||
positionPlaceholder() {
|
||||
if (!this._searchbarInput || !this._searchbarIcon) {
|
||||
return;
|
||||
}
|
||||
const inputEle = this._searchbarInput.nativeElement;
|
||||
const iconEle = this._searchbarIcon.nativeElement;
|
||||
|
||||
@@ -265,9 +251,6 @@ export class Searchbar extends BaseInput<string> {
|
||||
* Show the iOS Cancel button on focus, hide it offscreen otherwise
|
||||
*/
|
||||
positionCancelButton() {
|
||||
if (!this._cancelButton || !this._cancelButton.nativeElement) {
|
||||
return;
|
||||
}
|
||||
const showShowCancel = this._isFocus;
|
||||
if (showShowCancel !== this._isCancelVisible) {
|
||||
var cancelStyleEle = this._cancelButton.nativeElement;
|
||||
|
||||
@@ -44,4 +44,10 @@
|
||||
<p padding>
|
||||
<button ion-button block (click)="changeValue()">Change Value</button>
|
||||
</p>
|
||||
|
||||
<button ion-button (click)="activeTab = 'a'">Error Please</button>
|
||||
<div *ngIf="activeTab == 'a'">
|
||||
<ion-searchbar animated="true" [showCancelButton]="true"></ion-searchbar>
|
||||
</div>
|
||||
|
||||
</ion-content>
|
||||
@@ -13,6 +13,7 @@ export class RootPage {
|
||||
isAutocorrect: string = 'on';
|
||||
isAutocomplete: string = 'on';
|
||||
isSpellcheck: boolean = true;
|
||||
activeTab = '';
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ContentChildren, Directive, ElementRef, Optional, QueryList, Renderer } from '@angular/core';
|
||||
import { AfterContentInit, ContentChildren, Directive, ElementRef, Optional, QueryList, Renderer } from '@angular/core';
|
||||
import { NgControl } from '@angular/forms';
|
||||
|
||||
import { Config } from '../../config/config';
|
||||
import { BaseInput } from '../../util/base-input';
|
||||
import { assert } from '../../util/util';
|
||||
import { SegmentButton } from './segment-button';
|
||||
|
||||
/**
|
||||
@@ -67,7 +68,7 @@ import { SegmentButton } from './segment-button';
|
||||
'[class.segment-disabled]': '_disabled'
|
||||
}
|
||||
})
|
||||
export class Segment extends BaseInput<string> {
|
||||
export class Segment extends BaseInput<string> implements AfterContentInit {
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
@@ -86,7 +87,7 @@ export class Segment extends BaseInput<string> {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
ngAfterContentInit() {
|
||||
this._initialize();
|
||||
this._buttons.forEach(button => {
|
||||
button.ionSelect.subscribe((selectedButton: any) => this.value = selectedButton.value);
|
||||
@@ -98,12 +99,14 @@ export class Segment extends BaseInput<string> {
|
||||
* Write a new value to the element.
|
||||
*/
|
||||
_inputUpdated() {
|
||||
if (this._buttons) {
|
||||
var buttons = this._buttons.toArray();
|
||||
var value = this.value;
|
||||
for (var button of buttons) {
|
||||
button.isActive = (button.value === value);
|
||||
}
|
||||
if (!this._buttons) {
|
||||
assert(false, 'buttons are undefined');
|
||||
return;
|
||||
}
|
||||
const buttons = this._buttons.toArray();
|
||||
const value = this.value;
|
||||
for (var button of buttons) {
|
||||
button.isActive = (button.value === value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, Component, ContentChildren, ElementRef, EventEmitter, Input, HostListener, OnDestroy, Optional, Output, Renderer, QueryList, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, ContentChildren, ElementRef, EventEmitter, Input, HostListener, OnDestroy, Optional, Output, Renderer, QueryList, ViewEncapsulation } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ActionSheet } from '../action-sheet/action-sheet';
|
||||
@@ -9,7 +9,7 @@ import { Config } from '../../config/config';
|
||||
import { DeepLinker } from '../../navigation/deep-linker';
|
||||
import { Form } from '../../util/form';
|
||||
import { BaseInput } from '../../util/base-input';
|
||||
import { isCheckedProperty, isTrueProperty, deepCopy, deepEqual } from '../../util/util';
|
||||
import { isCheckedProperty, isTrueProperty, deepCopy, deepEqual, assert } from '../../util/util';
|
||||
import { Item } from '../item/item';
|
||||
import { NavController } from '../../navigation/nav-controller';
|
||||
import { Option } from '../option/option';
|
||||
@@ -147,13 +147,12 @@ import { SelectPopover, SelectPopoverOption } from './select-popover-component';
|
||||
providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: Select, multi: true } ],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class Select extends BaseInput<string[]|string> implements AfterViewInit, OnDestroy {
|
||||
export class Select extends BaseInput<any> implements OnDestroy {
|
||||
|
||||
_multi: boolean = false;
|
||||
_options: QueryList<Option>;
|
||||
_texts: string[] = [];
|
||||
_text: string = '';
|
||||
_values: string[] = [];
|
||||
|
||||
/**
|
||||
* @input {string} The text to display on the cancel button. Default: `Cancel`.
|
||||
@@ -222,6 +221,15 @@ export class Select extends BaseInput<string[]|string> implements AfterViewInit,
|
||||
this.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
getValues(): any[] {
|
||||
const values = Array.isArray(this._value) ? this._value : [this._value];
|
||||
assert(this._multi || values.length <= 1, 'single only can have one value');
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the select interface.
|
||||
*/
|
||||
@@ -394,8 +402,8 @@ export class Select extends BaseInput<string[]|string> implements AfterViewInit,
|
||||
@ContentChildren(Option)
|
||||
set options(val: QueryList<Option>) {
|
||||
this._options = val;
|
||||
|
||||
if (this._values.length === 0) {
|
||||
const values = this.getValues();
|
||||
if (values.length === 0) {
|
||||
// there are no values set at this point
|
||||
// so check to see who should be selected
|
||||
// we use writeValue() because we don't want to update ngModel
|
||||
@@ -409,17 +417,24 @@ export class Select extends BaseInput<string[]|string> implements AfterViewInit,
|
||||
return !deepEqual(this._value, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: REMOVE THIS
|
||||
* @hidden
|
||||
*/
|
||||
_inputChangeEvent(): any {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputUpdated() {
|
||||
this._texts.length = 0;
|
||||
this._values = Array.isArray(this._value) ? this._value : [this._value + ''];
|
||||
|
||||
if (this._options) {
|
||||
this._options.forEach(option => {
|
||||
// check this option if the option's value is in the values array
|
||||
option.selected = this._values.some(selectValue => {
|
||||
option.selected = this.getValues().some(selectValue => {
|
||||
return isCheckedProperty(selectValue, option.value);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
|
||||
import { Select } from '../select';
|
||||
import { mockApp, mockConfig, mockDeepLinker, mockElementRef, mockRenderer, mockItem, mockForm } from '../../../util/mock-providers';
|
||||
import { commonInputTest } from '../../../util/input-tester';
|
||||
// import { Select } from '../select';
|
||||
// import { mockApp, mockConfig, mockDeepLinker, mockElementRef, mockRenderer, mockItem, mockForm } from '../../../util/mock-providers';
|
||||
// import { commonInputTest } from '../../../util/input-tester';
|
||||
|
||||
describe('Select', () => {
|
||||
|
||||
it('should pass common test', () => {
|
||||
|
||||
const app = mockApp();
|
||||
const config = mockConfig();
|
||||
const deepLinker = mockDeepLinker();
|
||||
const elementRef = mockElementRef();
|
||||
const renderer = mockRenderer();
|
||||
const item: any = mockItem();
|
||||
const form = mockForm();
|
||||
const select = new Select(app, form, config, elementRef, renderer, item, null, deepLinker);
|
||||
// TODO: needs to be enabled in v4
|
||||
// const app = mockApp();
|
||||
// const config = mockConfig();
|
||||
// const deepLinker = mockDeepLinker();
|
||||
// const elementRef = mockElementRef();
|
||||
// const renderer = mockRenderer();
|
||||
// const item: any = mockItem();
|
||||
// const form = mockForm();
|
||||
// const select = new Select(app, form, config, elementRef, renderer, item, null, deepLinker);
|
||||
|
||||
commonInputTest(select, {
|
||||
defaultValue: [],
|
||||
corpus: [
|
||||
[['hola'], ['hola']],
|
||||
[null, []],
|
||||
['hola', 'hola'],
|
||||
[['hola', 'adios'], ['hola', 'adios']]
|
||||
]
|
||||
});
|
||||
// commonInputTest(select, {
|
||||
// defaultValue: [],
|
||||
// corpus: [
|
||||
// [['hola'], ['hola']],
|
||||
// [null, []],
|
||||
// ['hola', 'hola'],
|
||||
// [['hola', 'adios'], ['hola', 'adios']]
|
||||
// ]
|
||||
// });
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,146 +1,117 @@
|
||||
import { BooleanInputComponent, GestureDetail } from '../../util/interfaces';
|
||||
import { Component, h, Ionic, Listen, Prop, Watch } from '../index';
|
||||
import { Component, h, Ionic } from '../index';
|
||||
|
||||
|
||||
/**
|
||||
* @name Toolbar
|
||||
* @description
|
||||
* A Toolbar is a generic bar that is positioned above or below content.
|
||||
* Unlike a [Navbar](../../navbar/Navbar), a toolbar can be used as a subheader.
|
||||
* When toolbars are placed within an `<ion-header>` or `<ion-footer>`,
|
||||
* the toolbars stay fixed in their respective location. When placed within
|
||||
* `<ion-content>`, toolbars will scroll with the content.
|
||||
*
|
||||
*
|
||||
* ### Buttons in a Toolbar
|
||||
* Buttons placed in a toolbar should be placed inside of the `<ion-buttons>`
|
||||
* element. An exception to this is a [menuToggle](../../menu/MenuToggle) button.
|
||||
* It should not be placed inside of the `<ion-buttons>` element. Both the
|
||||
* `<ion-buttons>` element and the `menuToggle` can be positioned inside of the
|
||||
* toolbar using different properties. The below chart has a description of each
|
||||
* property.
|
||||
*
|
||||
* | Property | Description |
|
||||
* |-------------|-----------------------------------------------------------------------------------------------------------------------|
|
||||
* | `start` | Positions element to the left of the content in `ios` mode, and directly to the right in `md` and `wp` mode. |
|
||||
* | `end` | Positions element to the right of the content in `ios` mode, and to the far right in `md` and `wp` mode. |
|
||||
* | `left` | Positions element to the left of all other elements. |
|
||||
* | `right` | Positions element to the right of all other elements. |
|
||||
*
|
||||
*
|
||||
* ### Header / Footer Box Shadow and Border
|
||||
* In `md` mode, the `<ion-header>` will receive a box-shadow on the bottom, and the
|
||||
* `<ion-footer>` will receive a box-shadow on the top. In `ios` mode, the `<ion-header>`
|
||||
* will receive a border on the bottom, and the `<ion-footer>` will receive a border on the
|
||||
* top. Both the `md` box-shadow and the `ios` border can be removed by adding the `no-border`
|
||||
* attribute to the element.
|
||||
*
|
||||
* ```html
|
||||
* <ion-header no-border>
|
||||
* <ion-toolbar>
|
||||
* <ion-title>Header</ion-title>
|
||||
* </ion-toolbar>
|
||||
* </ion-header>
|
||||
*
|
||||
* <ion-content>
|
||||
* </ion-content>
|
||||
*
|
||||
* <ion-footer no-border>
|
||||
* <ion-toolbar>
|
||||
* <ion-title>Footer</ion-title>
|
||||
* </ion-toolbar>
|
||||
* </ion-footer>
|
||||
* ```
|
||||
*
|
||||
* @usage
|
||||
*
|
||||
* ```html
|
||||
*
|
||||
* <ion-header no-border>
|
||||
*
|
||||
* <ion-toolbar>
|
||||
* <ion-title>My Toolbar Title</ion-title>
|
||||
* </ion-toolbar>
|
||||
*
|
||||
* <ion-toolbar>
|
||||
* <ion-title>I'm a subheader</ion-title>
|
||||
* </ion-toolbar>
|
||||
*
|
||||
* <ion-header>
|
||||
*
|
||||
*
|
||||
* <ion-content>
|
||||
*
|
||||
* <ion-toolbar>
|
||||
* <ion-title>Scrolls with the content</ion-title>
|
||||
* </ion-toolbar>
|
||||
*
|
||||
* </ion-content>
|
||||
*
|
||||
*
|
||||
* <ion-footer no-border>
|
||||
*
|
||||
* <ion-toolbar>
|
||||
* <ion-title>I'm a footer</ion-title>
|
||||
* </ion-toolbar>
|
||||
*
|
||||
* </ion-footer>
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/demos/src/toolbar/
|
||||
* @see {@link ../../navbar/Navbar/ Navbar API Docs}
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-toggle',
|
||||
tag: 'ion-toolbar',
|
||||
styleUrls: {
|
||||
ios: 'toggle.ios.scss',
|
||||
md: 'toggle.md.scss',
|
||||
wp: 'toggle.wp.scss'
|
||||
ios: 'toolbar.ios.scss',
|
||||
md: 'toolbar.md.scss',
|
||||
wp: 'toolbar.wp.scss'
|
||||
}
|
||||
})
|
||||
export class Toggle implements BooleanInputComponent {
|
||||
activated: boolean;
|
||||
hasFocus: boolean;
|
||||
id: string;
|
||||
labelId: string;
|
||||
startX: number;
|
||||
export class Toolbar {
|
||||
private sbPadding: boolean;
|
||||
|
||||
@Prop() checked: boolean;
|
||||
@Prop() disabled: boolean;
|
||||
@Prop() value: string;
|
||||
|
||||
|
||||
@Watch('checked')
|
||||
changed(val: boolean) {
|
||||
Ionic.emit(this, 'ionChange', { detail: { checked: val } });
|
||||
constructor() {
|
||||
this.sbPadding = Ionic.config.getBoolean('statusbarPadding');
|
||||
}
|
||||
|
||||
|
||||
canStart() {
|
||||
return !this.disabled;
|
||||
}
|
||||
|
||||
|
||||
onDragStart(detail: GestureDetail) {
|
||||
this.startX = detail.startX;
|
||||
this.fireFocus();
|
||||
}
|
||||
|
||||
|
||||
onDragMove(detail: GestureDetail) {
|
||||
if (this.checked) {
|
||||
if (detail.currentX + 15 < this.startX) {
|
||||
this.checked = false;
|
||||
this.activated = true;
|
||||
this.startX = detail.currentX;
|
||||
}
|
||||
|
||||
} else if (detail.currentX - 15 > this.startX) {
|
||||
this.checked = true;
|
||||
this.activated = (detail.currentX < this.startX + 5);
|
||||
this.startX = detail.currentX;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onDragEnd(detail: GestureDetail) {
|
||||
if (this.checked) {
|
||||
if (detail.startX + 4 > detail.currentX) {
|
||||
this.checked = false;
|
||||
}
|
||||
|
||||
} else if (detail.startX - 4 < detail.currentX) {
|
||||
this.checked = true;
|
||||
}
|
||||
|
||||
this.activated = false;
|
||||
this.fireBlur();
|
||||
this.startX = null;
|
||||
}
|
||||
|
||||
|
||||
@Listen('keydown.space')
|
||||
onSpace(ev: KeyboardEvent) {
|
||||
this.toggle();
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
toggle() {
|
||||
if (!this.disabled) {
|
||||
this.checked = !this.checked;
|
||||
this.fireFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fireFocus() {
|
||||
if (!this.hasFocus) {
|
||||
this.hasFocus = true;
|
||||
Ionic.emit(this, 'ionFocus');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fireBlur() {
|
||||
if (this.hasFocus) {
|
||||
this.hasFocus = false;
|
||||
Ionic.emit(this, 'ionBlur');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return h(this,
|
||||
h('ion-gesture', Ionic.theme(this, 'toggle', {
|
||||
class: {
|
||||
'toggle-activated': this.activated,
|
||||
'toggle-checked': this.checked,
|
||||
'toggle-disabled': this.disabled,
|
||||
},
|
||||
props: {
|
||||
'canStart': this.canStart.bind(this),
|
||||
'onStart': this.onDragStart.bind(this),
|
||||
'onMove': this.onDragMove.bind(this),
|
||||
'onEnd': this.onDragEnd.bind(this),
|
||||
'onPress': this.toggle.bind(this),
|
||||
'gestureName': 'toggle',
|
||||
'gesturePriority': 30,
|
||||
'type': 'pan,press',
|
||||
'direction': 'x',
|
||||
'threshold': 20,
|
||||
'listenOn': 'parent'
|
||||
}
|
||||
}),
|
||||
[
|
||||
h('div.toggle-icon',
|
||||
h('div.toggle-inner')
|
||||
),
|
||||
h('div.toggle-cover', {
|
||||
attrs: {
|
||||
'id': this.id,
|
||||
'aria-checked': this.checked ? 'true' : false,
|
||||
'aria-disabled': this.disabled ? 'true' : false,
|
||||
'aria-labelledby': this.labelId,
|
||||
'role': 'checkbox',
|
||||
'tabindex': 0
|
||||
}
|
||||
})
|
||||
]
|
||||
)
|
||||
return h(this, { class: { 'statusbar-padding': this.sbPadding } },
|
||||
h('div', Ionic.theme(this, 'toolbar'), [
|
||||
h('div', Ionic.theme(this, 'toolbar-background')),
|
||||
h('div', Ionic.theme(this, 'toolbar-content'),
|
||||
h('slot')
|
||||
),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ElementRef, EventEmitter, Input, NgZone, Output, Renderer } from '@angular/core';
|
||||
import { AfterContentInit, ElementRef, EventEmitter, Input, NgZone, Output, Renderer } from '@angular/core';
|
||||
import { ControlValueAccessor } from '@angular/forms';
|
||||
import { NgControl } from '@angular/forms';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Form } from './form';
|
||||
import { TimeoutDebouncer } from './debouncer';
|
||||
|
||||
|
||||
export interface CommonInput<T> extends ControlValueAccessor {
|
||||
export interface CommonInput<T> extends ControlValueAccessor, AfterContentInit {
|
||||
|
||||
id: string;
|
||||
disabled: boolean;
|
||||
@@ -38,6 +38,8 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
_disabled: boolean = false;
|
||||
_debouncer: TimeoutDebouncer = new TimeoutDebouncer(0);
|
||||
_init: boolean = false;
|
||||
_initModel: boolean = false;
|
||||
|
||||
id: string;
|
||||
|
||||
/**
|
||||
@@ -122,8 +124,14 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
*/
|
||||
writeValue(val: any) {
|
||||
if (this._writeValue(val)) {
|
||||
this._fireIonChange();
|
||||
if (this._initModel) {
|
||||
this._fireIonChange();
|
||||
} else if (this._init) {
|
||||
// ngModel fires the first time too late, we need to skip the first ngModel update
|
||||
this._initModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,9 +143,12 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
if (isUndefined(val)) {
|
||||
return false;
|
||||
}
|
||||
const normalized = (val === null)
|
||||
? deepCopy(this._defaultValue)
|
||||
: this._inputNormalize(val);
|
||||
let normalized;
|
||||
if (val === null) {
|
||||
normalized = deepCopy(this._defaultValue);
|
||||
} else {
|
||||
normalized = this._inputNormalize(val);
|
||||
}
|
||||
|
||||
const notUpdate = isUndefined(normalized) || !this._inputShouldChange(normalized);
|
||||
if (notUpdate) {
|
||||
@@ -160,7 +171,8 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
if (this._init) {
|
||||
this._debouncer.debounce(() => {
|
||||
assert(NgZone.isInAngularZone(), 'IonChange: should be zoned');
|
||||
this.ionChange.emit(this);
|
||||
this.ionChange.emit(this._inputChangeEvent());
|
||||
this._initModel = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -227,7 +239,7 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
* @hidden
|
||||
*/
|
||||
private onChange() {
|
||||
this._onChanged && this._onChanged(this._value);
|
||||
this._onChanged && this._onChanged(this._inputNgModelEvent());
|
||||
this._onTouched && this._onTouched();
|
||||
}
|
||||
|
||||
@@ -259,7 +271,7 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
ngAfterContentInit() {
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
@@ -276,7 +288,7 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initFocus() {}
|
||||
initFocus() { }
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
@@ -292,6 +304,21 @@ export class BaseInput<T> extends Ion implements CommonInput<T> {
|
||||
return this._value !== val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputChangeEvent(): any {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
_inputNgModelEvent(): any {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
|
||||
@@ -55,7 +55,7 @@ export function commonInputTest<T>(input: BaseInput<T>, config: TestConfig) {
|
||||
const zone = new NgZone(true);
|
||||
zone.run(() => {
|
||||
testInput(input, config, false);
|
||||
input.ngAfterViewInit();
|
||||
input.ngAfterContentInit();
|
||||
testInput(input, config, true);
|
||||
input.ngOnDestroy();
|
||||
assert(!input._init, 'input was not destroyed correctly');
|
||||
|
||||
Reference in New Issue
Block a user