This is part ES6 question part React question. I'm trying to use namespaced components in React with ES6 classes and Babel. So I guess the real question is how to name space es6 classes so I can do what is explained here: https://facebook.github.io/react/docs/jsx-in-depth.html#namespaced-components
Since I get an unexpected token error:
class Headline extends Component { ... }
class Headline.Primary extends Component { ...
^
The ECMAScript-6 class declaration syntax expects a standard BindingIdentifer as the class name. A dot is not a valid character inside an identifier name.
In the context used in the link in OP, the "namespace" is an object, and properties are added to that object one by one using the dot notation for property access.
You could replicate that by using a class expression instead:
'use strict'
var ns = {}
ns.MyClass = class {
constructor() {
console.log('in constructor')
}
}
new ns.MyClass()
This doesn't really change with ES6, you still will have to do an assignment:
Headline.Primary = class Primary extends Component { … };
However, using classes like Headline as namespaces is getting pretty deprecated with ES6 (and has previously been a questionable practice anyway), you should instead leverage the new module system. Export Primary as a named export, and instead of importing the Headline class rather do import * as headlines from ….
This link also relates to this question.
In the Module objects section, it is described that you can do something like this:
// headline.js file
export {Headline, Primary}
class Headline {}
class Primary {}
// In another module...
import * as Headline from "headline";
let h = new Headline.Headline();
let hp = new Headline.Primary();
It's not exactly what you are trying to do, but is an alternative.
Another way of doing it is almost like #Bergi has already pointed out, but I'm just clarifying it further:
let Headline = class Headline extends Component { }
Headline.Primary = class Primary extends Component { }
export {Headline as default}
// in another module:
import Headline from 'headline';
let headline = new Headline();
let primary = new Headline.Primary();
Related
I'm modeling a web page with a lot of items on it. Coming from a Ruby background, I had one class for each, say, large item and its subitems on the page. For instance, a navbar would be its own class:
import { Selector, t } from 'testcafe';
export class NavBar {
constructor () {
this.home = Selector('#home')
this.intro = Selector('#intro')
...
}
}
export class HeaderSection {
...
}
Questions:
Do I need a default class? My IDE is complaining, but the test work. I believe, the answer is no, but it's a good practice(?)
What's the recommended way to write a complex page model in JavaScript? I'm leaning to have one-page class, say index and then have multiple child classes (Navbar and HeaderSection on my example) that inherit from the index class
This is what I think it should be:
import { Selector, t } from 'testcafe';
export default class Index {
}
export class NavBar extends Index {
constructor () {
super ();
this.home = Selector('#home')
this.intro = Selector('#intro')
...
}
}
export class HeaderSection extends Index {
constructor () {
super ();
...
}
}
so when I import the page model into my test case, I can call import Index from ../pages/index_page.js
Do I need a default class? My IDE is complaining, but the test work. I believe, the answer is no, but it's a good practice(?)
It's not necessary. The default keyword determines the way of the export in JavaScript. You can organize page objects as you like.
What's the recommended way to write a complex page model in JavaScript? I'm leaning to have one page class, say index and then have multiple child classes (Navbar and HeaderSection on my example) that inherit from the index class
It depends on page complexity. If test page is simple then one page object class for one page is enough. If test page is complex, creating separate classes for complex controls is a good approach.
I'd like to do inheritance in an es6 class without the extends keyword:
Typical approach:
class Foo extends Bar {
contructor() {
...
}
}
What I am looking for is to generate an object with the same signature but using this pattern:
class Foo {
contructor(Bar) {
// use Bar class somehow
...
}
}
Thanks
== EDITS ==
Context:
I build an extension (ami) for a JS library threejs.
It provides new objects that seamlessly work in threejs.
Problem:
threejs has an internal mechanism to generate unique ids for each object, that is critical for its proper behavior.
Current implementations rely on three to be exposed as a global variable, so anybody that creates an object must reference it to ensure the ids are actually unique.
// in ami
// ID of this object will be unique accros all classes
// that are based of global THREE.Mesh
class Foo extends THREE.Mesh {
contructor() {
...
}
}
Using global variable works fine but I want to get rid of the global namespace requirement.
If I do not reference the same base elements in ami and in my application, id can conflict.
// in ami
import {Mesh} from 'three';
class Foo extends Mesh {
contructor() {
...
}
}
// in my app
import {Foo} from 'ami';
imoport {Mesh} from 'three';
const foo = new Foo(); // it uses "Mesh" from ami as a base.
const mesh = new Mesh(); // it uses current "Mesh" as a base.
// IDs will conflict...
One solution that could work is that I provide a new argument in ami constructors, to provide the three reference:
// in ami
class Foo {
contructor(mesh) {
...
}
}
// in my app
imoport {Mesh} from 'three';
import {Foo} from 'ami';
const foo = new Foo(Mesh);
const mesh = new Mesh();
But I do not know how to implement this solution.
This answer addresses the question as originally posed and was good enough to help the OP refine the question and garner immediate upvotes. I'd appreciate it if you didn't downvote it merely because the goalposts have moved.
Assuming that you are not crazy and this is a learning exercise, the best way to learn how to implement this is to get Typescript, write a class using extends, compile it with ES5 as the target, and look at the generated JavaScript. Ensure your base class has methods, properties, static methods, static properties, and a constructor with mixed required and optional parameters. Then derive another class from it and override some methods and replace some. You'll see how it's done by people who got serious about it.
I might have my terminology mixed up, but in the same manner that I can access the global context through window, I would like to access the current context of my imported modules.
To give a simple example of what I'm doing, imagine we have a file called MyClasses.js which contains the following two classes:
export class MyClass1 {}
export class MyClass2 {}
Then we import said classes into a file called main.js
import {MyClass1, MyClass2} from './MyClasses'
In main.js I might construct a new instance of each class based on some property value.
function main()
{
const config = { case1: 'MyClass1', case2: 'MyClass2', case3: 'MyClass1' };
const myPropValue = 'case3';
const constructorName = config[myPropValue];
const myClass = new context[constructorName](); // MyClass1
}
This is a basic example, but in a situation where there are many classes and cases for constructing such classes, I'd like to map the relationship rather than depend on intricate if/else logic.
One solution would be to attach the imported classes to the window context...
window.MyClass1 = MyClass1;
window.MyClass2 = MyClass2;
... and construct instances of my classes from there:
const myClass = new window[constructorName](); // valid construction
But I'd like to avoid binding these to the global scope. Is there a default context for any imported modules, or do I need to set up a context myself?
Just modify your import and you will be ok:
import * as MyClasses from './MyClasses';
Though this topic has already been discussed in other posts like this:
Dynamically loading a typescript class (reflection for typescript)
I'm not able to find an answer to my specific issue. So, pardon me if this is duplicated.
I'm trying to create a very simple directive in Angular 2 (using Typescript), which allows dynamic addition or removal of a set of controls represented by a Type. For example, if the type is:
class Stone{
constructor(
public nameOfStone?: string,
public typeOfStone?: string
){}
}
the UI would have something like this:
I'm able to get this working with a specific Type (ex: Stone). But, given that the directive's objective is just to add this dynamic add/remove feature, I felt that it would make sense to parameterise the type to be created and use this for different type definitions. I tried something like this in the Component class:
import {Component} from 'angular2/core';
import {NgForm} from 'angular2/common';
import {ControlGroup, Control, FormBuilder, FORM_DIRECTIVES} from 'angular2/common'
#Component({
selector: 'stone-details',
templateUrl: '../stones/stone-details.component.html',
directives: [FORM_DIRECTIVES]
})
export class StoneComponent {
type = 'Stone';
Stones = new Array<Stone>();
addBtnClicked(){
let Stone = Object.create(window['Stone'].prototype);
//let Stone = new Stone('', '');
this.Stones.push(Stone);
}
removeBtnClicked(index: number){
if(index >= this.Stones.length){
alert('Not a valid index');
}else if(confirm('Remove this Stone?')){
this.Stones.splice(index, 1);
}
}
}
class Stone{
constructor(
public nameOfDeity?: string,
public typeOfDeity?: string
){}
}
When I use the commented line
let Stone = new Stone('', '');
the component works perfectly, but if I use
let Stone = Object.create(window['Stone'].prototype);
it doesn't seem to work and the error I see is
angular2.dev.js:23941 ORIGINAL EXCEPTION: TypeError: Cannot read property 'prototype' of undefined.
I initially thought exporting the Stone class would help, but none of the crazy variations (exporting the class, trying to refer to the class as window['StoneComponent'].export_1['Stone']) helped. I understand the component isn't directly visible under the window component, but I'm not sure what I'm missing. Is there an alternate way to doing this? Am I missing something? Please advise.
P.S: I'm using the latest version of Angular 2 and Typescript (I started this application a couple of days back).
The problem with your code is definition order.
Specifically, class definitions are not hoisted like function definitions are. The tricky part is that the type Stone is hoisted, which is perfectly valid, but the value Stone, the constructor function, is not.
To get around this just move the definition of Stone above the component or extract it into a separate module and import it.
Do not try to shove it into a global variable, say window. That is a very poor practice and will lead to bugs and name collisions faster than one might think. It also defeats the benefits of modules.
In short, what you need is
class Stone {
constructor(
readonly nameOfDeity?: string,
readonly typeOfDeity?: string
) {}
}
export class StoneComponent {
kind = 'Stone';
stones: Stone[] = [];
addBtnClicked() {
const stone = new Stone();
this.stones.push(stone);
}
removeBtnClicked(index: number) {
if (index >= this.stones.length) {
alert('Not a valid index');
} else if (confirm('Remove this Stone?')){
this.stones.splice(index, 1);
}
}
}
UPDATE
Since in the original question you state that this will be a generic component and you will have multiple classes where the actual class is selected by a kind property of the component class. You may want to consider the following pattern rather.
component-field-kinds.ts
export class Stone { ... }
export class Widget { ... }
export class Gizmo { ... }
generic-component.ts
import * as kinds from './component-field-kinds';
type Kind = keyof typeof kinds;
export class GenericComponent {
#Input() kind: Kind;
values: typeof kinds[Kind][] = [];
addBtnClicked() {
const value = new kinds[this.kind]();
this.values.push(value);
}
Note, for what it is worth, JavaScript, and therefore TypeScript has no such thing as a dynamic class loader. This is just how the language works all the time and the whole structure is first class.
This is not Java.
Since class is simple function you could create instance using new func(), but you still have to pass func from outer code.
I guess the most efficient solution is the following:
export class StoneComponent<T> {
addBtnClicked(){
let item:T = <T>{}
this.items.push(item);
}
}
types will match as long as objects have the same set of properties.
This question already has an answer here:
ES6 classes : what about instrospection?
(1 answer)
Closed 6 years ago.
I just can't find out where the references to declared ES6 classes are stored, I would have expected them in the window Object, bit they don't appear there.
I don't think it's a duplicate of ES6 classes : what about instrospection? since he is asking for a existance check of a class, what I want is a list of available classes.
For example:
class Test {
constructor() {
}
}
window.Test // undefined
What I want is a list of all classes that extend a class of mine
To clarify that I have a structure that looks something like this:
class Base {
constructor(components) {
for(let component of components) {
window[component](); // window.Test2 not defined
}
}
start() {
new this();
}
}
class Test2 extends Base {
constructor() {
super();
}
}
class Test extends Base {
constructor() {
super(['Test2','Test2']);
}
}
Test.start();
That's just an abstraction of my structure, in short I have to use strings at super(['Test2', 'Test2'])
At the moment I'm doing something like this
Base.register(Test2);
for every class and I want to get rid of that.
You can use Class expressions to store them in some sort of array, although I probably wouldn't do it, if I were you.
var Test = class Test {
constructor() {
}
}
allClasses.push(Test);
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.
Basically ES6 classes are compiled to plain old-fashioned Javascript functions. You may "store" them at the window object but this is a major pitfall as you are killing the whole module patter ES6 introduced.
If you want sort of a "module" of classes, you could theoretically do something like this:
// some-class.js
export default class SomeClass {}
Then:
// another-class.js
export default class AnotherClass {}
And your entry file:
// index.js
import SomeClass from './some-class.js' // extensions optional; here just for clarity
import AnotherClass from './another-class.js'
export default {
SomeClass,
AnotherClass
}
If you have all of those embedded in the same directory (we'll call the directory example), you can just import that entire module wherever you need it:
// something-that-requires-your-module.js
// this will by default enter through the `index.js` file of your `example` directory
import Example from './example';
console.log(Example.SomeClass);
console.log(Example.AnotherClass);