Ionic 2: View does not update based on model change - ionic-framework

I have a very simple page with a couple of controls.
My issue is that the page does not pickup changes to the model when the icon in upper right corner is clicked. This toggles the showFilterPane variable, which again should show or hide a div based on *ngIf="showFilterPane".
I have another page just like this one working, and I can not figure out why this isn't.
Any tips?
(I've tried using the ChangeDetectorRef.detectChanges(); which works, but then the rangeslider will not work. The draggable point doesn't update, or does not move to where you tap.)
The page:
<ion-header>
<ion-navbar>
<ion-title>MY AO</ion-title>
<ion-buttons end>
<button ion-button (click)="toggleFilterPane()" icon-only>
<ion-icon name="options"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content>
<div class="container">
<div class="left">
<div *ngIf="isSearching" class="spinner-container">
<ion-spinner></ion-spinner>
</div>
<!-- put content here -->
</div>
<div class="right" *ngIf="showFilterPane">
<ion-list inset>
<ion-list-header>BANA</ion-list-header>
<ion-item>
<ion-select multiple="true" [(ngModel)]="woTrackFilter">
<ion-option>1</ion-option>
<ion-option>2</ion-option>
<ion-option>3</ion-option>
<ion-option>4</ion-option>
<ion-option>5</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list inset>
<ion-list-header>TEKNIKSLAG</ion-list-header>
<ion-item>
<ion-select multiple="true" [(ngModel)]="woDisciplineFilter">
<ion-option>Signal</ion-option>
<ion-option>Bana</ion-option>
<ion-option>EL</ion-option>
<ion-option>Tele</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list inset>
<ion-list-header>DAGAR</ion-list-header>
<ion-item>
<ion-range min="10" max="80" step="4" [(ngModel)]="woDaysFilter">
<ion-label range-left>10</ion-label>
<ion-label range-right>80</ion-label>>
</ion-range>
</ion-item>
</ion-list>
<button ion-button (click)="doSearch()">Search</button>
</div>
</div>
</ion-content>
The component:
import { Component } from '#angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { WorkOrderDashboardPage } from "../work-order-dashboard/work-order-dashboard";
#Component({
selector: 'page-work-order-list',
templateUrl: 'work-order-list.html'
})
export class WorkOrderListPage {
private isSearching: boolean = false;
private showFilterPane: boolean=false;
private woTrackFilter: string[];
private woDisciplineFilter: string[];
private woDaysFilter: number;
constructor(public navCtrl: NavController, public navParams: NavParams) {
// Initialize storage providers here
}
ionViewDidLoad() {
console.log('ionViewDidLoad WorkOrderListPage');
}
toggleFilterPane(): void {
this.showFilterPane = !this.showFilterPane;
}
viewWorkOrder(event, workOrder): void {
this.navCtrl.push(WorkOrderDashboardPage, { workOrder: workOrder });
}
doSearch(): void {
console.log(this.woTrackFilter);
console.log(this.woDisciplineFilter);
console.log(this.woDaysFilter);
}
}
UPDATE: Found workaround
I tried creating a separate app, where the exact same code is working. That lead me to think something wasn't right on the LoginPage, the page that called setRoot() to the above page.
The login code looked like this:
WLAuthorizationManager.login("UserLogin", data).then(() => {
// Success
console.log("Logged in");
this.navCtrl.setRoot(WorkOrderListPage);
},
(err) => {
// failed
console.error(err);
this.showError("Username or password is incorrect");
})
I then figured it might be some Zone issue, and wrapped the setRoot call in zone.run() like this:
WLAuthorizationManager.login("UserLogin", data).then(() => {
// Success
console.log("Logged in");
this.zone.run(() =>
this.navCtrl.setRoot(WorkOrderListPage)
);
},
(err) => {
// failed
console.error(err);
this.showError("Username or password is incorrect");
})
After that the view started to respond as expected. I feel this is a bit of a hack. Can someone shed some light as to what is happening here?

Seems like you are basically using ngZone to make sure Angular knows you changed things so it will reload that part of the DOM to reflect the changes. I don't feel like it's a hack, because you are just making sure that it works as intended.
Angular 2 has some optimization features that help make your app run smoother and one of those is avoiding DOM updates whenever and wherever possible. By using zones (or ngZones) you are basically telling Angular "pay attention to this part, it changes and I need that change to be reflected in the DOM".
I've run into that sort of problem before myself and using zones is usually your best bet. Got into situations where a part of the interface would be stuck unless you touched a button or somesuch.
Another workaround (at least for range sliders) is using the AppllicationRef tick() method, which forces a DOM update. More info about it here.

Related

Show/Hide password button not working when input is active in Ionic 3

