How to trigger an event when component is navigated to? - javascript

I have an angular2 application (RC5), where I have a chapter component. This basically has a big template file - chapter.html - which has this:
<div *ngIf="chapter == 1">
<!-- Chapter 1 content -->
</div>
<div *ngIf="chapter == 2">
<!-- Chapter 2 content -->
</div>
<!-- etc. -->
I then have some arrow buttons for next and for previous chapters. These trigger an event e.g.
if (this.chapter !== '6') {
var next = parseInt(this.chapter) + 1;
console.log(next);
let link = ['/tutorial/chapter', next];
this.router.navigate(link);
}
So that all works fine, however, the buttons are at the bottom of a chapter, so when they are clicked, the next chapter is displayed, but automatically scrolled down. So on the router click, I would like to trigger an event and scroll to the top of the page, however, as I am navigating to the same component, ngOnInit() isn't triggered.
How can I do this?

If you navigate to the same component, at least a parameter has changed, otherwise the router wouldn't re-navigate.
You can subscribe to parameter changes and invoke scrolling on changes
constructor(private route:ActivatedRoute) {
this.route.params.forEach(p => this.doScroll());
}

constructor(private route:ActivatedRoute) {
this.route.params.subscribe(params => {
//do your stuffs...
});
}

Related

*ngIf not detecting changes on variable change on swiper.js (slideChange) event but detects on swiperRef.slideTo(index ) call

iam passing an array of object containing photo url to swiper container,
for unverified photo iam showing a a text,
on each slide change i have to check photos verification status
the variable is changing to true or false but dom is not updating even when i inspect it shows
ng-reflect-ng-if :'true;
but i have to scroll to a specific slide on user clicking on specific thumbnail
for that iam using
this.photoPopupSlider.swiperRef.slideTo(RowIndex, sliderSpeed);
at this time onSlidChange event triggers updates the dom correctly
my component.html ๐Ÿ‘‡
<div style="padding: 0">
<swiper
[config]="sliderConfig"
class="sliderBox"
[pagination]="false"
(slideChange)="onSlideChange($event)"
#photoPopupSlider
>
<ng-template swiperSlide *ngFor="let pic of myPhotos">
<div
class="slide_wrap"
style="height: 68vh; background: #f3f4f9"
>
<img
[src]="pic.image"
class="slide_img"
style="height: auto; width: auto; max-width: 100%"
/>
</div>
</ng-template>
</swiper>
<span
id="photo_under_verification"
*ngIf="underVerification"
class="ph_uv"
>Photo under verification</span
>
</div>
my component.ts file ๐Ÿ‘‡
underVerification = false;
onSlideChange([swiper]: any) {
const index = swiper.activeIndex;
this._ps.sliderIndex = index;
this.sliderIndex = index;
console.log("sliderChanged", this.sliderIndex);
const photoShowingInSlider = this.myPhotos[index];
const isPhotoUnderVerification =
photoShowingInSlider?.photostatus == "0" ? true : false;
this.underVerification = isPhotoUnderVerification;
console.log("under", isPhotoUnderVerification);
// const photUnderVerifyMsg = document.getElementById(
// "photo_under_verification"
// ).style;
// if (isPhotoUnderVerification) photUnderVerifyMsg.display = "inline";
// else photUnderVerifyMsg.display = "none";
}
actually i was able to implement the logic by using document.getElemntById
those commented lines of code ( i thing it is a bad way in angular to access dom that way )
how can implement this login in angular way ( means not accessing dom directly ) ?
Well, you have to tell Angular to run change detection as the code under onSlideChange (and other events) runs outside of Zone
Note that Swiper Angular component all events emits outside of NgZone
for better perfomance. Dont forget to use ngzone.run or
ChangeDetector if you need to change view (e.g slides) in event
handlers (e.g slideChange).
import the ChangeDetectorRef in the constructor and call the detectChange method in onSlideChange method
this.cd.detectChanges()
Here is the working code. I just take a random stackblitz link so adapt to your swiper version: https://stackblitz.com/edit/swiper-angular-example-rmgv2b

Angular not detecting changes in ngFor in a new window from a postMessage

