5

I'm trying to use Blockly in an Angular 7 application but I'm unable to inject the Blockly editor.

I have downloaded the files from https://developers.google.com/blockly/guides/get-started/web and copied blockly_compressed.js into my src directory (and renamed it blockly.js). I then try to access Blockly from my component and get errors.

What I have tried:

import "../blockly.js"

Does not compile, gives "error TS2304: Cannot find name 'Blockly'."

import { Blockly } from '../blockly'

Compiles, but gives the following error when the app is opened in a browser:

ERROR TypeError: _blockly__WEBPACK_IMPORTED_MODULE_4__.Blockly.inject is not a function

Adding a blockly.d.ts file with the following:

export namespace Blockly {
    export function inject(div: string, config: any): void;
}

Gives the same error as above.

Any suggestions on what else I could try?

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83
Craig
  • 97
  • 1
  • 5

5 Answers5

3

I was able to setup with the config mentioned below -

install blockly with npm -

npm install git://github.com/google/blockly.git#1.20190419.0

included below files in scripts section of angular.json file -

    "scripts": [
      "node_modules/blockly/blockly_compressed.js",
      "node_modules/blockly/blocks_compressed.js",
      "node_modules/blockly/msg/js/en.js",
      "src/assets/blockly/custom_blocks.js"
    ]

added below lines in my component html file -

  <div id="blocklyDiv" style="width: 100%; height: 100%"></div>
  <xml id="toolbox" style="display: none">
    <category name="Control" colour="120">
      <block type="controls_if"></block>
      <block type="controls_repeat_ext" disabled="true"></block>
    </category>
    <category name="Text" colour="230">
      <block type="text"></block>
      <block type="text_print"></block>
    </category>
    <category name="Custom" colour="360">
      <block type="begin"></block>
      <block type="move"></block>
      <block type="end"></block>
    </category>
  </xml>

angular will throw error at this point saying it does not recognise the blockly tags. So need to use NO_ERRORS_SCHEMA in the module or can represent the toolbar XML as a string in the component TS file and use it to inject blockly.

my component TS file -

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ProgramService } from '../services/program.service';
import { IProgram } from '../models/program';

declare var Blockly: any;

@Component({
  selector: 'app-program-create',
  templateUrl: './program-create.component.html',
  styleUrls: ['./program-create.component.scss']
})
export class ProgramCreateComponent implements OnInit {
  title: string;
  programName: string;
  program: IProgram;
  workspace: any;

  constructor(
    private route: ActivatedRoute,
    private programService: ProgramService,
    private router: Router
  ) {
    this.title = 'Create Visual Program';
    this.route.params.subscribe(params => {
      this.programName = params['programName'];
      this.program = this.programService.getOne(this.programName);
      if (!this.program) {
        this.program = {
          name: this.programName,
          xmlData: null
        };
      }
      console.log(
        'creating/editing the program - ',
        JSON.stringify(this.program)
      );
    });
  }

  ngOnInit() {
    this.workspace = Blockly.inject('blocklyDiv', {
      toolbox: document.getElementById('toolbox'),
      scrollbars: false
    });

    if (this.program.xmlData) {
      this.workspace.clear();
      Blockly.Xml.domToWorkspace(
        Blockly.Xml.textToDom(this.program.xmlData),
        this.workspace
      );
    }
  }

  saveProgram(): void {
    this.program.xmlData = Blockly.Xml.domToText(
      Blockly.Xml.workspaceToDom(this.workspace)
    );
    console.log('saving the program - ', JSON.stringify(this.program));
    this.programService.upsertOne(this.program);
    this.router.navigate(['listProgram']);
  }
}

I have written an article explaining this in details here - Integrate Google Blockly with Angular

Nithin Kumar Biliya
  • 2,763
  • 3
  • 34
  • 54
3

My answer does not put the XML into the template itself, but rather in a variable which enable the integration without the NO_ERRORS_SCHEMA import in the module.

