peerlibrary / meteor-blaze-components

Reusable components for Blaze
http://components.meteorapp.com/
BSD 3-Clause "New" or "Revised" License
354 stars 26 forks source link

extend components from other file #65

Closed pixelass closed 9 years ago

pixelass commented 9 years ago

This works if both classes are in the same File. (FooBar.es6)
It does not work if they are in separate files (Foo.es6, Bar.es6)

Uncaught ReferenceError: Foo is not defined

class Foo extends BlazeComponent {
  constructor() {
    super();
  }
  handleClick() {
    console.log('event from Foo');
  }
  events() {
    return [{
      'click': this.handleClick
    }];
  }
}

class Bar extends Foo {
  template() {
    return 'Foo';
  }
  handleKeyUp() {
    console.log('event from Bar');
  }
  events() {
    return [{
      'keyup': this.handleKeyUp
    }];
  }
}

Am I doing something wrong here.. or is this intended?

mitar commented 9 years ago

This has nothing with Blaze Components. You should read how Meteor works. Meteor has its own rules how symbols are shared between files. I do not know what are rules for ES6, probably you should look/ask at the Meteor package you are using for ES6 support.

It maybe that only that Bar.es6 is alphabetically before Foo.es6 so it tries to load it first, and it fails, because Bar.es6 was not yet loaded.

pixelass commented 9 years ago

I was expecting a similar answer.

The loading order is correct

  api.addFiles([
  'lib/components/Foo.es6',
  'lib/components/Foo.tpl.jade',
 'lib/components/Foo.es6',
  'lib/components/Foo.tpl.jade'

I am now using globals to fix the issue.
Taken from here: https://github.com/grigio/meteor-babel/issues/9

This is super ugly.. but it works for now.

Foo = class Foo extends BalzeComponent { 
  //...
}
mitar commented 9 years ago

Yea. You could maybe mitigate this by using Blaze Components namespaces.

Namespace;

class Namespace.Foo extends BlazeComponent {
  constructor() {
    super();
  }
  handleClick() {
    console.log('event from Foo');
  }
  events() {
    return [{
      'click': this.handleClick
    }];
  }
}

class Namespace.Bar extends Namespace.Foo {
  template() {
    return 'Namespace.Foo';
  }
  handleKeyUp() {
    console.log('event from Bar');
  }
  events() {
    return [{
      'keyup': this.handleKeyUp
    }];
  }
}
pixelass commented 9 years ago

Thx. I know about namespaces but in my case they don't make sense.. anyways thx for the info. I aprechiate that

Alino commented 9 years ago

same issue here with coffeescript, cannot extend class in separate file, gives an error that the class I am trying to extend from is not defined.

mitar commented 9 years ago

@Alino: Maybe your load order of CoffeeScript files is not correct. This is definitely something I am doing regularly, so there must be some issue on your side. Feel free to create a reproduction and let's see what is wrong.

Alino commented 9 years ago

@mitar thank you for reply, here is a reproduction of the problem https://github.com/Alino/blazeComponentsCoffeescriptIssue/tree/master I thought it could be load order issue, but I renamed the files correctly in alphabetical order in same folder. And in my real app I use package based architecture where I have total control over load order, and this is still an issue for me.

mitar commented 9 years ago

Have you read documentation about CoffeeScript in Meteor?

So you are defining file local classes only, you are not exporting them out of the file. But it is a bit tricky. There are multiple ways to do that.

Inside a package

When wanting to share components inside one package, you should do something like:

class share.hello extends BlazeComponent
class helloAgain extends share.hello

Across packages

If you want to use components across packages, you can export them. In your package.js:

api.export('hello');

And then in another package you do api.use that first package, and then you can simply do:

class helloAgain extends hello

The interesting thing is that inside the first package, exporting it also makes it available in the first package as well in all the files. So you do not need to use share for components you are exporting to the outside, you can then also use that component in other files of the first package.

In an app

When you are not using packages, but have code in an app directly, then you should do:

class @hello extends BlazeComponent
class helloAgain extends hello

The @ sign in the first file exports it to the global scope. The downside is that this is the real global scope of the browser, so you are polluting the namespace. The good side is that then those symbols are available in the web console as well.

Blaze Components way

Because Blaze Components have an internal registry of all registered components, you can also simply do anywhere, no matter the package or app:

class hello extends BlazeComponent
  @register 'hello'
class helloAgain extends BlazeComponent.getComponent('hello')
Alino commented 9 years ago

@mitar wow, thank you very much for this nice detailed answer!

nicooprat commented 8 years ago

Side note if anyone was struggling with the same issue as me: if you have a problem with load order (inside the app, not a package), eg. because the child component is loaded (alphabetically) before its parent, it's possible to "force" the load order since Meteor 1.3 without editing files or folders names: simply import the parent file from the child one like this:

import './pathToParentComponent.js';

class child extends BlazeComponent.getComponent('parent') {
  ...
}

Can't find a way to really import/export Blaze Components to perform extends though (without getComponent), but I may find the "clean" solution one day or the other :)