I am trying to implement the show/hide button for the password field in Ionic 3. I have got the code help from here
login.html
<ion-item>
<ion-input [type]="passwordType" placeholder="Password" formControlName="password"></ion-input>
<ion-icon item-end [name]="passwordIcon" class="passwordIcon" (click)='hideShowPassword()'></ion-icon>
</ion-item>
login.scss
.passwordIcon{
font-size: 1.3em;
position: absolute;
right: .1em;
top: .5em;
z-index: 2;
}
login.ts
passwordType: string = 'password';
passwordIcon: string = 'eye-off';
hideShowPassword() {
this.passwordType = this.passwordType === 'text' ? 'password' : 'text';
this.passwordIcon = this.passwordIcon === 'eye-off' ? 'eye' : 'eye-off';
}
I have done one modification in the .scss file and made the icon absolute so that it appears on top of the input field instead of the side.
This icon click is working when the Input field is not active/selected but if I am in the middle of typing in the Input Field, the click is not working/recognized. Please help.
With the solution suggested my field looks like this.
I'm not entirely sure of what
... and made the icon absolute so that it appears on top of the input field instead of the side
would mean, but still, using position: absolute on top of inputs may lead to bugs specially on iOS.
Another possible issue of your code is that buttons are designed to handle issues related with taps in mobile devices, but icons may not work properly sometimes.
Anyway, please take a look at this working Stackblitz project to do something like this:
The code is quite simple actually - the idea is to use a button with an icon instead of just an icon to avoid issues with the tap event:
Component
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public showPassword: boolean = false;
constructor(public navCtrl: NavController) {}
public onPasswordToggle(): void {
this.showPassword = !this.showPassword;
}
}
Template
<ion-header>
<!-- ... -->
</ion-header>
<ion-content padding>
<!-- ... -->
<ion-item>
<ion-input placeholder="Password" [type]="showPassword ? 'text' : 'password'" clearOnEdit="false"></ion-input>
<button (click)="onPasswordToggle()" ion-button clear small item-end icon-only>
<ion-icon [name]="showPassword ? 'eye-off' : 'eye'"></ion-icon>
</button>
</ion-item>
</ion-content>
EDIT
Based on your comments, one way to keep the button inside of the border would be not to apply the border to the input, but to the ion-item.
Something like this for example:
ion-item {
border: 1px solid #ccc;
border-radius: 10px;
}
would result in:

Only down arrow coming for ION-SELECT and blank for textbox in IONIC 4

In my ionic select i got output like small down arrow rather than a full select tag and for input tag just a blank space. I can enter data but UI not looks good. Is there any css i need to add.
<ion-card class='sc-ion-card-md-h'>
<ion-card-header>
<ion-card-subtitle>Name:</ion-card-subtitle>
<ion-card-title>
<ion-select #inputName id="username" class="form-control">
<ion-select-option>SELECT</ion-select-option>
<ion-select-option>Monicka</ion-select-option>
<ion-select-option>Hema</ion-select-option>
<ion-select-option>Ramesh</ion-select-option>
<ion-select-option>Madhavan</ion-select-option>
<ion-select-option>Aadhavan</ion-select-option>
<ion-select-option>Madhan</ion-select-option>
<ion-select-option>Prasanth</ion-select-option>
</ion-select>
</ion-card-title>
</ion-card-header>
<ion-card-header>
<ion-card-subtitle>Date:</ion-card-subtitle>
<ion-card-title>
<ion-input type="text" #inputDate id="date" class="form-control"></ion-input>
</ion-card-title>
</ion-card-header>
</ion-card>
Output looks like this...
It seems like you are trying to convert a Bootstrap form over to Ionic and you are bringing some jQuery thinking along with it.
This is how you would do it with an Ionic page:
page.html
<ion-header>
<ion-toolbar>
<ion-title>card-select</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-card>
<ion-list>
<ion-item>
<ion-label position="stacked">Name:</ion-label>
<ion-select [(ngModel)]="username" placeholder="SELECT">
<ion-select-option>Monicka</ion-select-option>
<ion-select-option>Hema</ion-select-option>
<ion-select-option>Ramesh</ion-select-option>
<ion-select-option>Madhavan</ion-select-option>
<ion-select-option>Aadhavan</ion-select-option>
<ion-select-option>Madhan</ion-select-option>
<ion-select-option>Prasanth</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label position="stacked">Date:</ion-label>
<ion-input [(ngModel)]="date"></ion-input>
</ion-item>
</ion-list>
</ion-card>
</ion-content>
page.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-card-select',
templateUrl: './card-select.page.html',
styleUrls: ['./card-select.page.scss'],
})
export class CardSelectPage implements OnInit {
username: string;
date: string;
constructor() { }
ngOnInit() {
}
}
notes
You don't need all those classes you put on.
Using label position="stacked" puts it above the input.
You dont need to bind the name like #username or id="username". What you do is put a variable on the page behind it and then use ngModel to bind to it. Any change that is made to the user interface (typing into a box, selecting an option) is then automatically set to the variable in the page. This works both ways because of the [()] syntax, so if you change username with something like this.username = "superman"in the code then the input box on the page will automatically update to match that value as well.
You don't need type="text" on the input, its the default.
You can use the placeholder attrib to pass some SELECT text instead of having an extra select option.

