I would like to use JQuery inside my Angular components for DOM manipulation, but I would like to limit JQuery searches to the component's markup it's used in.
I'm trying using Shadow DOM, so I have this component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import * as $ from 'jquery';
#Component({
selector: 'app-layout',
templateUrl: './layout.component.html',
styleUrls: ['./layout.component.less'],
encapsulation: ViewEncapsulation.Native
})
export class LayoutComponent implements OnInit {
constructor() { }
ngOnInit() {
}
toggleSidebar(): void {
$('.main-sidebar').toggleClass('active');
$('.content-wrapper').toggleClass('expanded');
}
}
Here's the HTML:
<div class="wrapper">
<header class="main-header">
Logo
<nav class="navbar">
<button type="button" id="sidebar-collapse" class="btn btn-info navbar-btn" (click)='toggleSidebar()'>
<i class="fa fa-align-left"></i>
</button>
</nav>
</header>
<aside class="main-sidebar">
<section class="sidebar">
Sidebar
</section>
</aside>
<div class="content-wrapper">
<section class="content">
Content
</section>
</div>
</div>
If I remove the encapsulation configuration, it works, but if I leave it in place, JQuery can't find the elements searched. I would like to know how can I make JQuery find those elements. Is there any other way of limiting JQuery searches apart from using Shadow DOM?
Why would you do menu toggling with jQuery?
It is not good practice to use it with Angular application. Let Angular do it.
You can do it with host binding
HTML
<nav class="navbar">
<button type="button" id="sidebar-collapse" class="btn btn-info navbar-btn"
(click)='toggleSidebar()'>
<i class="fa fa-align-left"></i>
</button>
</nav>
<aside class="main-sidebar" [ngClass]="{'menu_open': menuOpen}">
<section class="sidebar">
Sidebar
</section>
</aside>
Component
import { Component, HostBinding, OnInit } from '#angular/core';
export class LayoutComponent implements OnInit {
// set it closed by default
#HostBinding('class.menu_open') public menuOpen = false;
....
toggleSidebar() {
this.menuOpen = !this.menuOpen;
}
...
}
Related
I am trying to display a pop up that I tried to implement as modal, but I get the error:
TypeError: Cannot read property 'open' of undefined
I created the pop up as component:
import { Component, OnInit, ViewChild, ElementRef, Input } from '#angular/core';
import User from '../types/user'
#Component({
selector: 'app-modal',
templateUrl: './chat-user-profile.component.html',
styleUrls: ['./chat-user-profile.component.css']
})
export class ChatUserProfileComponent {
me: User;
ngOnInit() {}
#ViewChild('ChatUserProfile') modal: ElementRef;
open() {
this.modal.nativeElement.style.display = 'block';
}
close() {
this.modal.nativeElement.style.display = 'none';
}
}
<div #myChatUserProfile class="container">
<div class="content">
<p>Some content here...</p>
<div class="text-center mb-2">
<h4><span class="badge badge-pill">{{me?.username}}</span></h4>
</div>
<!-- <button (click)="save()">Save</button> -->
<button (click)="close()">Close</button>
</div>
</div>
Now I try to open it on clicking a button
#Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrls: []
})
export class NavComponent {
isLoggedIn = false;
constructor(private amplifyService: AmplifyService, public router: Router) {
this.amplifyService.authStateChange$.subscribe(authState => {
const isLoggedIn = authState.state === 'signedIn' || authState.state === 'confirmSignIn';
if (this.isLoggedIn && !isLoggedIn) {
router.navigate(['']);
} else if (!this.isLoggedIn && isLoggedIn) {
router.navigate(['/chat']);
}
this.isLoggedIn = isLoggedIn;
});
}
public signOut() {
this.amplifyService.auth().signOut();
}
//Stuff for Profile Button
#ViewChild('app-modal') modal: ChatUserProfileComponent
openModal() {
this.modal.open();
}
}
<nav class="navbar navbar-toggleable-md navbar-inverse navbar-dark bg-dark fixed-top">
<a class="navbar-brand text-white" routerLink='/'>
<img src="../../assets/img/chat.png" width="30" height="30" class="d-inline-block align-top" alt="http://freepngimg.com/png/11489-chat-free-download-png">
<strong>ChatQL</strong>
</a>
<!--STUFF TO INTEGRATE THE POP UP-->
<app-modal #modal></app-modal>
<button (click)="openModal()">Open Modal</button>
<!------------->
<ul class="nav navbar-nav">
<li *ngIf="isLoggedIn" class="nav-item">
<button class="btn btn-primary" (click)="signOut()">Sign Out <i class="ion-log-in" data-pack="default" data-tags="sign in"></i></button>
</li>
</ul>
</nav>
I also tried adding the modal to the constructor but that gave me a No Provider error.
I am based on the chatQL project and using this as template for the pop up. I think something is wrong when initializing but I can not figure out where exactly.
In your HTML code, you have used template reference #modal
<app-modal #modal></app-modal>
So in typescript file you have to replace '#ViewChild('app-modal')' with '#ViewChild('modal')' like below
#ViewChild('modal') modal: ChatUserProfileComponent
Currently, you are using the wrong template reference so you are getting this undefined error because there is no template with reference 'app-modal'.
I am making a single page in Angular 10. It a simple page and I would like to highlight navbar depending on scroll. how can i highlight the element on navbar where i am currently, but make sure it is single page , so i am navigation to sections with id in my page.
here is my code:
HOME.COMPONENT.htmlstrong text
`
<!-- site header
================================================== -->
<header id="navbar" class="s-header" (Scroll)="onWindowScroll($event);">
<nav class="header-nav-wrap">
<ul class="header-main-nav">
<li><a class="smoothscroll" href='/home' title="intro">Intro</a></li>
<li><a class="smoothscroll" href='home#about' title="about">About</a></li>
<li><a class="smoothscroll" href='home#services' title="services">Services</a></li>
<li><a class="smoothscroll" href='home#works' title="works">Works</a></li>
<li><a class="smoothscroll" href='home#contact-us' title="contact us">Say Hello</a></li>
</ul>
</nav>
</header> <!-- end s-header -->
<!-- intro
================================================== -->
<section id="intro" class="s-intro target-section">
<div class="row intro-content">
<div class="column large-9 mob-full intro-text">
<h3>Hello, I'm xyz</h3>
<h1>
Full stack Developer <br>
and Dotnet Developer <br>
Based In somewhere.
</h1>
</div>
<div class="intro-grid"></div>
<div class="intro-pic" style="background-image: url(/assets/images/intro-pic.jpg);"></div>
</div> <!-- end row -->
</section> <!-- end intro -->
<section id="about" class="s-about">
hi about
</section>
<section id="services" class="s-services">
hi services
</section>
<section id="works" class="s-works">
hi works
</section>
<section id="contact-us" class="s-contactUs">
hi contacts
</section>`
HOME.COMPONENT.ts
import { Component, OnInit, HostListener } from '#angular/core';
import $ from 'jquery';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
/* sticky navbar on Scrolldown
* ------------------------------------------------------ */
#HostListener('window:scroll', ['$event'])
onWindowScroll(e) {
if (window.pageYOffset > 200) {
let element = document.getElementById('navbar');
element.classList.add('sticky');
} else {
let element = document.getElementById('navbar');
element.classList.remove('sticky');
}
}
}
Have a flag on the component and use it to set the class
export class HomeComponent implements OnInit {
menuSticky = false;
#HostListener('window:scroll', ['$event'])
onWindowScroll(e) {
if (window.pageYOffset > 200) {
this.menuSticky = true;
} else {
this.menuSticky = false;
}
}
}
and use it in the template
<header id="navbar" class="s-header" [ngClass]="{ sticky: menuSticky }" (Scroll)="onWindowScroll($event);">
I am have an angular 6 project that includes a toolbar on the top of the page with a "Sign In" button. When I click on this button, I want a modal dialog to appear.
However, I ran there are two issues illustrated in the screenshot below. The first is when I load the page, both modals (outlined in red) are loaded. They should only appear if the appropriate button is clicked.
Stackblitz project is at below link:
https://stackblitz.com/github/eugene01a/online-exam/tree/master/frontend
The second issue is why are they not appearing in their own dialog popup?
import {
Component,
OnInit
} from '#angular/core';
import {
ModalService
} from "./_services";
import './_content/app.less';
import './_content/modal.less';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
authenticated = false;
constructor(private modalService: ModalService) {}
ngOnInit() {}
openModal(id: string) {
this.modalService.open(id);
}
closeModal(id: string) {
this.modalService.close(id);
}
}
<!-- main app container -->
<mat-toolbar color="primary" class="mat-elevation-z10">
<button mat-button>Online Exams</button>
<button mat-button>About</button>
<!-- This fills the remaining space of the current row -->
<span class="fill-remaining-space"></span>
<button mat-button (click)="openModal('signin-modal')" *ngIf="!authenticated">Sign In</button>
<button mat-button (click)="openModal('signout-modal')" *ngIf="authenticated">Sign Out</button>
</mat-toolbar>
<div class="view-container">
<router-outlet></router-outlet>
</div>
<jw-modal id="signin-modal">
<h1>Sign in button clicked</h1>
<button (click)="closeModal('signin-modal');">Close</button>
</jw-modal>
<jw-modal id="signout-modal">
<h1>Sign Out button clicked</h1>
<button (click)="closeModal('signout-modal');">Close</button>
</jw-modal>
I have a recursive component to create a dynamically updating tree structure which can be collapsed. However, it seems that the active variable which determines the state is shared with child components. Is there any way to prevent this?
#Component({
selector: 'app-og-span',
templateUrl: './og-span.component.html',
styleUrls: ['./og-span.component.css']
})
export class OgSpanComponent {
#Input() comments;
private isActive = true;
toggleActive($event) {
console.log('wtf');
this.isActive = !this.isActive;
$event.stopPropagation();
return false;
}
}
And the HTML is:
<div class="ui accordion" style="padding-left: 15px" *ngFor="let comment of comments">
<div class="title" [ngClass]="{'active': isActive}" (click)="toggleActive($event);$event.stopPropagation()">
<i class="dropdown icon"></i>
{{comment.text}}
</div>
<div class="content" [ngClass]="{'active': isActive}">
<app-og-span [comments]="comment.comments" *ngIf="comment.comments"></app-og-span>
</div>
</div>
I know the event is fired only once from logs. There was a case of variable scopes with AngularJS (1.x) but I cannot find to find the corresponding in 6 (2+)
It seems I was wrong changing the code to following worked, because I placed the *ngFor wrongly at the top to get the view I wanted but not semantically correct one (and I assumed the AngularJS 1.x case of variable scopes):
<div class="ui styled accordion" style="padding-left: 10px">
<div class="title" (click)="click($event)" [ngClass]="{'active': active}">
<i class="dropdown icon"></i>
{{ node.name }}
</div>
<div class="content" [ngClass]="{'active': active}">
<span *ngFor="let node of node.children">
<app-og-span [node]="node"></app-og-span>
</span>
</div>
</div>
JS:
#Component({
selector: 'app-og-span',
templateUrl: './og-span.component.html',
styleUrls: ['./og-span.component.css']
})
export class OgSpanComponent {
#Input() node;
private active = true;
click($event) {
this.active = !this.active;
$event.stopPropagation();
}
}
I'm using Angular 2 Material sidenav in my project this way:
<md-sidenav-layout>
<md-sidenav #start mode="side" [opened]="true">
<md-nav-list>
</md-nav-list>
</md-sidenav>
<button md-button (click)="start.toggle()">Close</button>
<router-outlet></router-outlet>
</md-sidenav-layout>
How to call start.toggle() from my component instead of element with click event?
Thank you for reading
You want to declare a ViewChild in your controller that references the MdSidenav inside your component, like this:
// Sidemenu
#ViewChild('start') sidenav: MdSidenav;
where start is the name of the component you want to reference, in this case the sidenav.
Next you can call methods on that sidenav, like this.sidenav.toggle() inside your controller's functions.
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-view-child
Pass the object to your function.
<md-sidenav-layout>
<md-sidenav #start mode="side" [opened]="true">
<md-nav-list>
</md-nav-list>
</md-sidenav>
<button md-button (click)="randomName(start)">Close</button>
<router-outlet></router-outlet>
</md-sidenav-layout>
import {Component} from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
constructor() {
}
randomName(start: any) {
start.toggle();
}
}
The simplest way for was to pass the drawer to the component that triggers the toggle.
Html:
<mat-drawer #drawer mode="side">
....
</mat-drawer>
<another-component [drawerRef]="drawer"><another-component/>
another-component TS:
export class AnotherComponent implements OnInit {
#Input() drawerRef: MatDrawer;
...
}
another-component HTML:
<button (click)="this.drawerRef.toggle()">
Where another-component is the component that controls the toggle action.
<md-sidenav #sidenav class="example-sidenav" opened="false">
<div class="example-sidenav-content">
<button type="button" md-button (click)="toogleNav(sidenav)">
toogle
</button>
</div>
</md-sidenav>
And in your ts:
export class AppComponent {
toogleNav(nav: any) {
if (nav.opened) {
nav.close()
} else {
nav.open();
}
}
}