How to fix #ViewChild ElementRef undefined in NativeScript Angular? - scrollview

I am developing a mobile application with NativeScript (CLI v5.2.0) Angular (v7.2.3) and I have my #ViewChild ElementRef that was undefined.
I checked the presence of ViewChild and ElementRef in the import of "#angular/core", renamed my #ViewChild variable, changed the scope from public to private, moved the console.log() in ngAfterViewInit (see: https://github.com/NativeScript/nativescript-angular/issues/188#issuecomment-212815619) and rebuilt my project with "tns debug android --clean --bundle".
component-name.component.ts :
#ViewChild("test") private _scrollView: ElementRef;
constructor(page: Page) {
page.actionBarHidden = true;
}
ngAfterViewInit() {
console.log("ScrollView element:", this._scrollView.nativeElement);
}
...
component-name.component.html :
<GridLayout columns="*" rows="*, auto">
<ScrollView (swipe)="onSwipe($event)" col="0" row="0" #test>
<StackLayout>
...
If I put #test at the beginning, just after the ScrollView element, I have "this._scollView" variable that is undefined.
If I put #test at the end, like the example above, everything works and I show my element in the console.log(this._scrollView.nativeElement)!
A bug ?

Previous code:
import { ElementRef } from "#angular/core";
#ViewChild("myElement") myElement: ElementRef;
Migrated code:
import { ElementRef } from "#angular/core";
#ViewChild("myElement", { static: false }) myElement: ElementRef;
{static: false} means nfAfterInit,
{static: true} mean ngInit
its works for me.

Related

ion-slides methods not working in ionic 4