Ionic 2: Menu Toogle not working

I have 2 pages, login and home, when the user successfully logins, it will be directed to the home page and I set this as the root page. I've confirmed this by using navCtrl.canGoBackFunction and it is false. I tried adding a Menu Toggle but when I pressed the toggle button the menu does not show up
This is my home.html
<ion-header>
<ion-navbar color="primary">
<button menuToogle ion-button icon-only class="menu-button-left">
<ion-icon name="menu"></ion-icon>
</button>
<ion-title class="alogo"><img alt="logo" height="35" src="../../assets/imgs/logo.png" ></ion-title>
<button ion-button class="menu-button-right" (click)="logout()">
<p>Logout</p>
</button>
</ion-navbar>
</ion-header>
<ion-content padding-left padding-right>
</ion-content>
my home.ts
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { LoginPage } from '../login/login';
import { AuthService } from '../../app/services/auth.service'
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(
public navCtrl: NavController,
private authService: AuthService
) {
}
ionViewDidLoad() {
console.log('ionViewDidLoad HomePage');
console.log(localStorage.getItem('token'));
}
logout(){
console.log('logout button clicked');
this.authService.logOut();
this.navCtrl.setRoot(LoginPage);
this.navCtrl.popToRoot();
}
}
my app.html
<ion-menu [content]="mycontent">
<ion-content>
<ion-list>
<p>List/p>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav #mycontent [root]="rootPage" swipeBackEnabled="false"></ion-nav>
I've re-read multiple times the manual and I didn't see any issues with the way I did, the manual says it I put it in navbar the page should be root. I also tried using a toolbar but again clicking the menu toggle button does not do anything. Any idea ?
Persistent menus display the MenuToggle button in the Navbar on all pages in the navigation stack. To make a menu persistent set persistent to true on the element. Note that this will only affect the MenuToggle button in the Navbar attached to the Menu with persistent set to true, any other MenuToggle buttons will not be affected. In your code you have to change like below code.
my app.html
<ion-menu [content]="mycontent" persistent="true">
<ion-content>
<ion-list>
<p>List/p>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav #mycontent [root]="rootPage" swipeBackEnabled="false"></ion-nav>

Pass data from home.html to home.ts

I'm new to Ionic and I would like to know how I can pass data that is in between the <p></p> HTML tag to my home.ts file. I have tried doing getElementById but that doesn't seem to work. Also ViewChild but with zero results. I'd like to have some help concerning this question.
From .ts -> html :
.ts-> varString : String = "Hallo world";
html-> {{varString}}
From html -> .ts depends of html tag
Ex. <input type=“text” name=“username” [(ngModel)]=“username”>
username is username : String = ""; and it changes avry time you update the input. Similar to other html tags. You can also get elements by id or class using Element of ionic angular
there is many way to pass data between html and ts, but you must have good understand about MVC design pattern. the MVC is the reason of why google introduced angular.
in angular (engine of ionic), your View(html in your project) knows everything about controller(ts file).
***** home.ts********
public MyName:string="jon";
MyFunc1()
{
alert(this.MyName);
}
MyFunc2()
{
this.MyName="other name";
alert(this.MyName);
}
*****home.html*******
<input type="text" [(ngModel)]="MyName" >
<p>{{MyName}}</p>
<button ion-button (click)="MyFunc1()" >MyFunc1</button>
<button ion-button (click)="MyFunc2()" >MyFunc2</button>
.
.
.
if you change the value of MyName in ts, then it will change automatically in html and also if you change you input value (that it is binded to MyName) it will change the MyName value in model (in ts).
Selecting DOM in ionic isn't a right way to change models value.
Taking the simple example of a Login page,
<ion-item>
<ion-label floating color="primary">Registered Email ID</ion-label>
<ion-input [(ngModel)]="login.email" name="email" type="text" #email="ngModel" spellcheck="false" autocapitalize="off"
required>
</ion-input>
</ion-item>
<ion-item>
<ion-label floating color="primary">Passcode</ion-label>
<ion-input [(ngModel)]="login.passcode" name="password" type="password" #password="ngModel" required>
</ion-input>
</ion-item>
The .ts code for this would be:
login= { username: '', email:'', mobile:'', passcode:'' };
That is literally it!
You can also go about without the login object and can declare variables like
username: any
email: any
and so on; and directly reference them in the html as
[(ngModel)]="username"
Hope it helps!
First try to understand that when you are working on Ionic, you are working on Angular but not Core Javascript.
and here is simple example that may help
home.html
import {Component} from '#angular/core';
import {NavController, AlertController} from 'ionic-angular';
#Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {
name: string;
constructor(public navCtrl: NavController, private alertController: AlertController) {
}
showName() {
console.log(this.name);
let alert = this.alertController.create({
title: 'Hello ' + this.name + '!',
buttons: ['OK']
});
alert.present();
}
}
home.ts
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-label>{{ name }}</ion-label>
<ion-input [(ngModel)]="name"></ion-input>
<button (click)="showName()">Click Me</button>
</ion-content>

