Infinite Loop on ngModelChange - ionic-framework

I have a list containing users, and i have an item that i want it to redirect me to a modal page.
My problem is as soon as my page is available, i get an infinite pop up of my modal, instead of going on my ion-select and select the item so i can get the pop up.
html
<ion-select interface="popover" (ngModelChange)="onChange($event)">
<ion-option>Bacon</ion-option>
<ion-option [value]="openConfig()"></ion-option>Black Olives</ion-option>
<ion-option>Extra Cheese</ion-option>
<ion-option>Mushrooms</ion-option>
<ion-option>Pepperoni</ion-option>
<ion-option>Sausage</ion-option>
</ion-select>
ts
onChange(value: any) {
if (value === 'openConfig') {
this.openConfig()
}
}
openConfig() {
this.modalCtrl.create('ConfigModal').present;
console.log('heeey')
}

Setting the [value] in the template is actually calling your openConfig() function, creating an infinite loop on the page load. To do what you're trying to do here you don't need to reference your openConfig function in the template at all.
ion-select uses the output event ionChange, which outputs the value of the ion-option selected. So the normal way to do this in Ionic 3 would be something like this:
html:
<ion-content padding>
<ion-select interface="popover" [ngModel]="option" (ionChange)="onChange($event)">
<ion-option>Bacon</ion-option>
<ion-option>Black Olives</ion-option>
<ion-option>Extra Cheese</ion-option>
<ion-option>Mushrooms</ion-option>
<ion-option>Pepperoni</ion-option>
<ion-option>Sausage</ion-option>
</ion-select>
</ion-content>
js:
onChange(value: any) {
if (value === 'Black Olives') {
this.openConfig()
}
}
openConfig() {
this.modalCtrl.create(ConfigModal).present();
}
Note that the value of an ion-option is simply the text of the label. So that's what you should check for in your "onChange" function.
You have a couple of other unrelated typos, but I believe this addresses the question of your infinite loop. Hope this helps!

Related

Ionic 4 infinite-scroll initial scroll position

I'm trying to make a dual infinite scroll case when new items are added as the user scrolls either all the way up or all the way down.
<ion-infinite-scroll (ionInfinite)="scrolledUp($event)" position="top">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
<ion-list>
<ion-item *ngFor="let item of items">
<!-- item content -->
<ion-item>
</ion-list>
<ion-infinite-scroll (ionInfinite)="scrolledDown($event)" position="bottom">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
The problem is that the list opens initially at the bottom. Of course I can scrollIntoView the first item on ionViewDidEnter but it wouldn't look nice and would confuse the users.
I noticed it's the one with position=top that is responsible for the initial position at the bottom.
Is there a way to set the initial scroll position for the ion-infinite-scroll without having to explicitly scroll the list to top?
As I initially suspected the infinite scroll component does force a scroll to bottom if position is set to top. It occurs in the componentDidLoad method as shown below:
async componentDidLoad() {
const contentEl = this.el.closest('ion-content');
if (contentEl) {
await contentEl.componentOnReady();
this.scrollEl = await contentEl.getScrollElement();
}
this.thresholdChanged();
this.disabledChanged();
if (this.position === 'top') {
writeTask(() => {
if (this.scrollEl) {
this.scrollEl.scrollTop = this.scrollEl.scrollHeight - this.scrollEl.clientHeight;
}
});
}
}
As the component provides no means to intervene in this behavior and without an option to extend or otherwise customize the default stencil components, I turned to a somewhat hacky way to achieve the desired behavior.
Therefore, you first need to remove the position=top part from the ion-infinite-scroll declaration in your view so that the scroll part of the above code won't trigger. You would also provide a name for the ion-infinite-scroll so that it can be referenced from your page class.
<ion-infinite-scroll #top (ionInfinite)="scrolledUp($event)">
Then you make a reference to #top above in your page.
#ViewChild('top') topScroller;
Then in the ionViewDidEnter life cycle hook, you can set the position for the scroller as top.
setTimeout(() => {
this.topScroller.position = 'top';
}, 200);
It has to be in a time out because the topScroller variable is still not assigned when ionViewDidEnter is called and the next life cycle hook is ionViewWillLeave which won't be that useful for our purpose.

Ionic-3 ion-input maxlength attribute not working

