I'm using ES5 flavor of Angular 2. Thus instead of using decorators, I define a template as follow:
var someComponent = ng.core.Component({
// component configuration
}).Class({
// component class
});
It was working fine. Recently I upgraded to Angular 5, and now I have these error for the same code:
ng.core.Pipe(...).Class is not a function
ng.core.Component(...).Class> is not a function
ng.core.Directive(...).Class is not a function
How should I change this in Angular 5?
JavaScript is currently considered second-class (no pun) citizen, while TypeScript is the language of choice, primarily because of the focus on AOT compilation, which is available for TypeScript only.
As explained here,
It is no longer possible to declare classes in this format.
Component({...}).
Class({
constructor: function() {...}
})
This format would only work with JIT and with ES5. This mode doesn’t
allow build tools like Webpack to process and optimize the code, which
results in prohibitively large bundles. We are removing this API
because we are trying to ensure that everyone is on the fast path by
default, and it is not possible to get on the fast path using the ES5
DSL. The replacement is to use TypeScript and #Decorator format.
#Component({...})
class {
constructor() {...}
}
As described in this answer, the alternative to decorators in plain ES5 and higher is the annotation of classes (and therefore, functions) with annotations and parameters static properties.
So
var SomeComponent = ng.core.Component({...})
.Class({
constructor: [
[new ng.core.Inject(Svc)],
function (svc) {...}
],
foo: function () {...}
});
becomes
SomeComponent.annotations = [new ng.core.Component(...)];
SomeComponent.parameters = [
[new ng.core.Inject(Svc)]
];
function SomeComponent(svc) {...}
SomeComponent.prototype.foo = function () {...};
As a replacement for Class functionality, any third-party utility class library can be used to introduce syntactic sugar and inheritance in ES5 as a substitute for functions, e.g. uberproto or inherits.
It it highly advisable to migrate existing Angular JavaScript applications to TypeScript, the latter offers superior performance and much smaller application footprint in AOT compilation mode, which is going to be default mode in future versions.
Related
I have an existing project (which is run fully in the browser, no server-side component) which uses RequireJS to manage dependencies. Previously the way I'd been defining "classes" (including inheritance) was like this:
define([
'jquery',
'classes/SomeParentClass',
], function($, SomeParentClass){
function ThisClass(data){
SomeParentClass.call(this, data);
this.init = function(data){
//Do stuff
}
this.init(data);
}
ThisClass.prototype = Object.create(SomeParentClass.prototype);
ThisClass.prototype.constructor = ThisClass;
return ThisClass;
});
And then when I wanted to include it in some other class I'd use the standard RequireJS define/require syntax:
define(['classes/ThisClass'], function(ThisClass){
var fooInstance = new ThisClass(fooData);
});
I wanted to try my hand at the "class" syntax in ES6, so I set about converting one of my existing classes (I started with a "static" class for simplicity). It currently looks something like this:
define([
'jquery',
], function($){
class SomeClass {
static someStaticFn(){
//Do whatever
}
}
});
Where I'm stymied currently is how to reference SomeClass (with the new ES6 syntax) using RequireJS. Assuming that's even possible.
UPDATE
As others have pointed out below, returning the class is all RequireJS cares about. I could have sworn that I already tried that, but sure enough it worked.
In case anyone else is a dummy like me and ends up here under similar circumstances, I want to also point out that I did have some success with the requirejs-babel plugin, however only after I'd re-written my class to use ES6 export and import statements instead of RequireJS's define. After having done that, anywhere else I was including the class I had to do it like so:
define(['es6!ThisClass'], function(ThisClass){ ...
I ended up choosing to modify a class that was included in every other module, so adding all those "es6!" strings turned out to be quite a drag. Although this article on porting existing projects to ES6 mentions a way of automating the process via RequireJS config.
However, as requirejs-babel transpiles the ES6 code at runtime, I will probably not incorporate that into my project at this time. If I do decide I want to go whole hog with ES6 I'd probably use "regular" Babel or possibly TypeScript
ES6 Class is just a sugarcoat and still with same implementation underneath. You can use it the old way as creating class by function. Also be careful with browser compatibility https://kangax.github.io/compat-table/es6/.
define([
'jquery',
], function($){
return class SomeClass {
static someStaticFn(){
//Do whatever
}
}
});
I have a Node application, which specifies babel with transform options and register in a main file, like so:
require('babel').transform('code', { stage: 1 });
require('babel/register')({ ignore: false });
require('../src/index');
Which does an excellent job of allowing me to use ES6 in ../src/index, and all subsequent files, but does not seem to allow me to use Decorators, as it should by declaring level 1 transform. I get instead a syntax error. Why is this not enabling decorator support? The actual decorator I'm trying to use is:
#test1
test Class() {
constructor() {
this.test = 'test';
}
}
function test1(obj) {
obj.test1 = 'test1';
}
That's not how you specify options, that first like does nothing. It compiles the JavaScript code code and does nothing with the result of the compilation.
require('babel/register')({
ignore: false,
stage: 1
});
require('../src/index');
Also generally ignore: false is a bad idea because some files should ignored.
Update
If you are using Babel 6, you'll want to download babel-plugin-transform-decorators-legacy rather than enabling stage: 1.
It's worth noting that the newest version of babel (v6) (over 1 month old) does not support decorators and the babeljs team has identified that they do not consider decorators to be a priority to fix -
https://twitter.com/sebmck/status/661501967412301824
As #jdanyow has mentioned in other answers you can track the issue here but beware if you choose to use Babeljs v6 that there is functionality from the previous release which is broken in the released v6 at the moment.
As #loganfsmyth points out in comments there is a plugin available babel-plugin-transform-decorators-legacy although it does mention that there are differences in the implementation, however I am not aware of them.
I'm using ECMAScript6 modules. What is the correct way to export/import multiple methods from a module from the options below?
Single class of static methods:
//------ myClass.js ------
export default class myClass {
static myMethod1() {
console.log('foo');
}
static myMethod2(args...) {
console.log('bar');
}
}
//------ app.js ------
import myClass from 'myClass';
myClass.myMethod1(); //foo
Multiple exported methods:
//------ myMethods.js ------
export function myMethod1() {
console.log('foo');
}
export function myMethod2() {
console.log('bar');
}
//------ app.js ------
import {myMethod1, myMethod2} from 'myMethods';
myMethod1() //foo;
//OR
import * as myMethods from 'myMethods';
myMethods.myMethod1() //foo;
1) Exporting:
A class of just static methods feels like a bit of a 'code smell' but similarly exporting everything individually does feel a bit verbose. Is it simply developer preference or are there performance implications here?
2) Importing:
'* as' syntax is my preferred method as it allows you to use the dot notation (referencing both the module AND the method) aiding code readability. Does this have performance implications though when I may only be using 1 of the methods?
A class of just static methods feels like a bit of a 'code smell'
Yes indeed. You don't need a class structure here! Just export a normal "module" object:
//------ myMethods.js ------
export default {
myMethod1() {
console.log('foo');
},
myMethod2(args...) {
console.log('bar');
}
};
I do recommend your second approach with multiple exports, though.
exporting everything individually does feel a bit verbose
Well, you don't need any wrapper structure, so I'd say it's less boilerplate. You just have to explicitly tag everything that you want to be exported, which is not a bad thing.
* as syntax is my preferred method as it allows you to use the dot notation (referencing both the module AND the method) aiding code readability.
That's very much personal preference, and does depend on the type of code you are writing. Sometimes conciseness is superior, but the ability to explicitly reference the module can be helpful as well. Notice that namespace imports using * as and default-imported objects are very similar here, though only named exports allow you to directly reference them via import {myMethod1, myMethod2}. So better leave the choice to those that import your module.
Does this have any performance implications?
Not much. Current ES6 implementations are not yet aiming for performance optimisations anyway.
In general, static identifiers are easier to resolve and optimise than property accesses[1], multiple named exports and partial imports could theoretically make JIT faster, and of course smaller files need less time to load if unused exports are removed during bundling. See here for details. There hardly will be noticeable performance differences, you should use what is better maintainable.
[1]: module namespaces (import * as ns) are static as well, even if ns.… looks like a dynamic property access
TLDR; Use multiple exported methods and explicit import.
#Bergi is right about not needing a class with static fields, only an object in your first case. However, this option is discouraged by Axel Rauschmayer:
Note that default-exporting objects is usually an anti-pattern (if you want to export the properties). You lose some ES6 module benefits (tree-shaking and faster access to imports).
Devs at Airbnb recommend named exports and explicit wildcrad import, see this thread: https://github.com/airbnb/javascript/issues/710#issuecomment-297840604
I am experiencing a really weird behavior and can't even say which package to blame for it.
My setup: RequireJS project with the JSXTransformer and the jsx! plugin
I have an es6 class like this:
define([
'react'
], function(
React
) {
class MyComponent extends React.Component {
myMethod() {
otherObject.someMethod()._privateProp; // Yes, we need this accessing and have no influence on it
}
}
return MyComponent;
});
The transpiled output in the resulting bundle after running r.js is:
define('jsx!project/components/InputOutput',[
'react'
], function(
React
) {
var ____Class8=React.Component;for(var ____Class8____Key in ____Class8){if(____Class8.hasOwnProperty(____Class8____Key)){MyComponent[____Class8____Key]=____Class8[____Class8____Key];}}var ____SuperProtoOf____Class8=____Class8===null?null:____Class8.prototype;MyComponent.prototype=Object.create(____SuperProtoOf____Class8);MyComponent.prototype.constructor=MyComponent;MyComponent.__superConstructor__=____Class8;function MyComponent(){"use strict";if(____Class8!==null){____Class8.apply(this,arguments);}}
MyComponent.prototype.myMethod=function() {"use strict";
otherObject.someMethod().$MyComponent_privateProp;
};
return MyComponent;
});
Note how otherObject.someMethod().$MyComponent_privateProp; is written there. This obviously breaks because it is not a property on instances of MyComponent.
Add /** #preventMunge */ to the top of the file. See this GitHub issue:
Yes, sorry this is a non-standard fb-ism. For now you can work around this and toggle this feature off by putting /** #preventMunge */ at the top of your file -- but that's also a pretty big fb-ism. We should (a) turn this into a transform option (rather than a direct docblock directive) and (b) make it opt-in rather than opt-out (since it's non-standard).
For context: We munge all under-prefixed object properties on a per-module basis partly because our under-prefix convention applies to both objects and classes. Additionally, even if we wanted to lax the objects vs classes distinction, it's impossible to tell (in the general case) if a property is a reference to this since alias variables can occur (i.e. var self = this; self._stuff;).
There is a very interesting picture was posted in the official TypeScript blog.
I wonder what the # (at sign) symbol is doing there since (as far as I know) it cannot be used in JavaScript identifiers.
The big news this week is the merging of AtScript and TypeScript.
The following example from the AtScript documentation...
#Component()
class MyApp {
server:Server;
#Bind('name') name:string;
#Event('foo') fooFn:Function;
#Inject()
constructor(#parent server:Server) {}
greet():string {}
}
Compiles into the following JavaScript...
function MyApp() {}
MyApp.properties = {
'server': { is: Server },
'name': { is:string,
annotate: [new Bind('name']},
'fooFn': { is:Function,
annotate:[new Event('foo')]}
}
MyApp.annotate = [
new Component(),
new Inject()
];
MyApp.parameters = [
{is:Server, annotate:[parent]}
];
MyApp.prototype.greet = function() {}
MyApp.prototype.greet.returns = string;
AtScript was planned to be a layer on top of TypeScript (i.e. a super-set of a super-set) - but now the two projects are one.
Annotations are described thus:
AtScript annotation syntax is just a shorthand of placing the same information in ES5. It would be reasonable for an ES5 developer to write these annotations manually. A helper library could even be provided.
Annotations can only be placed on functions.
An annotation placed on a class is an annotation placed on the class’ constructor function.
An annotation placed on a field gets moved to the constructor function.
All annotations are translated as properties on a function.