I have some troubles with setting range sliders inside ion-slides components.
Indeed, when I'm trying to swipe values of range, ionic detects I'm trying to swipe to another slide.
So I've two questions :
Is there a simple way to implement a range inside a ion-slide content with possibility to set data with range sliders without swiping to another slide and at the opposite, possibility to swipe to another slide without impacting range values.
If there isn't any simple way to do this, I've tried to changing direction of swipe to go to another slide, and according to the documentation I've tried it but I've compile error :
SyntaxError: /my/path/diagnostic.js: Unexpected token (8:18) while parsing file: /my/path/diagnostic.js so the error is targetting this line : diagnosticSlides = {
Here my code :
diagnostic.js
import {Page, NavController, NavParams, Slides} from 'ionic-angular';
#Page({
templateUrl: 'build/pages/diagnostic/diagnostic.html'
})
export class DiagnosticPage {
// Code for changing swiping direction -- not working
diagnosticSlides = {
initialSlide: 1,
loop: false,
direction: 'vertical'
};
static get parameters() {
return [[NavController], [NavParams]];
}
constructor(nav, navParams) {
this.nav = nav;
}
}
diagnostic.html
<ion-slides [options]="diagnosticSlides" pager>
<ion-slide style="background-color: blue">
<div class="item range">
<i class="icon ion-volume-low"></i>
<input type="range" name="volume">
<i class="icon ion-volume-high"></i>
</div>
</ion-slide>
<ion-slide style="background-color: red">
<h2>Other slide</h2>
</ion-slide>
</ion-slides>
Could you help me please ?
Thanks by advance,
From Ionic documentation available range component
So you can use ionFocus and ionBlur of this component and use lockSwipes(true) method between these two events.
public swipe: IonSlides;
#ViewChild(IonRange) range: IonRange;
this.range.ionFocus.subscribe(() => {
this.swipe.lockSwipes(true);
});
this.range.ionBlur.subscribe(() => {
this.swipe.lockSwipes(false);
});
Related
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:
I am using ion-slide in ionic4 and i want to go for next slide manually by clicking on button. I have seen the ion-slide documentation in which they have mention slideNext method. But I don't know how to use this method for changing slide.
Ya, I got the solution. In ionic 4 you have to import IonSlides instead of Slides.
home.ts
import { IonSlides} from '#ionic/angular';
export class HomePage {
#ViewChild('mySlider') slides: IonSlides;
swipeNext(){
this.slides.slideNext();
}
}
home.html
<ion-slides pager="true" pager="false" #mySlider>
<ion-slide>
</ion-slide>
<ion-slide>
</ion-slide>
<ion-button (click)="swipeNext()">Next</ion-button>
</ion-slides>
In Angular 8 :
#ViewChild('mySlider', { static: true }) slides: IonSlides;
Is simple and just necessary in html
<ion-slides #slides pager="true">
<ion-slide>1
</ion-slide>
<ion-slide>2
</ion-slide>
</ion-slides>
<ion-button (click)="slides.slidePrev()" > Prev </ion-button>
<ion-button (click)="slides.slideNext()" > Next </ion-button>
Simple way (same as above answer but I tried to make it simpler):
import { Slides } from 'ionic-angular';
class abc {
#ViewChild(Slides)slides: Slides;
public next(){
this.slides.slideNext();
}
public prev(){
this.slides.slidePrev();
}
}
I'm building a mobile app in ionic and I wanna make a slack-like side menu by placing slides.
For example, when you click on main menu item, it will slide out another slide in the sidemenu as slack does.
I tried to use ion-slides in ion-menu but slides is not working.
Check out the screenshot, please.
Here is the code snippet.
<ion-menu [content]="mycontent" [swipeEnabled]="false">
<ion-content>
<ion-slides>
<ion-slide>
<h1>Slide 1</h1>
</ion-slide>
<ion-slide>
<h1>Slide 2</h1>
</ion-slide>
<ion-slide>
<h1>Slide 3</h1>
</ion-slide>
</ion-slides>
</ion-content>
</ion-menu>
<ion-nav #mycontent [root]="rootPage"></ion-nav>
Here is what I'm trying to build.
Any thoughts on how to implement this?
Thanks.
The ion-slides component makes use of the Swiper library under the hood. Part of the init code for Swiper depends on knowing the width of the slide container, and the code uses clientWidth to get it. Since the menu starts out with display:none, the retrieved width is always zero and the init code bails on you.
You can get around this by temporarily setting display:block while Swiper initializes. I have my side menu inside of a component, so you may need to adjust this code to your situation:
app.html:
<sidebar [content]="content"></sidebar>
<ion-nav #content [root]="rootPage" swipeBackEnabled="false"></ion-nav>
sidebar.html:
<ion-menu [content]="content" swipeEnabled="false">
<ion-content>
<ion-slides pager>
<ion-slide>
<h2>Slide 1</h2>
</ion-slide>
<ion-slide>
<h2>Slide 2</h2>
</ion-slide>
<ion-slide>
<h2>Slide 3</h2>
</ion-slide>
</ion-slides>
</ion-content>
</ion-menu>
sidebar.component.ts:
...
#Component({
selector: 'sidebar',
templateUrl: 'sidebar.html',
})
export class SidebarComponent implements AfterViewInit {
#Input('content') content: NavController;
#ViewChild(Slides) slides: Slides;
#ViewChild(Menu, { read: ElementRef }) menuRef: ElementRef;
// Use Renderer2 instead of direct DOM manipulation through the
// ElementRef.nativeElement.
//
// #see: https://medium.com/#kmathy/angular-manipulate-properly-the-dom-with-renderer-16a756508cba
//
constructor(private renderer: Renderer2) {
}
// ViewChild template references might not be available until
// AfterViewInit lifecycle hook runs.
//
// #see: https://blog.angular-university.io/angular-viewchild/
//
ngAfterViewInit() {
this.renderer.setStyle(this.menuRef.nativeElement, 'display', 'block');
setTimeout(() => {
// Swiper init has its own ngAfterViewInit code that delays 300ms
// before running. Don't remove the 'display' style until after that.
this.renderer.removeStyle(this.menuRef.nativeElement, 'display');
}, 310);
}
}
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.
I have an ion-card list of elements in a page and I want to navigate to a specific position when entering the view, the page renders properly and I can see in the generated code that div with specific id is indeed on the view but trying to get the element by id is returning null.
Here is the code for the view where I build the cards:
<ion-content class="cards-bg">
<ion-card *ngFor="let meal of meals">
<ion-card-header>
{{meal.date | date: 'dd/MM/yyyy HH:mm'}}
</ion-card-header>
<div id="{{meal.id}}">
<img src="{{serverUrl + '/' + meal.picture}}">
</div>
<ion-card-content>
<ion-card-title>
{{meal.title}}
</ion-card-title>
<p>{{meal.description}}</p>
</ion-card-content>
</ion-card>
<ion-row no-padding></ion-row>
</ion-content>
And this is the code that tries to get the reference to the element so I can navigate to it:
public index: string;
constructor(public navCtrl: NavController, navParams: NavParams, public mealService: MealService) {
this.index = navParams.get('index');
}
ionViewDidEnter() {
this.mealService.getMeals().then((result) => {
this.meals = result;
let yOffset = document.getElementById(this.index).offsetTop;
this.content.scrollTo(0, yOffset, 1000);
}).catch((error) => {
console.log(error);
});
As I mentioned the page with cards render, I can even see the generated code contains the specific div with id but the call:
document.getElementById(this.index)
returns null, so I get the following error in logs:
chromium: [INFO:CONSOLE(57687)] "TypeError: Cannot read property 'offsetTop' of null", source: file:///android_asset/www/build/main.js (57687)
Use Angular`s ElementRef
Inject it in constructor:
constructor(private elRef:ElementRef){}
For getting the element,
let el = this.elRef.nativeElement;//get underlying native element.
let yOffset = el.getElementById(this.index).offsetTop;
Or you could also use ViewChildren:
Set an id for the div:
<div #divElement id="{{meal.id}}">
<img src="{{serverUrl + '/' + meal.picture}}">
</div>
Set the decorator on a class variable:
#ViewChildren('divElement')
divs:any;
For accessing elements,
let yOffset = this.divs[this.index].nativeElement.offsetTop