Unable to use methods provided by ion-slides in official documentation. checked other answers in here but all seems to confuse ionic 4 with ionic 3 and providing answers applicable in ionic 3.
I want to get active index of slide. online documentation is not complete about how to implement it.
Note: Use IonSlides and don't use ElementRef and nativeElement
Just follow the code below and it will work fine to get the active index from getActiveIndex()
import { IonSlides } from '#ionic/angular';
#ViewChild('slides', {static: true}) slides: IonSlides;
slideChanged(e: any) {
this.slides.getActiveIndex().then((index: number) => {
console.log(index);
});
}
In ionic 4, the return type of the getActiveIndex() method is Promise<number>, so the code you were using in ionic 3 will not work anymore. You could at a bare minimum switch it out for somehting like:
this.slider.getActiveIndex()
.then(activeIndex => {
console.log('active index = ', activeIndex );
if (activeIndex < this.slides.length) {
this.selectedSegment = this.slides[activeIndex ].id;
}
});
Or whatever you want to use it for. The official doc is actually pretty awesome on this: https://ionicframework.com/docs/api/slides
Build the slider in your html with a slides ID and a function which is emitted when the active slide has changed.
<ion-slides #slides (ionSlideDidChange)="getIndex()">
<ion-slide></ion-slide>
</ion-slides>
In the .ts file you import the slider ID with ViewChild and set the function to get the active index.
import { Component, OnInit, ViewChild } from '#angular/core';
import { Slides } from '#ionic/angular';
export class Page implements OnInit {
#ViewChild('slides') slides: Slides;
constructor() {}
ngOnInit() {
}
async getIndex() {
console.log(await this.slides.getActiveIndex());
}
}
I had the same issue, but I solved it with the following code:
My .ts file:
export class RegistroPage implements OnInit {
#ViewChild('registroWizard') registroWizard: IonSlides;
slideOpts: any;
constructor() {
this.slideOpts = {
effect: 'fade'
};
}
ngOnInit() {
this.registroWizard.lockSwipeToNext(true);
}
}
My HTML file:
<ion-slides #registroWizard pager="true" [options]="slideOpts">
<ion-slide>
<h1>Slide 1</h1>
<ion-button>Hola</ion-button>
</ion-slide>
<ion-slide>
<h1>Slide 2</h1>
<ion-button>Hola</ion-button>
</ion-slide>
<ion-slide>
<h1>Slide 3</h1>
<ion-button>Hola</ion-button>
</ion-slide>
</ion-slides>
I solved the problem like this:
page.ts:
import { IonSlides } from '#ionic/angular';
...
#ViewChild('slides') slides: IonSlides;
nextSlide() {
this.slides.slideNext();
}
page.html:
<ion-slides #slides pager="true" [options]="slideOpts">
<ion-slide>slide 1</ion-slide>
<ion-slide>slide 2</ion-slide>
</ion-slides>
<ion-button (click)="nextSlide()" class="register-buttons">go next</ion-button>
exact the same thing goes for the back action
static: true
#ViewChild('ionSlides', { static: true }) ionSlides: IonSlides;
u need declaration class to app.module.ts
#NgModule({
declarations: [MySliderComponent]
})
I used IonSlides as type but it didn't help.
For me, the above mentioned solutions didnt work (ionic v6.17.1). What worked was:
#ViewChild('slides', {static: true}) slides: ElementRef;
swipeRight() {
this.slides.nativeElement.slideNext();
All methods working this way. Altering ```{static: true} didn't throw any error
If you console.log after declaring slides as IonSlides type, it shows ElementRef type

Unable to get nativeElement of ion-textarea in Ionic 4 to set height

I have a custom directive to adjust the ion-textarea height to autosize the height as text is entered rather than setting a fixed row height or having ugly scroll bars as the textarea fills up.
In Ionic-4 I am unable to get the nativeElement of the html textarea of the ion-textarea. Any help would be great
It's running on Angular 6 and Ionic 4 but when I try and get this.element.nativeElement.getElementsByTagName('textarea')[0] it is always undefined so I can't set the height programatically.
import { ElementRef, HostListener, Directive, OnInit } from '#angular/core';
#Directive({
selector: 'ion-textarea[autosize]'
})
export class AutosizeDirective implements OnInit {
#HostListener('input', ['$event.target'])
onInput(textArea:HTMLTextAreaElement):void {
this.adjust();
}
constructor(public element:ElementRef) {
}
ngOnInit():void {
setTimeout(() => this.adjust(), 0);
}
adjust():void {
const textArea = this.element.nativeElement.getElementsByTagName('textarea')[0];
textArea.style.overflow = 'hidden';
textArea.style.height = 'auto';
textArea.style.height = textArea.scrollHeight + 'px';
}
}
As the const textArea always comes back undefined I can't set the height to follow the scroll height to prevent the scroll bars.
Anyone been able to do this in Ionic-4? seen working examples in Ionic-3 as per the above code.
Thank you
Rowie
Below code would help your problem.
import { ElementRef, HostListener, Directive, AfterViewInit } from '#angular/core';
#Directive({
selector: 'ion-textarea[autosize]'
})
export class AutoSizeDirective implements AfterViewInit {
readonly defaultHeight = 64;
#HostListener('input', ['$event.target'])
onInput(textArea: HTMLTextAreaElement) {
this.adjust(textArea);
}
constructor(private element: ElementRef) {}
ngAfterViewInit() {
this.adjust();
}
adjust(textArea?: HTMLTextAreaElement) {
textArea = textArea || this.element.nativeElement.querySelector('textarea');
if (!textArea) {
return;
}
textArea.style.overflow = 'hidden';
textArea.style.height = 'auto';
textArea.style.height = (textArea.value ? textArea.scrollHeight : defaultHeight) + 'px';
}
}
Usage: <ion-textarea autosize></ion-textarea>
I have confirmed it on Ionic 4.0.2/Angular 7.2.6.
Regards.
this package does all the autosizing of my ion-textareas for me https://github.com/chrum/ngx-autosize
just follow the guide and get it working, if it doesn't work importing it into the app.module.ts then try importing it into the page's module, I personally needed that dunno if you will, but package is a life saver

Get ng-template from component angular 2

how can i get the element in angular 2?
in case i have this in html
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">Header</div>
<div class="modal-body">Body</div>
<div class="modal-footer">footer</div>
</ng-template>
i use that for ngBmodal ng-bootstrap
if i use button for open content its work = button
(click)="open(content,data.id)"
then i would like open content from component
in this case, im redirect from other page and open content
ngOnInit() {
this.activatedRoute.queryParams.subscribe((params: Params) => {
let id = params['id'];
if(id != undefined){
this.open('content',id);
}
});
}
open(content, id) {
this.dataModal = {};
this.getDataModal(id);
this.mr = this.modalService.open(content, { size: 'lg' });
}
modal open but not with the html, i try afterviewinit to get #content it doesnt work
thanks,sorry for my english :v
First import NgbModal and ModalDismissReasons
import { NgbModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
and add modalservice to constructor
private modalService: NgbModal
See:
https://ng-bootstrap.github.io/#/components/modal/examples#options
Then in your typescript file:
1 - Import TemplateRef and ViewChild, example:
import { TemplateRef, ViewChild } from '#angular/core';
2 - Create the variable that binds the ngtemplate (add before constructor):
#ViewChild('content')
private content: TemplateRef<any>;
3 - Open modal from typescript:
this.modalService.open(this.content);

Aurelia dialog not getting the view easy-webpack skeleton

Somehow when I let the framework load dialog view automatically, it said:
Failed to load view exception
However, when using inlineView it works as expected.
How can I make it load the view ?
It's a little late, but it may be useful for other users.
This error is because Webpack loader.
More info where: https://github.com/aurelia/dialog/issues/127
I don't like using string to reference the dialog ViewModel, because this I suggest using the option to force the View in dialog ViewModel. I don't have to change anything else.
Example:
Dialog ViewModel:
import {autoinject, useView} from 'aurelia-framework';
import {DialogController} from 'aurelia-dialog';
#autoinject()
#useView('./dialog-message.html') //This is the important line!!!!!
export class DialogMessage {
message: string = 'Default message for dialog';
constructor(private controller: DialogController){
controller.settings.centerHorizontalOnly = true;
}
activate(message) {
this.message = message;
}
}
Dialog View:
<template>
<ai-dialog>
<ai-dialog-body>
<h2>${message}</h2>
</ai-dialog-body>
<ai-dialog-footer>
<button click.trigger = "controller.cancel()">Cancel</button>
<button click.trigger = "controller.ok(message)">Ok</button>
</ai-dialog-footer>
</ai-dialog>
</template>
Method to show Dialog:
import {DialogMessage} from './dialog-message';
(...)
showDialog(){
this.dialogService.open({viewModel: DialogMessage, model: 'Hi, how are you?' }).then(response => {
if (!response.wasCancelled) {
console.log('good');
} else {
console.log('bad');
}
console.log(response.output);
});
}

How to import ion-rangeslider in Aurelia

I am trying to use the ionRangeSlider plugin in Aurelia but am not sure how to make use of it.
https://github.com/IonDen/ion.rangeSlider/issues
I have jspm'd it into my project but how do I import it as well as call the one function that runs the plugin?
You will find the exact package names for including ion-rangesider in your package.json:
jspm": {
"dependencies": {
...
"ion-rangeslider": "npm:ion-rangeslider#^2.1.3",
"jquery": "npm:jquery#^2.2.3",
...
}
}
Then you need to create your own custom element like:
import {inject, noView} from 'aurelia-framework';
//import your dependencies
import $ from 'jquery';
import ionRangeSlider from 'ion-rangeslider';
#noView()
#inject(Element)
export class Slider {
constructor(element){
this.element = element;
}
bind(){
$(this.element).ionRangeSlider({min: 100, max: 1000, from: 550});
}
}
And where you want to use your slider you have to write:
<require from='./slider'></require>
<require from="ion-rangeslider/css/ion.rangeSlider.skinHTML5.css"></require>
<require from="ion-rangeslider/css/ion.rangeSlider.css"></require>
<slider></slider>
Normally you would put the <require from="xxx.css"></require> tags inside slider.html to ensure style encapsulation. In my example i put them where i wanted to use the slider because so i donĀ“t needed to create a slider.html.
Here is an example how to use the bootstrap popover.
I guess you should be able to do the same and calling $("#example_id").ionRangeSlider(); from within the bind function
if you imported all resources
Install ion-rangeslider first:
npm install ion-rangeslider
jspm install npm:ion-rangeslider
Create a custom attribute
import {customAttribute, bindable, inject} from 'aurelia-framework';
import {ionRangeSlider} from 'ion-rangeslider';
#customAttribute('rangeslider')
#inject(Element)
export class RangesliderCustomAttribute {
//make your own options based on requirements
options = { type: "single", min: 0, max: 100 };
constructor(element) {
this.element = element;
}
attached() {
$(this.element).ionRangeSlider(this.options).on('change', e => {
fireEvent(e.target, 'input');
});
}
detached() {
$(this.element).ionRangeSlider('destroy').off('change');
}
}
function createEvent(name) {
var event = document.createEvent('Event');
event.initEvent(name, true, true);
return event;
}
function fireEvent(element, name) {
var event = createEvent(name);
element.dispatchEvent(event);
}
import css into app.html or where you import css in your application
<require from="ion-rangeslider/css/ion.rangeSlider.css"></require>
<require from="ion-rangeslider/css/ion.rangeSlider.skinNice.css"></require>
Now you can use your attribute in input in any view
<require from="./rangeslider"></require>
<input rangeslider type="text" value.bind="yourInitialSliderValue">