Step 1: Download the files from Blockly website, and look for:

  • blockly_compressed.js
  • blocks_compressed.js
  • en.js (Or any language that you want your blockly to be in)

    Copy and paste them into src/assets/blockly.

Step 2: In your angular.json file, add the following ( in projects.architect.build.options ):

"scripts": [
          "src/assets/blockly/blockly_compressed.js",
          "src/assets/blockly/blocks_compressed.js",
          "src/assets/blockly/en.js"
]

Step 3: In your component.ts :

import { Component, AfterViewInit } from '@angular/core';
declare var Blockly: any

@Component({
  template: `<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>`,
  selector: 'app-blockly',
  styleUrls: ['./blockly.component.scss']
})

export class BlocklyComponent implements AfterViewInit {
  ngAfterViewInit(): void {
    const toolbox = `
      <xml>
        <block type="controls_if"></block>
        <block type="controls_whileUntil"></block>
      </xml>`;
    Blockly.inject('blocklyDiv', { toolbox });
  }
}

And that's it !

Emmanuel
  • 199
  • 4
  • 7
1

I assume you're using @angular/cli.

Demo

Step 1: Install blockly

npm install blockly

Step 2: Add scripts to angular.json under the architect node:

"scripts": [
   "node_modules/blockly/blockly_compressed.js",
   "node_modules/blockly/blocks_compressed.js",
   "node_modules/blockly/msg/js/en.js"
]

Step 3: Add NO_ERRORS_SCHEMA to your AppModule (this is so that you can define custom tags in your components)

@NgModule({
  imports:      [ BrowserModule, AppRoutingModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ],
  exports: [AppComponent],
  schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule {
}

Step 4: Create a Component, declare Blockly as any, and implement AfterViewInit so that you can access the blockly-related elements in the DOM:

import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
declare var Blockly: any;

@Component({
  selector: 'app-root',
  template: `
    <div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
    <xml id="toolbox" #toolbox style="display: none">
            <block type="controls_if"></block>
            <block type="logic_compare"></block>
            <block type="controls_repeat_ext"></block>
            <block type="math_number"></block>
            <block type="math_arithmetic"></block>
            <block type="text"></block>
            <block type="text_print"></block>
    </xml>
  `
})
export class AppComponent implements AfterViewInit {
  workspace: any;
  @ViewChild('toolbox') toolbox: ElementRef;

  ngAfterViewInit(): void {
    this.workspace = Blockly.inject('blocklyDiv',
    {toolbox: this.toolbox.nativeElement });
  }
}

NOTE: The Blockly package in NPM is at v1.0, while the latest version is v1.2. To use the latest, just download the library, put it in a known directory, and fix your script references (Step 2).

Michael Kang
  • 52,003
  • 16
  • 103
  • 135
  • I had to import NO_ERRORS_SCHEMA from @angular/core to get rid of a compile error when modifying app.module.ts. – Craig Oct 22 '18 at 18:01
  • Tried all of the steps and it still doesn't work: getting `ReferenceError: Blockly is not defined` in ngAfterViewInit(). – Craig Oct 22 '18 at 18:04
  • 1
    Thanks for looking at this. For some reason the demo doesn't work on my PC at all (I tried three different browsers). I'll try again on another machine in case there is something screwed up on my machine. – Craig Oct 23 '18 at 03:05
  • Yeah, I think Stackblitz behaves differently when dealing with globals. If you try locally should work – Michael Kang Oct 23 '18 at 04:14
  • 1
    The demo is not working Cannot read property 'inject' of undefined. doesn't look like any thing Machine Specific. – N.K Apr 09 '19 at 06:59
1

Use ngx-blockly instead of blockly , ngx-blockly is angular port of blockly.

follow the steps explained in npm page

npm install ngx-blockly --save

https://www.npmjs.com/package/ngx-blockly

Davut Gürbüz
  • 5,526
  • 4
  • 47
  • 83
0

You should add it to angular.json firstly. So angular CLI can build it.

        "scripts": [
          "../blockly.js"
        ]
Emon
  • 1,401
  • 14
  • 24