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>
–
–
–
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.
–
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.