I am creating a webapp with vueJs and bootstrap. I want to change CSS class of an element after a particular amount of scroll, Is there some vue way of doing it.
I want something like following:
<div :class="{classA: scrollPosition < 100, classB: scrollPosition > 100}">
</div>
One option I found is by using vue-scroll, which seems promising, but not working.
Is there some other native way as well to achive the same?
You could try to make it like this
const app = new Vue({
el: '#app',
data: {
scrollPosition: null
},
methods: {
updateScroll() {
this.scrollPosition = window.scrollY
}
},
mounted() {
window.addEventListener('scroll', this.updateScroll);
}
})
You should also consider removing event listener when component is being destroyed, in order to prevent leaks:
destroy() {
window.removeEventListener('scroll', this.updateScroll)
}
Related
I am developing the drag and drop application in Angular 6 and on drop operation, I am creating the new HTML element (elements like 'DIV', 'textarea' etc.), dynamically and assign styles, attributes, default x and y coordinates to it by using ElementRef and Renderer2 features of application and adding the newly created HTML element to parent element on which drop operation is being performed.
I also have an angular directive created which attaches draggable behavior to the newly created HTML element so user can move it anywhere on the parent element. The partial code for directive is given below:
#Directive({
selector: '[appMovable]'
})
export class MovableDirective {
#HostBinding('style.transform') get transform(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(
`translateX(${this.position.x}px) translateY(${this.position.y}px)`);
}
private position = {x: 100, y: 0};
constructor(private sanitizer: DomSanitizer) {
}
#HostListener('dragStart') onDragStart() {
console.log('drag start');
// Code to calculate the start position of the control being dragged....
}
#HostListener('dragMove') onDragMove() {
console.log('drag move');
// Code to calculate the current position of the control while it is being dragged....
}
#HostListener('dragEnd') onDragEnd() {
console.log('drag end');
// Code to calculate the end position of the control being dragged and the the position of the control properly.....
}
}
While I am able to assign the styles, attributes and default x and y coordinates to the newly created element but I am not able to bind the 'appMovable' directive to newly created HTML element. The code to create the HTML element and assigning different attributes to it is as below:
#Directive({
selector: '[appDroppable]'
})
export class DroppableDirective {
constructor(private elementRef: ElementRef, private renderer: Renderer2) {
}
#HostListener('dragover', ['$event'])
public onDragOver(evt) {
evt.preventDefault();
evt.stopPropagation();
}
#HostListener('dragleave', ['$event'])
public onDragLeave(evt) {
evt.preventDefault();
evt.stopPropagation();
}
#HostListener('drop', ['$event'])
public onDrop(evt) {
evt.preventDefault();
evt.stopPropagation();
// Since text element is being dragged so create new textarea html control
const textareaElement = this.renderer.createElement('textarea');
this.renderer.setAttribute(textareaElement, 'placeholder', 'click to add content...');
this.renderer.setAttribute(textareaElement, 'class', 'proInput editing');
this.renderer.setAttribute(textareaElement, 'draggable', 'true');
//Assign the appMovable directive to element
this.renderer.setAttribute(textareaElement, 'appMovable', '');
// this.renderer.appendChild(newDivElement, textareaElement);
this.renderer.appendChild(this.elementRef.nativeElement, textareaElement);
}
}
When I inspect the newly created HTML element in the browser debugger tool, I can see the appMovable directive getting assigned to the HTML element but element does not behave as per the directive assigned to it.
Is there anything else needs to be done or there is any alternate option to get directive work properly with dynamically created HTML elements?
I'm using React with Leaflet, and want to launch the drawing menu immediately upon the component mounting, without making the user click any buttons. The React Leaflet Draw API is a bit opaque on this, and what I'd like to do to make this simple is to trigger a click on the appropriate button programmatically, without the user having to. I'll then hide the button.
The trouble is that I'm not having any luck either using the .click() or the MouseEvent('click') APIs. Here's my attempt at the latter:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../actions';
import { Polygon, FeatureGroup } from 'react-leaflet';
import { EditControl } from 'react-leaflet-draw';
export class DrawNewPlot extends Component {
componentDidMount() {
let simulateClick = elem => {
let evt = new MouseEvent('click', {
bubbles: true,
view: window
});
};
let drawControl = document.getElementsByClassName('leaflet-draw-toolbar');
simulateClick(drawControl);
}
render() {
return (
<FeatureGroup>
<EditControl
position="bottomright"
onEdited={e => {
e.layers.eachLayer(a => {
this.props.setNewPlotGeojson(a.toGeoJSON());
});
}}
onCreated={e => {
this.props.setNewPlotGeojson(e.layer.toGeoJSON());
}}
draw={{
marker: false,
circle: false,
rectangle: false,
polygon: true,
polyline: false,
circlemarker: false,
edit: false
}}
edit={{ edit: false }}
/>
</FeatureGroup>
);
}
}
function mapStateToProps(state) {
return {
addingNewPlotDetails: state.plots.addingNewPlotDetails
};
}
export default connect(mapStateToProps, actions)(DrawNewPlot);
Any thoughts as to what I'm doing wrong?
Your simulateClick method creates the event, but never dispatches it. Try adding elem.dispatchEvent(evt);
Although I must add that simulating mouse click this way just to trigger some initial side effect feels wrong. I am not familiar with Leaflet, but it could be worth checking if they have some API to set initial state
want to change a class of an element when the width of browser changes
have that in my .ts
matchMedia('(max-width: 400px)').addListener((mql => {
if (mql.matches) {
this.myclass = 'toggled';
}
}));
and in the html somthing like that:
<app-side-bar [ngClass]="myclass"></app-side-bar>
value of 'myclass' is changed but the HTML element(app-side-bar) is not getting updated -what am I missing here?
Because Angular does keep track of the the event that occurs when the browser size changes, it wont detect the change. You have to trigger it yourself:
You can do this by warpping the code inside NgZone:
import { NgZone } from '#angular/core';
// Inject NgZone in your constructor:
constructor(private zone: NgZone) {
}
// Run the code that changes state inside the zone
matchMedia('(max-width: 400px)').addListener((mql => {
if (mql.matches) {
this.zone.run(() => {
this.myclass = 'toggled';
});
}
}));
A vue.js component creates a button which should call a function, but the function is never called and the v-on:click is not visible in Chrome's element inspect. My html goes like this:
<bottomcontent></bottomcontent>
And my Vue is like this:
var bottomcontent = {
template: '<div class="bottomcontent"><div class="moreresults" v-on:click="appendcontent">More Results</div></div>'
}
new Vue({
el : 'body',
data : {
homepage:{
numberofdivs: 60
}
},
methods : {
appendcontent: function() {
homepage.numberofdivs += 60
}
},
components: {
'bottomcontent': bottomcontent
}
})
The problem is that methods has to use funcions, not objects.
methods: {
appendcontent: function() {
homepage.numberofdivs += 60
}
}
You also have to correct your markup accordingly.
var bottomcontent = {
template: '<div class="bottomcontent"> <div class="moreresults" v-on:click="appendcontent"> More Results </div></div>'
}
There are some problems lead to the crack.
In the function appendcontent,you should call the data "this.homepage.numberofdivs".
and the correct demo is posted on https://jsfiddle.net/atsknydr/
methods : {
appendcontent: function() {
this.homepage.numberofdivs += 60;
console.log(this.homepage.numberofdivs);
}
}
First, as the warning says:
[Vue warn]: Do not mount Vue to <html> or <body> - mount to normal elements instead.
So, you should create an element like <div id="app"></div> to mount your app instead of <body>.
The problem you are facing is a scope problem. You are trying to call a method from the inside a component scope, that is why it's not finding the method.
Take a look at the docs to understand better.
So, in order to make this work you should change the method from the app scope to the template scope.
Your html:
<body>
<div id="app">
<bottomcontent></bottomcontent>
</div>
</body>
Your js:
<script>
var bottomcontent = {
template: '<div class="bottomcontent"><div class="moreresults" v-on:click="appendcontent">More Results</div></div>',
data: function () {
return {
homepage: {
numberofdivs: 60
}
}
},
methods: {
appendcontent: function () {
console.log('Called method!');
this.homepage.numberofdivs += 60
}
}
}
new Vue({
el: '#app',
components: {
'bottomcontent': bottomcontent
}
})
</script>
I need to get scroll events from a div with overflow: scroll in my Angular 2 app.
It seems onscroll event do not works on Angular 2.
How could I achieve that?
// #HostListener('scroll', ['$event']) // for scroll events of the current element
#HostListener('window:scroll', ['$event']) // for window scroll events
onScroll(event) {
...
}
or
<div (scroll)="onScroll($event)"></div>
You could use a #HostListener decorator. Works with Angular 4 and up.
import { HostListener } from '#angular/core';
#HostListener("window:scroll", []) onWindowScroll() {
// do some stuff here when the window is scrolled
const verticalOffset = window.pageYOffset
|| document.documentElement.scrollTop
|| document.body.scrollTop || 0;
}
for angular 4, the working solution was to do inside the component
#HostListener('window:scroll', ['$event']) onScrollEvent($event){
console.log($event);
console.log("scrolling");
}
Listen to window:scroll event for window/document level scrolling and element's scroll event for element level scrolling.
window:scroll
#HostListener('window:scroll', ['$event'])
onWindowScroll($event) {
}
or
<div (window:scroll)="onWindowScroll($event)">
scroll
#HostListener('scroll', ['$event'])
onElementScroll($event) {
}
or
<div (scroll)="onElementScroll($event)">
#HostListener('scroll', ['$event']) won't work if the host element itself is not scroll-able.
Examples
Using Event Binding
Using HostListener
Alternative to #HostListener and scroll output on the element I would suggest using fromEvent from RxJS since you can chain it with filter() and distinctUntilChanges() and can easily skip flood of potentially redundant events (and change detections).
Here is a simple example:
// {static: true} can be omitted if you don't need this element/listener in ngOnInit
#ViewChild('elementId', {static: true}) el: ElementRef;
// ...
fromEvent(this.el.nativeElement, 'scroll')
.pipe(
// Is elementId scrolled for more than 50 from top?
map((e: Event) => (e.srcElement as Element).scrollTop > 50),
// Dispatch change only if result from map above is different from previous result
distinctUntilChanged());
To capture scroll events and see which of the scroll event is being called, you have to use host listener who will observe the scroll behavior and then this thing will be detected in the function below the host listener.
currentPosition = window.pageYOffset;
#HostListener('window:scroll', ['$event.target']) // for window scroll events
scroll(e) {
let scroll = e.scrollingElement.scrollTop;
console.log("this is the scroll position", scroll)
if (scroll > this.currentPosition) {
console.log("scrollDown");
} else {
console.log("scrollUp");
}
this.currentPosition = scroll;
}
Check the multiple examples as mention on this URL.
I will recommend the method 3,
https://itnext.io/4-ways-to-listen-to-page-scrolling-for-dynamic-ui-in-angular-ft-rxjs-5a83f91ee487
#Component({
selector : 'ngx-root',
templateUrl : './app.component.html',
styleUrls : [ './app.component.scss' ],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnDestroy {
destroy = new Subject();
destroy$ = this.destroy.asObservable();
constructor() {
fromEvent(window, 'scroll').pipe(takeUntil(this.destroy$))
.subscribe((e: Event) => console.log(this.getYPosition(e)));
}
getYPosition(): number {
return (e.target as Element).scrollTop;
}
ngOnDestroy(): void {
this.destroy.next();
}
}
However Method 4 is not bad.