Background: my main page opens up an external window (same origin) of another module within my project upon button click.
I also set up a BroadcastChannel so that these two windows can now communicate. Now, if this window is already open & the user clicks the triggering button once again, I want to communicate this to the window:
onAddNewFieldClick() {
if (this.window === null) {
this.window = window.open(window.location.origin + '/wizard', 'Field Wizard', 'resizable,scrollbar');
this.channel = new BroadcastChannel('edit-spec-wizard-channel');
} else {
this.channel.postMessage(1);
}
}
The new window listens on this channel and appends the message data to an array that is used in an ngFor. To be extra safe. I go ahead and create a brand new array each time a new value is pushed to cause a rebind. Here is the logic that powers the component in the new window.
export class EntryComponent implements OnInit, OnDestroy {
newFieldChannel: BroadcastChannel;
newFields: number[] = [];
constructor() { }
ngOnInit() {
this.newFieldChannel = new BroadcastChannel('edit-spec-wizard-channel');
this.newFieldChannel.onmessage = this.newFieldChannelOnMessage.bind(this);
this.newFields.push(1);
}
func() {
this.newFields.push(1);
this.newFields = this.newFields.slice();
}
private newFieldChannelOnMessage(event: MessageEvent) {
this.newFields.push(event.data as number);
this.newFields = this.newFields.slice();
}
ngOnDestroy() {
this.newFieldChannel.close();
}
}
And here is the template HTML:
<div class="row">
<div class="col" *ngFor="let newField of newFields">
<div style="width: 300px; height: 600px; background-color: white;">
NEW FIELD BOX
</div>
</div>
<button class="btn btn-primary" (click)="func()">Click me</button>
</div>
I have also included a button that triggers a function ("func()") that has the exact same logic as the postMessage handler.
Now, when I click the button in this window, I'll get the expected behavior: The correct number of "NEW FIELD BOX" divs will appear in this new window. However, when I press the original button from the main screen that posts a message over the BroadcastChannel, it WILL NOT update the UI to display the right number of "NEW FIELD BOX" divs. Using break points I can see that the array newFields does contain the right number of values, but ngFor does not re-render.
Example: I click the button on the main page that fires the onAddNewFieldClick(). It opens a new window which has one "NEW FIELD BOX" div. I click this button again which posts a message to add another. Still, only one remains on the window. I now click the button within the window that fires the function "func()." This will now render 3 "NEW FIELD BOX" divs (the original one from initialization, the one from the post message that didn't render, and the one from clicking this button).
Any ideas why change detection doesn't seem to happen from a postMessage?
The newFieldChannelOnMessage event handler is probably running outside of the Angular zone and does not trigger change detection. Try wrapping the code in NgZone.run():
import { NgZone } from "#angular/core";
constructor(private ngZone: NgZone) { ... }
private newFieldChannelOnMessage(event: MessageEvent) {
this.ngZone.run(() => {
this.newFields.push(event.data as number);
});
}

React : Pagination is not Working Smoothly

I am working on pagination in React . I am fetching data from online server through API . API was built in Loopback . I have some problem regarding next and previous button . I make logic for pagination if user want to render next page data it will click on next , If User want to load previous page data it click on previous button . With my logic Next button is working fine , it rendering the next page data if user click on next button but problem is that if user want to click on previous button it loading next page data not like previous page data. Somebody please help me how I can make this type of pagination if user click on next it must load next page data or user want to load previous page data it must load previous page with prev button .
Code
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
Item: 5,
skip: 0
}
this.handleClick = this.handleClick.bind(this);
}
urlParams() {
return `http://localhost:3001/meetups?filter[limit]=${(this.state.Item)}&&filter[skip]=${this.state.skip}`
}
handleClick() {
this.setState({skip: this.state.skip + 1})
}
render() {
return (
<div>
<a href={this.urlParams()}>Example link</a>
<pre>{this.urlParams()}</pre>
<button onClick={this.handleClick}>Change link</button>
</div>
)
}
}
ReactDOM.render(<Example/>, document.querySelector('div#my-example' ))
You have just write btnClick(e) in one way that adding the skip:skip+10 but if you need previous data it should subtract skip:skip-10
So the solution is
btnClick(e,type){
const Item=this.state.Item;
const skip=this.state.skip;
If(type === โ€œnextโ€){
this.setState({
Item,
skip:skip-10
},()=> this.getData())
} else {
this.setState({
Item,
skip:skip+10
})
}
}
// call function this.btnClick(e, โ€œnextโ€)
Or prev for pervious
If you look at the documentation for the Pagination component at https://react.semantic-ui.com/addons/pagination/ , You will see that you are passing this.btnClick into onClick={this.btnClick} when it should be passed into onPageChange={this.btnClick}
<Pagination
boundaryRange={0}
onPageChange={this.btnClick}
defaultActivePage={1}
ellipsisItem={null}
firstItem={null}
lastItem={null}
siblingRange={1}
totalPages={10}
/>
The parameters that are passed into onPageChange are as such:
onPageChange(event: SyntheticEvent, data: object)
It is not clear the difference between Item and Skip in state and what you are trying to accomplish with your btnClick function. It does not look like you are accounting for the possibility that you could click a numbered page on the pagination component instead of just the nextPage and PrevPage` buttons.
I'm going to assume Item indicates the current page you want to be on, in which case inside your btnClick function, you can grab the next active page from the data parameter (this will return the paginated number of the next page - whether you click that number directly or click nextPage).
btnClick(event, data){
const nextPage = data.activePage;
this.setState({item: nextPage});
}

Displaying Go To Top button when page becomes scrollable

Just wondering How I can do this in Angular 2/4 : This might be easy but I just can't figure out.
Here is my code:
Let me explain it, I have a component which scrolls me to the top of the page, when I am at the bottom. But the floating div i.e, little red arrow always stays visible even when page need not scroll.
In Html:
Each button is dynamically linked to div. So div displays when button is clicked
<div *ngFor="let sampledata of SAMPLEDATA; trackBy: trackId">
<button (click)="transmitInfo(sampledata ,0)" > </button>
<div *ngFor="let data of sampledata .data; trackBy: trackId" >
<button (click)="transmitInfo(data,1)" > </button>
</div>
<!-- This keeps on going -->
</div>
<div>
<div *ngIf="renderdata === 0"> {{Object Data}}</div>
<div *ngIf="renderdata === 1">{{Object Data}}</div>
<div *ngIf="renderdata === 2">{{Object Data}}</div>
</div>
<div id="scroolUpRight">
<img src="../../../content/images/scrollup.png" width="50px" height="50px" (click)="scrollToTop()">
</div>
Let's assume when a user clicks on button 2 or 3, 2nd or 3rd div is displayed based on button clicked, this div's are a huge data. Page automatically becomes scrollable when these are activated.
In Css:
#scroolUpRight {
position: fixed;
bottom: 4%;
right: 2%;
}
#scroolUpRight :hover {
cursor: pointer;
}
In my Component I have this to take me to the top of the page:
ngOnInit() {
this.renderdata = 0;
}
transmitInfo(data, type): void {
if (type === 1) { this.sampleData = data; this.renderdata = 1; }
if (type === 2) { this.dataData = data; this. renderdata = 2; }
}
scrollToTop() {
return window.scrollTo(0, 0);
}
Now I don't know if this works but I did this:
toogleScroolButton(): void {
if (window.screenY > 300 ) {
console.log('window length is 300 +');
}
}
But this is a function. How can I make a function or component that auto detects when page becomes scrollable and display this div, hide it when not scrollable.
Expected Result : Is to make this div visible once person starts to scroll.
Previous Knowledge:
I used Javascript and Jquery before to do the same. But how do I use
angular2,4 or higher for this? Reason I need this is to animate this div when
person starts to scroll.
I do accept recommendations to optimize the above code. Please do let me know if any.. ;)
This Worked. I need to get HostListener to get windows scroll even to see if I can scroll the page.
window.scrollY gives me the scroll page size which helps me in finding out if I am scrolling my page. If scrollY reaches to certain count I can say I am scrolling down i.e, I can trigger an *ngIf to true if I am scrolling bottom else I can make it false. Code Below :)
Add
import { HostListener } from '#angular/core';
export class BlaBlaBla {
//And this did the trick
activateGoTop : boolean;
OnNgInit :: activateGoTop = false /* added Silly Reference please put this in ngOnInit() { --- }*/
#HostListener('window:scroll',[])
onWindowScroll() {
if ( window.scrollY > 100 ) {
this.activateGoTop = true;
} else {
this.activateGoTop = false;
}
}
}
in Html:
//Gets activated when screenY is scrolled for more than 100px
<div id="scroolUpRight" *ngIf="activateGoTop">
<img src="../../../content/images/scrollup.png" width="50px" height="50px" (click)="scrollToTop()">
</div>
Hope this helps someOne .. ;)
You can use a simple *ngIf binding with your method:
<div *ngIf="scrollButton()">
Top <button>up button</button>
</div>
with scrollButton() method simple as that:
public scrollButton():boolean {
return window.screenY > 300;
}
The div will only get rendered if scrollButton() method returns true, this allows you to customize your top button render conditions easily, because you only need to return a boolean from it.

Specifying a default flatiron-director route (inside element) via polymer core-pages component

This question is directly related to: flatiron-director / core-pages SPA with route specific js functions & default route . I'm sure that solution works, but I'm a little too inexperienced with polymer (and js) to determine the correct event listener in my circumstance:
How/where would you specify an appropriate event listener to set the default route if the flatiron-director is used inside a polymer element, particularly when the element's template itself does not use is="auto-binding". In this case, and to be clear, the index.html page which imports the element shown below does in fact specify a template using is="auto-binding".
Here is the element code to show what I am attempting to communicate / achieve. The flatiron routing is working (if I manually enter #itemsList or #itemOpen into the URL and use browsers previous or next buttons), but it does not add the default #itemsList to the URL automatically when hitting index.html on its own:
<polymer-element name="my-app" attributes="user items connected">
<template>
<flatiron-director id="page-director" route="{{route}}" autoHash on-director-route="{{ routeChanged }}"></flatiron-director>
<!-- HIGH LEVEL APP LAYOUT ELEMENT -->
<core-header-panel id="appHeader" mode="standard">
<!-- OUTER APP TOOLBAR ELEMENT -->
<core-toolbar id="appToolbar">
<paper-icon-button id="navicon" icon="arrow-back" on-tap="{{ showItems }}"></paper-icon-button>
<span flex>App Name</span>
<paper-icon-button id="searchbutton" icon="search"></paper-icon-button>
</core-toolbar>
<!-- MAIN CONTENT ELEMENTS -->
<!-- ATTEMPT FLATIRON ROUTING -->
<core-pages id="mainPages" selected="{{route}}" valueattr="name">
<my-items-element name="itemsList" on-core-activate="{{ itemSelect }}" user="{{user}}" items="{{items}}" item="{{item}}"></my-items-element>
<item-open-scaffold-element name="itemOpen" user="{{user}}" item="{{item}}" hidden></item-open-scaffold-element>
</core-pages>
</core-header-panel>
</template>
<script>
Polymer('my-app', {
route: "itemsList",
itemSelect: function(e, detail, sender) {
if (sender.shadowRoot.activeElement == null || sender.shadowRoot.activeElement.nodeName != "PAPER-MENU-BUTTON"){
// Ensure the user hasn't clicked on the item menu dropdown to perform alternative actions (or another element with actions for that matter)
// (i.e. make sure the user intends to open the item)
this.openItem();
}
},
openItem: function() {
this.$.mainPages.children.itemOpen.hidden = false;
this.$.mainPages.selected = "itemOpen";
//this.route = "scaffoldPage";
},
showItems: function() {
this.$.mainPages.children.itemOpen.hidden = true;
this.$.mainPages.selected = "itemsList";
}
});
</script>
<script>
var template = document.querySelector('template');
template.addEventListener('template-bound', function() {
this.route = this.route || "itemsList";
});
</script>
As noted by Jeff, use ready() lifecycle method as intra-element equivalent to template-bound event outside of element. So...based on the example above, its as simple as including the following line within polymer element's ready():
this.route = this.route || "itemsList"

Categories

Resources