currently i am working in ionic framework on javascript and angular-js i just put the search box and render list of customer but suppose in first attempt i can search with 'a' it shows all item which having alpha 'a' but the problem is when i scroll down to see the search result list and at the bottom if i want to search with 'd' this time it gives result but at the top of the page but my scroll is at the bottom of the page.
So to solve the above problem i want set scroll position at the top of the page when search query is empty and display all customer so what should i do to solve this problem
thanks in adv..
You can try :
$ionicScrollDelegate.scrollTop();
http://ionicframework.com/docs/api/service/$ionicScrollDelegate/
For Ionic 2 and higher use the scrollToTop() method on the content class.
page.html
<ion-content>
Add your content here!
</ion-content>
page.ts
import { Component, ViewChild } from '#angular/core';
import { Content } from 'ionic-angular';
#Component({...})
export class MyPage{
#ViewChild(Content) content: Content;
scrollToTop() {
this.content.scrollToTop();
}
}
See http://ionicframework.com/docs/api/components/content/Content/#scrollToTop
I normally do this on my home page.
import { Component, ViewChild } from '#angular/core';
import { Content } from 'ionic-angular';
#Component({...})
export class MyPage{
#ViewChild(Content) content: Content;
scrollToTop() {
this.content.scrollToTop(400);
}
ionViewDidEnter(){
this.scrollToTop();
}
}
Or you can call scrollTop() whenever you want.
Note: 400 is duration in milliseconds, it is optional and you can use this as well
this.content.scrollToTop();
in this case scrollToTop() will set the default value of 300.
For Ionic 4 this has been renamed from Content to IonContent, along with a new package name:
Breaking: Component Imports Changes
Making the snippet look like this:
import { Component, ViewChild } from '#angular/core';
import { IonContent } from '#ionic/angular';
#Component({...})
export class MyPage{
#ViewChild(IonContent) content: IonContent;
scrollToTop() {
this.content.scrollToTop(400);
}
ionViewDidEnter(){
this.scrollToTop();
}
}
Thanks #Junaid (https://stackoverflow.com/a/48987846/156388) for the base of this answer.
I didn't edit that answer as it's still valid for Ionic 2-3.
The answer above will scroll all your views to top. If you want to have more control then you will have to use a delegate handler .
First whatever you want to scroll you will have to add the delegate handler name
<ion-content delegate-handle="dashboard">
......
</ion-content>
In your controller you will have to use this handler
$timeout(function(){$ionicScrollDelegate.$getByHandle('dashboard').scrollTop(false);});
I would recommend using a $timeout because if there is current digest cycle it will not work. Also change false to true if you want to animate the scroll. Lastly dont forget to inject $ionicScrollDelegate and $timeout in your controller
For Ionic 4.X Latest Stable Release Try this
Source Link
In Page add Angular variable id on ion-content tag
<ion-content
[scrollEvents]="true"
(ionScrollStart)="logScrollStart()"
(ionScroll)="logScrolling($event)"
(ionScrollEnd)="logScrollEnd()"
>
<ion-button (click)="ScrollToTop()">
Scroll To Top
</ion-button>
</ion-content>
In component add methods
logScrollStart(){
console.log("logScrollStart : When Scroll Starts");
}
logScrolling(){
console.log("logScrolling : When Scrolling");
}
logScrollEnd(){
console.log("logScrollEnd : When Scroll Ends");
}
ScrollToBottom(){
this.content.scrollToBottom(1500);
}
ScrollToTop(){
this.content.scrollToTop(1500);
}
ScrollToPoint(X,Y){
this.content.scrollToPoint(X,Y,1500);
}
For Ionic 4, the approaches here helped a lot. However, a second param was needed on
#ViewChild(IonContent) content: IonContent;
So, I set the static false, as suggested here. But it only worked for me when I changed:
#ViewChild(IonContent, {static: false}) content: IonContent;
to
#ViewChild(IonContent, undefined) content: IonContent;
In Ionic 6+ you need to declare the element's ID to refer to, you could use any when declaring the type, but to be specific you can set IonContent as per previous versions. This is obviously for the Angular flavour of Ionic.
So after you add the ID, for example I have called my content "content":
Now you can refer to this element in the TypeScript via ViewChild as per previous versions.
Now you can utilise the methods outlined in the docs. For example:
Related
I'm trying to use hook activate, didTransition, or willTransition, but none of these work, they do nothing at all.
I try to start by one route:
// app/routes/section.js
import Route from '#ember/routing/route';
export default class SectionRoute extends Route {
...
activate() { scroll(0, 0); }
}
<!-- app/templates/section.hbs -->
<h1>{{model.title}}</h1>
<p>{{model.body}}</p>
{{outlet}}
{{#each model.subsections as |s| }}
<Section #section={{s}} />
{{/each}}
This works in my js browser console:
scroll(0, 0);
This is my router
// app/router.js
...
Router.map(function() {
this.route('docs');
this.route('section', { path: '/docs/section/:slug' });
});
If I make section a child of docs, it works, as long as I don't hide parent's content, but I want to hide it.
An anchor in link-to component could help.
When I remove this piece of css, it works.
html {
scroll-behavior: smooth;
}
Your approach is working as expected. I have created an Ember Twiddle to verify that one, which you can find here. It has two routes. One which scrolls to the top when activated and one which scrolls to bottom when activated. It's scrolling as expected.
I guess you may be facing issues with the hook if transitioning between subroutes? A route is considered to stay active and the activate hook is not called
if a transition does not change the route but only the dynamic segments or query parameters used for the route or
if a transition happens between subroutes of that route.
If you want to have scroll to top on every transition or want to have more granular control, which transitions should trigger that scrolling I would recommend to use routeDidChange event of RouterService instead.
I have a project developed in Angular 6+, when user comes to website, first he sees logo and loading bar underneath it.
I have a problem switching to ngx-loading-bar insted of pace.js
Right now I am using Pace (the last example a fixed width line)
https://github.hubspot.com/pace/docs/welcome/
integrated into the index.html of the root project.
I want to put ngx-loading-bar instead of pace to do same job.
ngx-loading-bar is already implemented and working when adding <ngx-loading-bar></ngx-loading-bar> in some of the components but not in the index.html.
I want to put same style same width loading bar as on example link bellow (4th bullet example, similar to pace)
https://aitboudad.github.io/ngx-loading-bar/
so on the load, users sees the loading bar how it loads and on load, it goes on the next component as project example bellow.
Here is the example of project
www.wowlectures.com/pitchdeck
Not sure why you want it to work in index.html, do it in app.component.
You could create a loading service. A simple way is this:
#Injectable({providedIn: 'root'})
export class LoadingService {
private _loading = true;
get isLoading() {
return this._loading;
}
set isLoading(v: boolean) {
this._loading = v;
}
}
Then in app.component.ts:
export class AppComponent {
constructor(public loading: LoadingService) {}
}
And app.component.html:
<any-loading-component *ngIf="loading.isLoading"></any-loading-component>
<router-outlet></router-outlet>
A cleaner way is to use subjects/observables in the service and subscribe to changes where you want to show the loading-bar and dispatch from anywhere you want it to start loading.
<app-root>
<ngx-loading-bar></ngx-loading-bar>
</app-root>
hey you can add this in your index.html
I have a component ComponentA. there is some dom manipulation for deferent devices. I don't wanna fix them for every device when the browser resized.
that's why I wanna re-initialize the whole component again. is there any way to do it?
or any other solution would be very helpful.
thank you in advance. :)
There is no way to programatically destroy or re-initialize an component in Angular. An component will automatically be destroyed if there are no more variables or DOM elements referencing it.
EDIT: In order to solve your problem I'd suggest you have a look into the code nikolayandr posted.
if understood your question correctly then you can add just the next method to your component:
#HostListener('window:resize', ['$event'])
onResize(event) {
// event.target.screen.width is the current screen size
// you can reinit your component here
}
UPDATED.
Another way to catch window resize events is
import {FromEventObservable} from 'rxjs/observable/FromEventObservable';
import {debounceTime} from 'rxjs/operators';
initOnWindowResize() {
FromEventObservable.create(window, 'resize')
.pipe(
debounceTime(500)
)
.subscribe((event: any) => {
// Do something here
});
}
ngOnInit(): void {
this.initOnWindowResize();
});
Upon clicking a button (which is bottom of the page), I want to go to a certain element (in my case, #navbar) which is in the top of the current page, but I don't know how to do it. I've tried the following code with no avail.
<nav class="navbar navbar-light bg-faded" id="navbar">
<a class="navbar-brand" href="#">
{{appTitle}}
</a>
<!-- rest of the nav link -->
</nav>
<!-- rest of the page content -->
<!-- bottom of the page -->
<button class="btn btn-outline-secondary" (click)="gotoTop()">Top</button>
In angular component:
import { Router } from '#angular/router';
/* rest of the import statements */
export class MyComponent {
/* rest of the component code */
gotoTop(){
this.router.navigate([], { fragment: 'navbar' });
}
}
I would really appreciate if someone helped me out with a solution and explained why my code hadn't worked.
Please note that element (navbar) is in other component.
You can do this with javascript:
gotoTop() {
let el = document.getElementById('navbar');
el.scrollTop = el.scrollHeight;
}
This will bring the DOM element with id="navbar" into view when the method is called. There's also the option of using Element.scrollIntoView. This can provide a smooth animation and looks nice, but isn't supported on older browsers.
If the element is in a different component you can reference it several different ways as seen in this question.
The easiest method for your case would likely be:
import { ElementRef } from '#angular/core'; // at the top of component.ts
constructor(myElement: ElementRef) { ... } // in your export class MyComponent block
and finally
gotoTop() {
let el = this.myElement.nativeElement.querySelector('nav');
el.scrollIntoView();
}
Working plunker.
I know, you want to scroll to a specific element in the page. But, if the element is in the top of the page, then you can use the following:
window.scrollTo(0, 0);
document.getElementById("YOUR_DIV_ID").scrollIntoView({ behavior: 'smooth' });
I think your way didn't work because of the empty router.
this.router.navigate(['/home'], { fragment: 'top' });
would work if 'home' is declared as a route and you have the id="top" element on it.
I know you would like it to be "pure Angular way", but this should work (at least):
gotoTop(){
location.hash = "#navbar";
}
I have done some reading and investigation on this error, but not sure what the correct answer is for my situation. I understand that in dev mode, change detection runs twice, but I am reluctant to use enableProdMode() to mask the issue.
Here is a simple example where the number of cells in the table should increase as the width of the div expands. (Note that the width of the div is not a function of just the screen width, so #Media cannot easily be applied)
My HTML looks as follows (widget.template.html):
<div #widgetParentDiv class="Content">
<p>Sample widget</p>
<table><tr>
<td>Value1</td>
<td *ngIf="widgetParentDiv.clientWidth>350">Value2</td>
<td *ngIf="widgetParentDiv.clientWidth>700">Value3</td>
</tr></table>
This on its own does nothing. I'm guessing this is because nothing is causing change detection to occur. However, when I change the first line to the following, and create an empty function to receive the call, it starts working, but occasionally I get the 'Expression has changed after it was checked error'
<div #widgetParentDiv class="Content">
gets replaced with
<div #widgetParentDiv (window:resize)=parentResize(10) class="Content">
My best guess is that with this modification, change detection is triggered and everything starts responding, however, when the width changes rapidly the exception is thrown because the previous iteration of change detection took longer to complete than changing the width of the div.
Is there a better approach to triggering the change detection?
Should I be capturing the resize event through a function to ensure
change detection occurs?
Is using #widthParentDiv to access the
width of the div acceptable?
Is there a better overall solution?
For more details on my project please see this similar question.
Thanks
To solve your issue, you simply need to get and store the size of the div in a component property after each resize event, and use that property in the template. This way, the value will stay constant when the 2nd round of change detection runs in dev mode.
I also recommend using #HostListener rather than adding (window:resize) to your template. We'll use #ViewChild to get a reference to the div. And we'll use lifecycle hook ngAfterViewInit() to set the initial value.
import {Component, ViewChild, HostListener} from '#angular/core';
#Component({
selector: 'my-app',
template: `<div #widgetParentDiv class="Content">
<p>Sample widget</p>
<table><tr>
<td>Value1</td>
<td *ngIf="divWidth > 350">Value2</td>
<td *ngIf="divWidth > 700">Value3</td>
</tr>
</table>`,
})
export class AppComponent {
divWidth = 0;
#ViewChild('widgetParentDiv') parentDiv:ElementRef;
#HostListener('window:resize') onResize() {
// guard against resize before view is rendered
if(this.parentDiv) {
this.divWidth = this.parentDiv.nativeElement.clientWidth;
}
}
ngAfterViewInit() {
this.divWidth = this.parentDiv.nativeElement.clientWidth;
}
}
Too bad that doesn't work. We get
Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.
The error is complaining about our NgIf expressions -- the first time it runs, divWidth is 0, then ngAfterViewInit() runs and changes the value to something other than 0, then the 2nd round of change detection runs (in dev mode). Thankfully, there is an easy/known solution, and this is a one-time only issue, not a continuing issue like in the OP:
ngAfterViewInit() {
// wait a tick to avoid one-time devMode
// unidirectional-data-flow-violation error
setTimeout(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth);
}
Note that this technique, of waiting one tick is documented here: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-view-child
Often, in ngAfterViewInit() and ngAfterViewChecked() we'll need to employ the setTimeout() trick because these methods are called after the component's view is composed.
Here's a working plunker.
We can make this better. I think we should throttle the resize events such that Angular change detection only runs, say, every 100-250ms, rather then every time a resize event occurs. This should prevent the app from getting sluggish when the user is resizing the window, because right now, every resize event causes change detection to run (twice in dev mode). You can verify this by adding the following method to the previous plunker:
ngDoCheck() {
console.log('change detection');
}
Observables can easily throttle events, so instead of using #HostListener to bind to the resize event, we'll create an observable:
Observable.fromEvent(window, 'resize')
.throttleTime(200)
.subscribe(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth );
This works, but... while experimenting with that, I discovered something very interesting... even though we throttle the resize event, Angular change detection still runs every time there is a resize event. I.e., the throttling does not affect how often change detection runs. (Tobias Bosch confirmed this:
https://github.com/angular/angular/issues/1773#issuecomment-102078250.)
I only want change detection to run if the event passes the throttle time. And I only need change detection to run on this component. The solution is to create the observable outside the Angular zone, then manually call change detection inside the subscription callback:
constructor(private ngzone: NgZone, private cdref: ChangeDetectorRef) {}
ngAfterViewInit() {
// set initial value, but wait a tick to avoid one-time devMode
// unidirectional-data-flow-violation error
setTimeout(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth);
this.ngzone.runOutsideAngular( () =>
Observable.fromEvent(window, 'resize')
.throttleTime(200)
.subscribe(_ => {
this.divWidth = this.parentDiv.nativeElement.clientWidth;
this.cdref.detectChanges();
})
);
}
Here's a working plunker.
In the plunker I added a counter that I increment every change detection cycle using lifecycle hook ngDoCheck(). You can see that this method is not being called – the counter value does not change on resize events.
detectChanges() will run change detection on this component and its children. If you would rather run change detection from the root component (i.e., run a full change detection check) then use ApplicationRef.tick() instead (this is commented out in the plunker). Note that tick() will cause ngDoCheck() to be called.
This is a great question. I spent a lot of time trying out different solutions and I learned a lot. Thank you for posting this question.
Other way that i used to resolve this:
import { Component, ChangeDetectorRef } from '#angular/core';
#Component({
selector: 'your-seelctor',
template: 'your-template',
})
export class YourComponent{
constructor(public cdRef:ChangeDetectorRef) { }
ngAfterViewInit() {
this.cdRef.detectChanges();
}
}
Simply use
setTimeout(() => {
//Your expression to change if state
});
The best solution is to use setTimeout or delay on the services.
https://blog.angular-university.io/angular-debugging/
Mark Rajcok gave a great answer. The simpler version (without throttling) would be:
ngAfterViewInit(): void {
this.windowResizeSubscription = fromEvent(window, 'resize').subscribe(() => this.onResize())
this.onResize() // to initialize before any change
}
onResize() {
this.width = this.elementRef.nativeElement.getBoundingClientRect().width;
this.changeDetector.detectChanges();
}