将 gridstack.js 包装成 Angular 2 个组件
Wrap gridstack.js into Angular 2 component
我有一些 Angular 1 经验,但我们需要在 Angular 2 项目中使用 gridstack.js。
我们熟悉 gridstack-angular 项目,但该项目在 Angular 1。我认为我遇到的最大问题是 Angular 2 概念.
如有任何帮助,我们将不胜感激。
教程
好的,对于初学者来说 Angular 2 Quickstart 是最好的。
然后继续并进入 Tour of Heroes。这也是一个很棒的教程。
工具
对于教程,坦率地说构建任何 Angular 2 应用程序,我强烈建议使用 Angular-Cli。它使构建 Angular 2 个应用变得轻而易举
看看 Angular-Cli 的 Table of Contents 看看它能做什么
例子
我的网格-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>
<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>
</div>
</div>
我的网格-stack.component.ts ()
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);
}
}
然后我会把 gridstack.js
文件放在 src/assets/libs/gridstack
文件夹中。
然后不要忘记在您的 index.html
中导入
<script src="assets/libs/gridstack/gridstack.js"></script>
我们最终创建了两个指令:GridStackDirective 和 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>
</div>
<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>
<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>
</div>
</div>
</section>
index.html:
<html>
<head>
<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 -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
我们试图复制 gridstack.js 网页上的演示网格。如果你要 运行 这个并且你希望它看起来像他们的,你需要从他们的站点获取一些 .css 文件、.js 文件等。
根据@user3758236 的回答,我开发了几个组件,而不仅仅是指令:
interfaces.ts:
export interface IGridConfiguration {
width: number;
height: number;
x: number;
y: number;
}
网格-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[];
}
网格堆栈-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();
}
}
后一个组件使用动态组件创建服务,您可以在 Whosebug 的其他地方找到它。
用法如下:
<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>
基于用户@Etchelon 和@user3758236 的回答
我创建了这个 gridstack angular 4 个库模块以便于使用
它的用法很简单,我在那里添加了一个动态生成小部件的示例
<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>
干杯:)
我有一些 Angular 1 经验,但我们需要在 Angular 2 项目中使用 gridstack.js。
我们熟悉 gridstack-angular 项目,但该项目在 Angular 1。我认为我遇到的最大问题是 Angular 2 概念.
如有任何帮助,我们将不胜感激。
教程
好的,对于初学者来说 Angular 2 Quickstart 是最好的。
然后继续并进入 Tour of Heroes。这也是一个很棒的教程。
工具
对于教程,坦率地说构建任何 Angular 2 应用程序,我强烈建议使用 Angular-Cli。它使构建 Angular 2 个应用变得轻而易举
看看 Angular-Cli 的 Table of Contents 看看它能做什么
例子
我的网格-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>
<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>
</div>
</div>
我的网格-stack.component.ts (
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);
}
}
然后我会把 gridstack.js
文件放在 src/assets/libs/gridstack
文件夹中。
然后不要忘记在您的 index.html
<script src="assets/libs/gridstack/gridstack.js"></script>
我们最终创建了两个指令:GridStackDirective 和 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>
</div>
<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>
<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>
</div>
</div>
</section>
index.html:
<html>
<head>
<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 -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
我们试图复制 gridstack.js 网页上的演示网格。如果你要 运行 这个并且你希望它看起来像他们的,你需要从他们的站点获取一些 .css 文件、.js 文件等。
根据@user3758236 的回答,我开发了几个组件,而不仅仅是指令:
interfaces.ts:
export interface IGridConfiguration {
width: number;
height: number;
x: number;
y: number;
}
网格-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[];
}
网格堆栈-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();
}
}
后一个组件使用动态组件创建服务,您可以在 Whosebug 的其他地方找到它。
用法如下:
<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>
基于用户@Etchelon 和@user3758236 的回答
我创建了这个 gridstack angular 4 个库模块以便于使用
它的用法很简单,我在那里添加了一个动态生成小部件的示例
<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>
干杯:)