I will preface this by saying that there are several answers online for this exact same purpose with an Angular app. However, none of these appear to be working for me.
I have so far tried nearly everything from these issues
1 , 2 , 3 , 4 and a couple of other but I am still not getting the correct behavior.
Currently, if I am scrolled down and viewing a route, then I click on the item to view a new route via routerLink, the page stays in the same position. This causes some issues especially when on a smaller screen or when looking down a long list of items.
In the current app, my AppComponent is as follows (I am using Angular Material):
<app-header></app-header>
<mat-sidenav-container class="main-sidenav">
<mat-sidenav class="sidenav"#sidenav [mode]="mode" [opened]="openSidenav">
<app-sidenav></app-sidenav>
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
The router-outlet is displaying one of the following:
A workout page with a list and filter
A workout "detail page" that shows the workout description
In the workout list, when an item is clicked, it has a routerLink: <a [routerLink]="[i]"> where i is the index of the current workout coming from an array (from a firebase database in my case).
Here is the stackblitz to this code. The workouts-page can be found at src/app/features/workouts-page/ and then the workout-list, workout-filter, and workout-detail showing up accordingly. However, when clicked on the routerLink, the scroll position stays the same on the workout detail page.
Since Angular 6+, the recommended method has been to use, within your routing module:
...
imports: [RouterModule.forRoot(routes,{scrollPositionRestoration: 'top'})
...
At the same time, you could also use "enabled" and get the same result. This does not change any behavior in the application I am running. Furthermore, I have tried a few "work-arounds" such as adding the following to my main component:
export class MyAppComponent implements OnInit {
constructor(private router: Router) { }
ngOnInit() {
this.router.events.subscribe((evt) => {
if (!(evt instanceof NavigationEnd)) {
return;
}
window.scrollTo(0, 0)
});
}
}
This workaround and many others have once again not changed the behavior and it leaves the page in the same scroll position as before the router was clicked. One different method from the GitHub request is to add an (activate)="onActivate($event) to the <router-outlet>, where the function runs window.scrollTo(0, 0);. Still, this doesn't seem to be working. I can even put the window.scrollTo(0,0) or scrollTop() in the ngOnInit of any component related to the router outlet, and you guessed it, still no change.
I figured this had to do with the css of the page altogether so I changed my body and html positioning as mentioned in stack overflow issues with the same end result.
Is there a way that I can "reset" the scroll position when a route is clicked with the routerLink property so the page doesn't stay in the same scrolled position when the route is loaded?
ScrollPositionRestoration works properly with full page scroll (body getting scrolled).
In your case internal divs are scrollable. To restore scroll for those you need to customize the scroll restore process.
Here is a blog link which talks about the same https://www.bennadel.com/blog/3534-restoring-and-resetting-the-scroll-position-using-the-navigationstart-event-in-angular-7-0-4.htm
Base idea is to storage scroll position before navigation and restore them if you visit the page due to pop state.
Nikhil gave a method for the ScrollPositionRestoration and helped me find the reason this wasn't working, but I ended up finding a way to do this with a simple activate function.
Like he mentioned, the reason this wasn't working is due to the "body" being reset on the scroll. Since the fixed header was at the top of the body, it was already in the "top" scroll position all the time. To fix this, I added the following simple code:
In the AppComponent HTML
...
<mat-sidenav-content id="detail">
<router-outlet (activate)="resetPosition();"></router-outlet>
</mat-sidenav-content>
...
Note, the (activate)="resetPosition(); causes the function to run any time a router is activated (or any time you go to a new router). Here is the function added to the app.component.ts:
AppComponent TS
resetPosition() {
let myDiv = document.getElementById("detail");
myDiv.scrollTop = 0;
}
This takes the id="detail" from the html container and then sets the scroll position to the top of this div rather than the entire body which was already at the top.
Related
I am fighting with a small issue for a few hours.
I am working on Angular-9, in the HTML page, I have a very long table(500 rows data). when I going to the bottom of the table and then refresh the page, it will be on the bottom of the page. not scrolling to the top of the page.
but whenever I refresh the page, the page should go to the top of the HTML page(top of the table).
Actually, I have searched a lot of google solutions, but no one is work.
Can u please suggest how to resolve this?
Add this in js file.
$(this).scrollTop(0);
If you have a header component you can directly try this, or just make a empty div at the top of the page with id header and paste this in your main function.
Make sure of the ID or it will not work.
const header= document.getElementById('#header');
header.scrollIntoView();
On your template, add a template variable to your scrollable container like this:
<div #myScrollableContainer>
<!-- Here is your content that scrolls -->
</div>
And on your component get that one with #ViewChild and set the scrollTop on that.
#Component({ /*...*/ })
export class MyComponent {
// pass the name of the template variable you picked with # on your template
#ViewChild('myScrollableContainer') private myContainer:ElementRef<HTMLDivElement>;
// Call this when you want to scroll to top
public scrollToTopOfScrollable():void {
this.myContainer.nativeElement.scrollTop = 0;
}
}
I am experiencing the strangest issue. I do have a component that's style is controlled by some object it's handling. As the object is manipulated (by some inner component click), the whole view collapses to a height of 0.
I have tried to apply the view changes via ngStyle, ngClass, plain ol'javascript - but nothing ever worked. Second strange thing - handling outer component's component styles is perfectly working.
Object
const valueMap = {
width: '100%',
...
}
Pseudo Component Tree
<component-a>
<component-b [ngStyle]="{'width': valueMap.width} [valueMap]="valueMap">
<div (click)="changePosition()">Button</div>
</component-b>
</component-a>
Method
changePosition() {
this.valueMap.width = this.valueMap.width==='100%' ? '50%' : '100%';
}
I would really expect the component-b to collapse to a width of 50%, but not having the height collapse to zero. I can see in the code, that as I click the button the style is applied to the element - and if in Chrome DevTools I deactivate the applied style and activate it again, it is showing properly. Never had something like this. Can someone please help?
Thanks in advance. André
I tried to recreate your problem. altough i couldnt get the error you seem to be having, i think i can see the issue at hand:
from your html
<component-a>
<component-b [ngStyle]="{width: 'valueMap.width'} [valueMap]="valueMap">
<div (click)="changePosition()">Button</div>
</component-b>
</component-a>
i could get, that your "valueMap" is an Input() within component-b.
so i did that on my end aswell and declared valuemap on the outermost component (the one using component a and b)
i recreated your html and found parsing errors and whatnot until i realized something:
<app-collapse-on-click>
<app-collapse-child [ngStyle]="{'width': valueMap.width, 'background-color': 'rebeccapurple'}" [valueMap]="valueMap">
<div><button (click)="changePosition()">Button</button></div>
</app-collapse-child>
</app-collapse-on-click>
when using ngStyle, the style is interpreted as an object. meaning, in your case you need to remove the ' arround valueMap.width and add them to width.
i added the background color to get a visual result other than just the style html.
hope that somehow helps.
regards
Alan
I have created two components in Angular 2:
ReaderComponent: The one that initiates and controls all functionality to Owl Carousel (initiate, add slide, remove slide and so on)
PageComponent: Each slide is a PageComponent and has events to handle input from the user (click, pinch, doubletap)
The ReaderComponent is created at start of the application and initiates a request to a service to get all data for each of the PageComponents.
Everything works fine until we add a slide that is a PageComponent. I have tried to add the PageComponent selector to owl Carousel:
this.slider.trigger("add.owl.carousel", ["<my-page-component></my-page-component>"]);
This does add an element of <my-page-component> but does not render the template or handles any of the PageComponents events.
I have tried to add all the PageComponents to an array and render it in ReaderComponents template:
<div *ng-for="#page of pages">
<my-page></my-page>
</div>
This renders correct but by that time all pages is rendered Owl is already initiated and no pages is visible.
So to summarize all of this: I need to know how to add a custom component via javascript (in this case the add functionality of Owl)? Is this even possible? Or is there another way to handle this so that I can add PageComponent in any way?
The first method you mentioned would require you to force angular to re-check it's bindings. This is probably possible, but I don't know off the top of my head.
The second method is much easier. You can use the lifecycle events of the Page or Reader Components to trigger the adding. They are as follows:
export var LIFECYCLE_HOOKS_VALUES = [
LifecycleHooks.OnInit,
LifecycleHooks.OnDestroy,
LifecycleHooks.DoCheck,
LifecycleHooks.OnChanges,
LifecycleHooks.AfterContentInit,
LifecycleHooks.AfterContentChecked,
LifecycleHooks.AfterViewInit,
LifecycleHooks.AfterViewChecked
];
If you add a listener to your PageComponent class, you can probably use OnInit or AfterViewChecked and then get it to add it's own element reference to the carousel (basic example). From a quick look at their documentation, it doesn't look like owl supports adding a new element, so you could have the PageComponents all on your page somewhere hidden and then just add the raw html from the elementref, then remove it again in the OnDestroy function.
If you do it in ReaderComponent you should look at OnChanges or add some clever checks into the DoCheck function and then just get it to reload all items inside it (perhaps owl.reinit?). I've not used the owl carousel before so can't be more specific there I'm afraid.
These are exported from the angular class as interfaces, so you should be extending your classes from them. An example is available on the Angular 2 website here: https://angular.io/docs/ts/latest/api/lifecycle_hooks/AfterViewChecked-interface.html
I have a web site that exists on one page: index.html. There is a lot of content on the site (that appears to be on many "pages") but via javascript and CSS, all the info is contained on index.html.
So there exists a "home" position and an "inside" position (like a home page and inside page), and I need some links to behave differently when the user is on an inside page vs the home page. So the way I have it set up, once an "inside" link is clicked, I remove a class from a div that I think should cause the links within that div to behave differently. But they are not behaving as I expect.
The page, very dumbed down for this example, is here:
http://littleduck.com/ns_sample/index.html
On this example, there is just the home page and the "Services" page. You can link back and forth between them.
If you mouseover those grey links on the left (which are called "balloons" in the code), you will see that they have a hover color, and a popup graphic appears. I ONLY want this to happen when the page is in the "home" position. I have a class called "popup_yes" that allows this hover/popup action to happen. It appears when index.html is loaded, and if I remove it or change its name in the code, the hover/popup does not work. So I know that class is doing something. Now, I REMOVE that class when "Services" is clicked. I can see by inspecting the element in Chrome that "popup_yes" DOES in fact get removed. HOWEVER ... the hover/popup action still happens when you mouseover the balloons.
And when I inspect the element, even though you can see in the code that "popup_yes" is gone, it is still being utilized by Chrome. Here is a screenshot of what Inspect Element looks like on the "home" and "inside" pages:
http://i.stack.imgur.com/p1ZP7.jpg
So, please tell me where my brain is derailing. How can I get the hover/popup action to NOT WORK when I'm on the "Services" page? Thank you incredibly much for any help you can provide.
The issue is in your mouseover/out code
$(".popup_yes .balloon_1").mouseover(function() {
$("#popup_1").show(400);
$(".balloon_1").addClass("hover");
});
When the page loads, the mouseover event is binding to that span, so that action is already established, if you add the check in the function
$(".popup_yes .balloon_1").mouseover(function() {
if ($('#balloons').hasClass('popup_yes') ){ //<--here
$("#popup_1").show(400);
$(".balloon_1").addClass("hover");
}
});
it should work as expected
I have modified the javascript that you are using and could make it work. Here are the changes that i made.
First of all when u removed the class when the services page is clicked I specified the class to be removed.
$('#balloons').removeClass("popup_yes");
After this when you click back the Home page the class has to be added back so i included this line after you animate the $('#buttons') div.
$("#balloons").addClass("popup_yes");
Now comes the changing of the hover effect on each of the balloons. I'll show the change I did to the first balloon element you can reproduce the same for the others.
$(".balloon_1").mouseover(function() {
if($(this).closest('#balloons').attr("class")=="popup_yes"){
$("#popup_1").show(400);
$(".balloon_1").addClass("hover");
}
});
$(".balloon_1").mouseout(function() {
if($(this).closest('#balloons').attr("class")=="popup_yes"){
$("#popup_1").hide(400);
$(".balloon_1").removeClass("hover");
}
});
What I modified is instead of checking for both the class for hovering I just checked the balloon_1 class and then I made the effect only if the closest element to it with an id balloons has the class popup_yes.
I tried it in chrome. Hope it works well in other browsers as well.
I bounced into a problem when I was trying to create a title on the first view I created within my application.
At first, i hopped over all the push actions I had within my application and applied after this create.view screen a title as refered in the code under here
this.StembureauView = Ext.create('UtrechtStem.view.Stembureaulijst_View', {
title: 'Stembureaulijst'
});
this.getMainnav().push(this.StembureauView);
this works fine, and I seem to have no problem with it. However, the first screen, so my main view, as loaded first view, doesnt accept the title statement.
Ext.create('UtrechtStem.view.MainNav', {
fullscreen: true,
title:'stembureau zoeker'
});
if i add nicely behind the fullscreen statement a title statement, it wont accept it. (this is done outside sencha architect, cause appearantly i cant modify it within sencha artchitect) It just shows blank. I tried removing the toolbar, to see if that was the mistake, but it didnt. I tried addding manualyy outside Sench
Does anybody have a clue why it wont accept the title statement there and to see if there is a work arround?
guess your class UtrechtStem.view.MainNav is inherited from the Ext.navigation.View. If yes, you can not set its title direct. Instead it borrow the active child's title.
Have a look at the doc site
Cheers, Oleg