Clicking the device back button closes the app instead of going back to previous page

If click any ion-item it open the desired page but if i click device back button it close the app rather than going back to previous page in android:
This is my ionic side menu:
<ion-side-menus enable-menu-with-back-views="false">
<ion-side-menu-content>
<ion-nav-bar class="bar-positive">
<ion-nav-back-button></ion-nav-back-button>
<ion-nav-buttons side="left">
<button class="button button-icon icon ion-android-menu" menu-toggle="left">
</button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-nav-view name="menuContent"></ion-nav-view>
</ion-side-menu-content>
<ion-side-menu side="left">
<ion-header-bar class="bar-positive">
<h1 class="title"></h1>
</ion-header-bar>
<ion-content>
<ion-list>
<ion-item menu-close ng-click="login()">
Login
</ion-item>
<ion-item menu-close ui-sref="app.search">
Search
</ion-item>
<ion-item menu-close href="#/app/browse">
Browse
</ion-item>
<ion-item menu-close href="#/app/playlists">
Playlists
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
</ion-side-menus>
Here is app.js :
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html',
controller: 'AppCtrl'
})
.state('app.search', {
url: '/search',
views: {
'menuContent': {
templateUrl: 'templates/search/default.html'
}
}
})
.state('app.search-form', {
url: '/search-form',
views: {
'menuContent': {
templateUrl: 'templates/search/search-form.html'
}
}
})
One solution I found:
$ionicHistory.nextViewOptions({
disableBack: true,
historyRoot: true
});
So when you click a button and going to next page, this will will disable back button.
The default behaviour of the back button is as follows:
Go back in history - if the history stack is empty -> exit the app.
So you should check the $state you are in, when you tap the hardware back button.
With the following code (placed in the run function of your module) you can overwrite the default behaviour. For example you can disable the app exit like this:
$ionicPlatform.registerBackButtonAction(function (event) {
if($state.current.name=="app.home"){
navigator.app.exitApp(); //<-- remove this line to disable the exit
}
else {
navigator.app.backHistory();
}
}, 100);
See the documentation for $ionicPlatform.
It is the menu-close attribute in the side menu that clears the history. You could experiment by replacing it with menu-toggle="left". That will still close the side bar but keep the history.
I ended up overriding the behaviour for the HW back key like below.
It sends the user to the starting view before exiting the app when pressing back. Note that I still use the menu-close attribute in the side menu. Also note I happen to store the start url in window.localStorage["start_view"] because it can change in my app. Hope it can help/inspire someone else with this problem.
$ionicPlatform.registerBackButtonAction(function(event) {
if ($ionicHistory.backView() == null && $ionicHistory.currentView().url != window.localStorage["start_view"]) {
// Goto start view
console.log("-> Going to start view instead of exiting");
$ionicHistory.currentView($ionicHistory.backView()); // to clean history.
$rootScope.$apply(function() {
$location.path(window.localStorage["start_view"]);
});
} else if ($ionicHistory.backView() == null && $ionicHistory.currentView().url == window.localStorage["start_view"]) {
console.log("-> Exiting app");
navigator.app.exitApp();
} else {
// Normal back
console.log("-> Going back");
$ionicHistory.goBack();
}
}, 100);
Import NavController in your app.component.ts and use below code.
import { NavController} from '#ionic/angular';
constructor( private nav: NavController ) {this.initializeApp();}
ngOnInit() {
this.plateform.backButton.subscribe(res => {
this.nav.goBack('');
});
}
One solution might be:
$ionicHistory.nextViewOptions({
disableBack: true,
historyRoot: true
});
This prevents the event from spreading, otherwise it did not work.
Code by: https://stackoverflow.com/users/5378702/andre-kreienbring
$ionicPlatform.registerBackButtonAction(function (event) {
if(condition){
navigator.app.exitApp(); //<-- remove this line to disable the exit
}
else {
navigator.app.backHistory();
}
throw "PreventBackButton";
}, 100);