I have tried to add ion-input for maxlength , max attribute but it's not working as per expectation.
<ion-input type="number" placeholder="*" maxlength="1"></ion-input>
<ion-input type="number" placeholder="*" max="1"></ion-input>
Anyone knows the solution for the same?
Thanks
According to this post: maxlength ignored for input type="number" in Chrome
Maxlength doesn't work on input type="number"
One alternative is suggested here: https://github.com/ionic-team/ionic/issues/7072
where dilhan119 suggests using type="tel"
A robust solution is to use a form validator, which will prevent form submission (and can show the user an error): https://www.joshmorony.com/advanced-forms-validation-in-ionic-2/
HTML:
<ion-textarea [(ngModel)]=“text” (ionChange)="textareaMaxLengthValidation()"></ion-textarea>
<ion-input type="text" [(ngModel)]=“text” (ionChange)="textareaMaxLengthValidation()"></ion-input>
TS:
textareaMaxLengthValidation() {
    if (text.length > 50) {
   text= text.slice(0, 50);
    }
I found my way out you can use below my code. great this about it is you can keep input type number so android will show keyboard of your desire
put this code in your form builder
phone: ['', [Validators.required, this.isValidNumber.bind(this)]]
in your ts file add below method
isValidNumber(fieldControl: FormControl) {
if(this.formBuilderGroup) {
return fieldControl.value.toString().length < 10 ? null : {
NotEqual: true
};
}
}
in above code change formBuilderGroup to whatever your form builder group name it
is. change 10 to whatever you prefer length
I stumbled accross this issue recently and I ended up using a solution similar to the one by #Karthick Chandra Sekar, only difference is I used the standard HTML keypress event instead, since ionChange lets the event go through and causes the character to actually be displayed briefly before it gets rid of. Using keypress allows to prevent the event before it actually takes effect. So, this is how it worked out for me:
Html file:
<ion-input type="number" (keypress)="limitInputLength($event)"></ion-input>
TS code:
limitInputLength($event, maxLength=2) {
if($event.target.value.length>=maxLength) {
$event.preventDefault();
return;
}
}

Ionic 2 Updating Button Text and Event on Click

Sorry if this sounds very obvious but I am new to Ionic 2 / Angular 2. Upon submitting a form, I need to update the button text and click event, ie:
first click on button = submit form + update button text to "Next"
second click on button = trigger goToNext()
I managed to update the button text but not update the click event (to goToNext() ).
.html
<form (ngSubmit)="logForm(i)">
<ion-item>
<ion-input type="text" [(ngModel)]="form.userinput[i]" name="userinput[i]"></ion-input>
</ion-item>
<button ion-button block type="submit" (click)="setNext($event.target, 'Next')">Check</button>
</form>
.ts
setNext(element, text){
element.textContent = 'Next';
}
goToNext(){
// go to Next Page
}
Ideally you change your design a bit to keep a variable that stores state of your 'Controller'. e.g. stores PageNumber. and then behave differently based on what page you are on. So I suggest change design a bit.
But to answer your current question without major change, you can bind the handler dynamically the same way you bind the text. then in the first handler, change the handler for the next click. the default values for handler and text will decide which one is going the be used initially
handler = this.setNext;
text = 'first text';
setNext(){
alert('handler1 called');
this.handler = this.goToNext;
this.text = 'other text';
}
goToNext(){
alert('second called');
// go to Next Page
}
and in your html you go like
<button ion-button block type="submit" (click)="handler()">{{text}}</button>
You can use n00b answer or something like this:
in html file:
<button ion-button block type="submit" (click)="check()">{{btn_txt}}</button>
in ts file:
btn_txt = 'Check';
check() {
if (this.btn_txt == 'Check') {
//do some logic
this.btn_txt = 'Next';
} else {
console.log('go to next page');
}
}

Ionic 3: executing a funtion with toggle button

I have a toggle button and two functions. I want one of the functions to be called when the toggle button is on, and the other to be called when the toggle button is in off state. How can I do that?
This is really easy actually if you make use of angular forms. You just need to subscribe to the toggle control that we define in the FormGroup. You can subscribe to the value of the entire form or just the value of an individual control as I've done below. Following the angular documentation, I usually use a separate function to construct my FormGroup and then call that function, buildForm() inside the constructor.
buildForm(): void {
this.myCoolForm = this._fb.group({
superCoolToggle: ['']
})
this.form.controls['superCoolToggle'].valueChanges.subscribe( value => {
if (value === true) {
// something here
} else if (value === false) {
// something different here
}
})
}
<form [formGroup]="myCoolForm">
<ion-item>
<ion-label> Sam</ion-label>
<ion-toggle formControlName="superCoolToggle"></ion-toggle>
</ion-item>
</form>

collection-repeat with angular component, what is happening?

I'm trying to use collection-repeat to display an angular component for each object in an array. I pass each object as parameter to an angular component but when I try to access the object in my component's controller I get undefined.
<ion-content>
<ion-list>
<ion-item
collection-repeat="user in users"
item-width="100%"
item-height="90px">
{{user}} //renders my user data correctly instantly
<usser user="user"></user>
</ion-item>
</ion-list>
</ion-content>
My component
angular
.module('app')
.component('user', {
templateUrl: 'components/user.html',
scope: true,
bindings: {
user: '<'
},
controller: function() {
console.log(self.user) //prints undefined
}
})
I've tried wrapping the console.log in a $timeout without success
Printing self displays {user: undefined} in my chrome console, but if I expand the object I can see that user contains the correct data (only for the some of the items)
Accessing self.user doesn't work
EDIT: I can't really understand what's going on..
controller: function() {
console.log(self.user) //prints undefined
setTimeout(function() {
console.log(self.user) // prints my data
}, 2000)
}
What am I doing wrong?
Thanks in advance!
Lost 3 hours to figure out this
This is a known issue at the moment with collection repeat. A fix
would require a refactor of collection repeat, which would be too big
of a change the moment.
Always check the issues on Github, [V] lesson learned