Sakuto Sakuto - 14 days ago 4
Javascript Question

Create custom script for DOM Manipulation

I'm currently working on an Angular 2 Project where I have a menu that should be closable by a click on a button. Since this is not heavy at all, I would like to put it outside of Angular (without using a component for the menu).

But I'm not sure of how to do it, actually I've just put a simple javascript in my html header, but shouldn't I put it somewhere else?

Also, what the code should be? Using class, export something? Currently this is my code:

var toggleMenuButton = document.getElementById('open-close-sidebar');
var contentHolder = document.getElementById('main-content');
var menuHolder = document.getElementById('sidebar');
var menuIsVisible = true;

var updateVisibility = function() {
contentHolder.className = menuIsVisible ? "minimised" : "extended";
menuHolder.className = menuIsVisible ? "open" : "closed";
}

toggleMenuButton.addEventListener('click', function() {
menuIsVisible = !menuIsVisible;

updateVisibility();
});





Finally moved to something with MenuComponent and a service, but I'm still encountering an issue.

MenuService.ts

@Injectable()
export class MenuService {
isAvailable: boolean = true;
isOpen: boolean = true;
mainClass: string = "minimised";
sidebarClass: string = "open";

updateClassName() {
this.mainClass = this.isOpen ? "minimised" : "extended";
this.sidebarClass = this.isOpen ? "open" : "closed";
}

toggleMenu(newState: boolean = !this.isOpen) {
this.isOpen = newState;
this.updateClassName();
}
}


MenuComponent.ts

export class MenuComponent {
constructor(private _menuService: MenuService) { }

public isAvailable: boolean = this._menuService.isAvailable;
public sidebarClass: string = this._menuService.sidebarClass;

toggleMenu() {
this._menuService.toggleMenu();
}
}


MenuComponent.html

<div id="sidebar" [class]="sidebarClass" *ngIf="isAvailable">
...
<div id="open-close-sidebar"><a (click)="toggleMenu()"></a></div>


The action are rightly triggered, if I debug the value with console.log, the class name are right but it didn't change the value of the class. I thought the binding was automatic. And I still do not really understand how to change it. Do I have to use Emmit like AMagyar suggested?

Answer

The advantage of using angular2 above your own implementation, greatly outweigh the marginal benefit in performance you will get from using plane JavaSccript. I suggest not going on this path.

If you however do want to continue with this, you should export a function and import and call this function inside the ngAfterViewInit of your AppComponent. The exported function should add the click EventListener and (important) set the document.getElementById variables. Because your script possibly won't be able to find those elements yet when it's loaded.

But let me emphasise once more, that angular2 is optimised for exactly these tasks, and once you get more familiar with it, it will also be a lot easier to code it.

update

For inter component communication you should immediately think about a service. Just create a service which stores the menu state and add this to your global ngModule providers array. For instance:

export class MenuService {

    public get menuOpen(): boolean {
        return this._menuOpen;
    }   

    private _menuOpen: boolean;

    public openMenu() : void {
        this._menuOpen = true;
    }

    public closeMenu() : void {
        this._menuOpen = false;
    }

    public toggleMenu() : void {
        this._menuOpen = !this._menuOpen;
    }

}

You can then inject this service into your menu component and bind the classes open/closed and minimized/extended to the MenuService.menuOpen.

@Component({
   selector : 'menu'
   template : `
       <button (click)="menuService.toggleMenu()">click</button>
       <div id="open-close-sidebar" [class.open]="menuService.menuOpen"></div>
   `
})
export class MenuComponent {

    constructor(public menuService: MenuService){}

}

For other component you can use the same logic to see if the menu is open or closed

update #2

You have to use a getter to get the value from menuService. There is only one way binding:

export class MenuComponent {
    constructor(private _menuService: MenuService) { }

    public get isAvailable(): boolean {
        return this._menuService.isAvailable;
    }

    public get sidebarClass(): string {
        return this._menuService.sidebarClass;
    }

    toggleMenu() {
        this._menuService.toggleMenu();
    }
}

FYI, it's better practice to use [class.open] instead of a string class name. If you want to do it like that, it will only require minimal change in your current css.

Comments