Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have some Angular 1 experience, but we have a need to use gridstack.js in an Angular 2 project.

We are familiar with the gridstack-angular project, but that project is in Angular 1. I think the biggest thing I am having trouble with is the Angular 2 concepts.

Any help would be appreciated.

Tutorials

Okay for begginers the Angular 2 Quickstart is the best.

Then that continues and moves into the Tour of Heroes . Which is also a fantastic tutorial.

For the tutorials, and quite frankly building ANY Angular 2 app, I would highly recommend using Angular-Cli . It makes building Angular 2 apps a breeze

Just take a look at the Angular-Cli's Table of Contents to see what it can do

Example

my-grid-stack.component.html

<div class="grid-stack">
    <div class="grid-stack-item"
        data-gs-x="0" data-gs-y="0"
        data-gs-width="4" data-gs-height="2">
            <div class="grid-stack-item-content"></div>
    <div class="grid-stack-item"
        data-gs-x="4" data-gs-y="0"
        data-gs-width="4" data-gs-height="4">
            <div class="grid-stack-item-content"></div>

my-grid-stack.component.ts (How to get JQuery in Angular 2)

import { Component, OnInit } from '@angular/core';
declare var $: any; // JQuery
@Component({
  selector: 'app-my-gridstack',
  templateUrl: './app/my-grid-stack/my-grid-stack.component.html',
  styleUrls: ['./app/my-grid-stack/my-grid-stack.component.css']
export class MyGridStackComponent implements OnInit {
  constructor() { }
  ngOnInit() {
      var options = {
          cell_height: 80,
          vertical_margin: 10
      $('.grid-stack').gridstack(options);

Then I would put the gridstack.js file in the src/assets/libs/gridstack folder.

Then don't forget to import in your index.html

<script src="assets/libs/gridstack/gridstack.js"></script>
                Oh! Well hell yeah! I am currently in the middle of making you an example of how it might be able to use gridstack with Angular 2
– Logan H
                Oct 6, 2016 at 17:07
                Finally got around to testing this ... I made some changes, but your basic idea was great and gave me a starting point.  I ended up creating GridStackDirective and a GridStackItemDirective which I'll post below.
– ArkieCoder
                Oct 12, 2016 at 18:12
                @user3758236 Yay! Thanks! Glad to help out! Yeah I am interested in seeing what you did! Good job man!
– Logan H
                Oct 12, 2016 at 18:14

We ended up creating two directives: GridStackDirective and GridStackItemDirective -

grid-stack-directive.ts:

import { Directive, OnInit, Input, ElementRef, Renderer } from '@angular/core';
declare var $: any; // JQuery
@Directive({
    selector: '[gridStack]'
export class GridStackDirective implements OnInit {
    @Input() w: number;
    @Input() animate: boolean;
    constructor(
        private el: ElementRef,
        private renderer: Renderer
        renderer.setElementAttribute(el.nativeElement, "class", "grid-stack");
    ngOnInit() {
        let renderer = this.renderer;
        let nativeElement = this.el.nativeElement;
        let animate: string = this.animate ? "yes" : "no";
        renderer.setElementAttribute(nativeElement, "data-gs-width", String(this.w));
        if(animate == "yes") {
            renderer.setElementAttribute(nativeElement, "data-gs-animate", animate);
        let options = {
            cellHeight: 80,
            verticalMargin: 10
        // TODO: listen to an event here instead of just waiting for the time to expire
        setTimeout(function () {
            $('.grid-stack').gridstack(options);
        }, 300);

grid-stack-item-directive.ts:

import { Directive, ElementRef, Input, Renderer, OnInit } from '@angular/core';
@Directive({
    selector: '[gridStackItem]'
export class GridStackItemDirective {
  @Input() x: number;
  @Input() y: number;
  @Input() w: number;
  @Input() h: number;
  @Input() minWidth: number;
  @Input() canResize: boolean;
  constructor(
    private el: ElementRef,
    private renderer: Renderer
    renderer.setElementAttribute(el.nativeElement, "class", "grid-stack-item");
  ngOnInit(): void {
    let renderer = this.renderer;
    let nativeElement = this.el.nativeElement;
    let cannotResize: string = this.canResize ? "yes" : "no";
    let elementText: string = '<div class="grid-stack-item-content">' + nativeElement.innerHTML + '</div>';
    // TODO: Find the Angular(tm) way to do this ...
    nativeElement.innerHTML = elementText;
    renderer.setElementAttribute(nativeElement, "data-gs-x", String(this.x));
    renderer.setElementAttribute(nativeElement, "data-gs-y", String(this.y));
    renderer.setElementAttribute(nativeElement, "data-gs-width", String(this.w));
    renderer.setElementAttribute(nativeElement, "data-gs-height", String(this.h));
    if(this.minWidth) {
      renderer.setElementAttribute(nativeElement, "data-gs-min-width", String(this.minWidth));
    if(cannotResize == "yes") {
      renderer.setElementAttribute(nativeElement, "data-gs-no-resize", cannotResize);

app.component.html:

<h1>My First Grid Stack Angular 2 App</h1>
<section id="demo" class="darklue">
    <div class="container">
        <div class="row">
            <div class="col-lg-12 text-center">
                <h2>Demo</h2>
                <hr class="star-light">
        <div gridStack w="12" animate="true">
            <div gridStackItem x="0" y="0" w="4" h="2">1</div>
            <div gridStackItem x="4" y="0" w="4" h="4">2</div>
            <div gridStackItem x="8" y="0" w="2" h="2" canResize="false" minWidth="2">
                <span class="fa fa-hand-o-up"></span> Drag me!
            <div gridStackItem x="10" y="0" w="2" h="2">4</div>
            <div gridStackItem x="0" y="2" w="2" h="2">5</div>
            <div gridStackItem x="2" y="2" w="2" h="4">6</div>
            <div gridStackItem x="8" y="2" w="4" h="2">7</div>
            <div gridStackItem x="0" y="4" w="2" h="2">8</div>
            <div gridStackItem x="4" y="4" w="4" h="2">9</div>
            <div gridStackItem x="8" y="4" w="2" h="2">10</div>
            <div gridStackItem x="10" y="4" w="2" h="2">11</div>
</section>

index.html:

<title>Angular QuickStart</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="node_modules/font-awesome/css/font-awesome.min.css"> <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css"> <link href="https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic" rel="stylesheet" type="text/css"> <link href="https://fonts.googleapis.com/css?family=Indie+Flower" rel='stylesheet' type='text/css'> <!-- 1. Load libraries --> <!-- Polyfill(s) for older browsers --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.js"></script> <script src="node_modules/reflect-metadata/Reflect.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <!-- 2. Configure SystemJS --> <script src="systemjs.config.js"></script> <script> System.import('app').catch(function(err){ console.error(err); }); </script> <!-- jquery --> <script src="node_modules/jquery/dist/jquery.js"></script> <script src="node_modules/jquery-ui-dist/jquery-ui.min.js"></script> <script src="node_modules/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js"></script> <script src="node_modules/jquery-easing/dist/jquery.easing.1.3.umd.min.js"></script> <!-- underscore and gridstack --> <script src="node_modules/underscore/underscore-min.js"></script> <script src="node_modules/gridstack/dist/gridstack.js"></script> <link rel="stylesheet" href="node_modules/gridstack/dist/gridstack.min.css"> <link rel="stylesheet" href="node_modules/gridstack/dist/gridstack-extra.min.css"> <link rel="stylesheet" href="app/css/gridstack-demo.css"> <!-- bootstrap --> <script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script> <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css"> <!-- freelancer stuff --> <script src="app/js/freelancer.js"></script> <link rel="stylesheet" href="app/css/freelancer.css"> </head> <!-- 3. Display the application --> <my-app>Loading...</my-app> </body> </html>

We tried to copy the demo grid on the gridstack.js web page. If you're going to run this and you want it to look like theirs, you'll need to grab some .css files, .js files, etc. from their site.

But there is still 1 issue. How do we place an angular component inside the grid stack item like : <div gridStackItem x="10" y="0" w="2" h="2"><custom-component></custom-component></div>. I tried but it's not rendering! – Rohit Rane Jan 6, 2017 at 13:55

Based on Users @Etchelon and @user3758236 answers

I have created this gridstack angular 4 library module for easy usage

  • https://github.com/ramyothman/ng4-gridstack
  • https://www.npmjs.com/package/ng4-gridstack
  • It has a simple usage and I added an example there for dynamically generating widgets

    <grid-stack class="grid-stack" [options]="options">
      <grid-stack-item [option]="widget1" class="grid-stack-item"  >
      </grid-stack-item>
      <grid-stack-item [option]="widget2" class="grid-stack-item" >
      </grid-stack-item>
    </grid-stack>
    

    Cheers :)

    Based on @user3758236's answer I developed a couple of components, instead of having just directives:

    interfaces.ts:

    export interface IGridConfiguration {
        width: number;
        height: number;
        x: number;
        y: number;
    

    grid-stack.component.ts:

    import { Component, HostBinding, OnInit, Input, OnChanges, AfterViewInit, AfterContentInit, ElementRef, Renderer, QueryList, ContentChildren } from '@angular/core';
    import { GridStackItemComponent } from "./grid-stack-item.component";
    import { IGridConfiguration } from "./interfaces";
    declare var jQuery: any; // JQuery
    declare var _: any;
    @Component({
        moduleId: module.id,
        selector: 'grid-stack',
        template: `<ng-content></ng-content>`,
        styles: [":host { display: block; }"]
    export class GridStackComponent implements OnInit, OnChanges, AfterContentInit {
        @HostBinding("class") cssClass = "grid-stack";
        @Input() width: number;
        @Input() animate: boolean;
        @Input() float: boolean;
        @ContentChildren(GridStackItemComponent) items: QueryList<GridStackItemComponent>;
        constructor(
            private _el: ElementRef,
            private _renderer: Renderer
        ) { }
        private _jGrid: any = null;
        private _grid: any = null;
        ngOnInit() {
            let nativeElement = this._el.nativeElement;
            this._renderer.setElementAttribute(nativeElement, "data-gs-width", String(this.width));
            let options = {
                cellHeight: 100,
                verticalMargin: 10,
                animate: this.animate,
                auto: false,
                float: this.float
            _.delay(() => {
                const jGrid = jQuery(nativeElement).gridstack(options);
                jGrid.on("change", (e: any, items: any) => {
                    console.log("GridStack change event! event: ", e, "items: ", items);
                    _.each(items, (item: any) => this.widgetChanged(item));
                this._jGrid = jGrid;
                this._grid = this._jGrid.data("gridstack");
            }, 50);
        ngOnChanges(): void { }
        ngAfterContentInit(): void {
            const makeWidget = (item: GridStackItemComponent) => {
                const widget = this._grid.makeWidget(item.nativeElement);
                item.jGridRef = this._grid;
                item.jWidgetRef = widget;
            // Initialize widgets
            this.items.forEach(item => makeWidget(item));
            // Also when they are rebound
            this.items
                .changes
                .subscribe((items: QueryList<GridStackItemComponent>) => {
                    if (!this._grid) {
                        _.delay(() => this.items.notifyOnChanges(), 50);
                        return;
                    items.forEach(item => makeWidget(item));
        private widgetChanged(change: IWidgetDragStoppedEvent): void {
            var jWidget = change.el;
            var gridStackItem = this.items.find(item => item.jWidgetRef !== null ? item.jWidgetRef[0] === jWidget[0] : false);
            if (!gridStackItem)
                return; 
            gridStackItem.update(change.x, change.y, change.width, change.height);
    interface IWidgetDragStoppedEvent extends IGridConfiguration {
        el: any[];
    

    grid-stack-item.component.ts

    import { Component, ComponentRef, ElementRef, Input, Output, HostBinding, Renderer } from "@angular/core";
    import { EventEmitter, OnInit, OnChanges, OnDestroy, AfterViewInit, ViewChild, ViewContainerRef } from "@angular/core";
    import { IGridConfiguration } from "./interfaces";
    import { DynamicComponentService } from "./dynamic-component.service";
    @Component({
        moduleId: module.id,
        selector: "grid-stack-item",
        template: `
    <div class="grid-stack-item-content">
        <div #contentPlaceholder></div>
        <ng-content *ngIf="!contentTemplate"></ng-content>
    </div>`,
        styleUrls: ["./grid-stack-item.component.css"]
    export class GridStackItemComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
        @HostBinding("class") cssClass = "grid-stack-item";
        @ViewChild("contentPlaceholder", { read: ViewContainerRef }) contentPlaceholder: ViewContainerRef;
        @Input() initialX: number;
        @Input() initialY: number;
        @Input() initialWidth: number;
        @Input() initialHeight: number;
        @Input() minWidth: number;
        @Input() canResize: boolean;
        @Input() contentTemplate: string;
        contentComponentRef: ComponentRef<any> = null;
        @Output() onGridConfigurationChanged = new EventEmitter<IGridConfiguration>();
        private _currentX: number;
        private _currentY: number;
        private _currentWidth: number;
        private _currentHeight: number;
        jGridRef: any = null;
        private _jWidgetRef: any = null;
        get jWidgetRef(): any { return this._jWidgetRef; }
        set jWidgetRef(val: any) {
            if (!!this._jWidgetRef)
                this._jWidgetRef.off("change");
            this._jWidgetRef = val;
            this._jWidgetRef.on("change", function () {
                console.log("Change!!", arguments);
        update(x: number, y: number, width: number, height: number): void {
            if (x === this._currentX && y === this._currentY && width === this._currentWidth && height === this._currentHeight)
                return;
            this._currentX = x;
            this._currentY = y;
            this._currentWidth = width;
            this._currentHeight = height;
            this.onGridConfigurationChanged.emit({
                x: x,
                y: y,
                width: width,
                height: height
        get nativeElement(): HTMLElement {
            return this.el.nativeElement;
        constructor(
            private el: ElementRef,
            private renderer: Renderer,
            private componentService: DynamicComponentService
        ) { }
        ngOnInit(): void {
            let renderer = this.renderer;
            let nativeElement = this.nativeElement;
            let cannotResize: string = this.canResize ? "yes" : "no";
            renderer.setElementAttribute(nativeElement, "data-gs-x", String(this.initialX));
            renderer.setElementAttribute(nativeElement, "data-gs-y", String(this.initialY));
            renderer.setElementAttribute(nativeElement, "data-gs-width", String(this.initialWidth));
            renderer.setElementAttribute(nativeElement, "data-gs-height", String(this.initialHeight));
            if (this.minWidth) {
                renderer.setElementAttribute(nativeElement, "data-gs-min-width", String(this.minWidth));
            if (cannotResize == "yes") {
                renderer.setElementAttribute(nativeElement, "data-gs-no-resize", cannotResize);
        ngOnChanges(): void {
            // TODO: check that these properties are in the SimpleChanges
            this._currentX = this.initialX;
            this._currentY = this.initialY;
            this._currentWidth = this.initialWidth;
            this._currentHeight = this.initialHeight;
        ngAfterViewInit(): void {
            if (!!this.contentTemplate) {
                this.componentService.getDynamicComponentFactory({
                    selector: `grid-stack-item-${Date.now()}`,
                    template: this.contentTemplate
                .then(factory => {
                    this.contentComponentRef = this.contentPlaceholder.createComponent(factory);
        ngOnDestroy(): void {
            if (this.contentComponentRef !== null)
                this.contentComponentRef.destroy(); 
    

    The latter component uses a service for dynamic component creation, which u can find elsewhere on stackoverflow.

    The usage is as follows:

    <grid-stack width="12" animate="true" float="true">
        <grid-stack-item *ngFor="let field of fields; let i = index;"
            [class.selected]="field.id === selectedFieldId" (click)="fieldClicked(field.id)"
            [initialX]="field.gridConfiguration.x" [initialY]="field.gridConfiguration.y"
            [initialWidth]="field.gridConfiguration.width" [initialHeight]="field.gridConfiguration.height"
            [contentTemplate]="getFieldTemplate(field)" (onGridConfigurationChanged)="fieldConfigurationChanged($event, field.id)">
        </grid-stack-item>
    </grid-stack>
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.