【问题标题】:Wrap gridstack.js into Angular 2 component将 gridstack.js 包装到 Angular 2 组件中
【发布时间】:2017-02-15 12:32:00
【问题描述】:

我有一些 Angular 1 的经验,但我们需要在 Angular 2 项目中使用gridstack.js

我们熟悉 gridstack-angular 项目,但该项目在 Angular 1 中。我认为我遇到的最大问题是 Angular 2 概念。

任何帮助将不胜感激。

【问题讨论】:

    标签: javascript jquery angularjs angular gridstack


    【解决方案1】:

    教程

    好的,对于初学者来说,Angular 2 Quickstart 是最好的。

    然后继续并移动到Tour of Heroes。这也是一个很棒的教程。

    工具

    对于教程,坦率地说,构建任何 Angular 2 应用程序,我强烈建议使用Angular-Cli。它使构建 Angular 2 应用程序变得轻而易举

    看看 Angular-Cli 的 Table of Contents 看看它能做什么


    示例


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

    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);
      }
    
    }
    

    然后我会将gridstack.js 文件放在src/assets/libs/gridstack 文件夹中。

    然后别忘了在你的index.html中导入

    <script src="assets/libs/gridstack/gridstack.js"></script>
    

    【讨论】:

    • 哦!好吧,是的!我目前正在为您提供一个示例,说明如何将 gridstack 与 Angular 2 一起使用
    • @user3758236 查看帖子,添加“示例”部分
    • 终于开始测试这个......我做了一些改变,但你的基本想法很棒,给了我一个起点。我最终创建了 GridStackDirective 和一个 GridStackItemDirective,我将在下面发布。
    • @user3758236 耶!谢谢!很高兴能帮忙!是的,我有兴趣看看你做了什么!干得好!
    • 你有同样的工作回购吗?这会很有帮助。
    【解决方案2】:

    我们最终创建了两个指令: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 文件等。

    【讨论】:

    • 谢谢老兄,非常有用!!
    • 但仍有 1 个问题。我们如何在网格堆栈项中放置一个角度组件,例如:
      。我试过了,但它没有渲染!
    • @ArkieCoder - 你有同样的工作回购吗?这会很有帮助。
    【解决方案3】:

    基于用户@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>
    

    干杯:)

    【讨论】:

      【解决方案4】:

      根据@user3758236 的回答,我开发了几个组件,而不仅仅是指令:

      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(); 
          }
      }
      

      后一个组件使用一个服务来创建动态组件,你可以在 stackoverflow 的其他地方找到它。

      用法如下:

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

      【讨论】:

      • DynamicComponentService 呢?
      猜你喜欢
      相关资源
      最近更新 更多
      热门标签