I'm trying to create an observable that will emit a value of true or false if the window screen is less than 520px or not. So I have this code below:
@Injectable({
providedIn: 'root'
})
export class ResizeService {
private mobileView$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
mobileViewObs$ = this.mobileView$.asObservable();
isMobile = false;
size!: {width: number, height: number};
constructor() {
}
resizeEvent(){
fromEvent(window, 'resize').pipe(throttleTime(500), debounceTime(500)).subscribe((resizeEvent: any) => {
this.size = {
width: +resizeEvent.currentTarget.innerWidth,
height: +resizeEvent.currentTarget.innerHeight
};
this.isMobile = this.isMobileView(this.size);
this.mobileView$.next(this.isMobile);
});;
}
isMobileView(size: {width: number, height: number}){
if(size.width < PHONE_SIZE.width && size.height < PHONE_SIZE.height){
return true;
} else {
return false;
}
}
}
It is working fine, but the problem is I can't set the initial size of a page. I don't know how to send the size object from the component to the service. Here is my component.ts:
@Component({
selector: 'app-books-list',
templateUrl: './books-list.component.html',
styleUrls: ['./books-list.component.scss'],
})
export class BooksListComponent implements OnInit {
@Input() book!: BookModel;
booksArray: BookModel[] = [];
oneBook!: BookModel;
isMobile: boolean = false;
size!: {width: number, height: number};
constructor(private service: BookService, private route: ActivatedRoute,
private resizeService: ResizeService) {}
ngOnInit(): void {
this.size = {width: window.innerWidth, height: window.innerHeight};
this.resizeService.resizeEvent();
this.resizeService.mobileViewObs$.subscribe(data => {
this.isMobile = data;
})
this.service.getBooks().subscribe((books) => {
this.booksArray = books;
});
}
The problem is that the first time the page reload it will always diplay false because the observable is created only on resize event. Do you know how to implement this feature?
You don't need that much code actually, you can just assign the true
or false
directly in the service to an observable, to which you in the component subscribe. Assumingly you want to show different content in template depending on mobile/desktop view, so it's a perfect scenario to use the async
pipe!
If you need the true
/false
in the component ts, you can of course subscribe to it, but remember to unsubscribe if that is the path you are taking!
Anyways, I suggest the following...
SERVICE:
isMobile$ = fromEvent(window, 'resize').pipe(
startWith(window), // to have an initial value, can be any value
mapTo(window), // map to window object instead (so we can have the initial value!)
throttleTime(500),
debounceTime(500),
map((window: Window) => {
if (window.innerWidth < PHONE_SIZE.width && window.innerHeight < PHONE_SIZE.height) {
return true;
}
return false;
})
);
And in the component you simply do....
isMobile$ = this.resizeService.isMobile$;
Then either use the async
pipe or subscribe!
HERE'S A DEMO - here only the screen width is taken into consideration for easy testing :)