I try to use a decorator to add some method or property to an annotated class.
This is my decorator :
export default function Component(params) {
return function decorator(target) {
target.template = params.template;
console.log(target, params.template);
}
}
I used it this way :
#Component({
template: template
})
export default class App {}
But when I use this class :
app.template // undefined;
Any idea?
You are modifying class object itself, i.e.
App.template // your template is here
and you just defined a static property for App class in this case.
In order to set template for class instances you should use:
target.prototype.template = params.template;
(Good example that classes are actually just syntactical sugar over constructor functions and prototype-based inheritance).
Also, I think this article about combining mixins and decorators might be helpful.
Related
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();
I have an abstract super class that defines a property "proposal".
export abstract class myBaseClass {
public proposal: Proposal;
}
and a class that extends it
export class mySubClassComponent Component extends myBaseClass{
constructor() {
super();
}
}
I try to access the property "proposal" of myBaseClass in the template of mySubClassComponent like this:
*ngIf="proposal.position"
but I'm getting the following error
TypeError: Cannot read property 'position' of undefined
how can I have access to this property inside the mySubClassComponent tempalte?
You are accessing it correctly. However, you have defined the type but, in fact, the class property is actually undefined.
Just add a null check in your ngIf and you will be done:
*ngIf="proposal?.position"
This will avoid undesired errors if proposal is declared but its value is undefined.
Basically, to make it clearer, this:
export abstract class myBaseClass {
public proposal: Proposal;
}
is the same as this:
export abstract class myBaseClass {
public proposal: Proposal = undefined;
}
Keep in mind that, in typescript, declaring a variable type have no effects after the code is compiled. If a variable is undefined, it will be undefined at runtime, regardless the type you have declared in your typescript code.
I'm encapsulating a Menu called JellMenu, and I want to expose CustomMenuItem like JellMenu.Item instead of import CustomMenuItem. I have no idea how to do.
I took look at react-native-viewpager source code, and I think this maybe helpful:
var ViewPager = React.createClass({
statics: {
DataSource: ViewPagerDataSource,
},
It will expose ViewPagerDataSource as ViewPager.DataSource, right? I don't know exactly. what's the meaning of the keyword statics in Es5, and what's the alternative in Es6? Thank you in advance.
It isn't a keyword, it's just a property name in an object initializer being passed to React.createClass. From the documentation, it's used to specify static (non instance-specific) parts of a React class:
The statics object allows you to define static methods that can be called on the component class.
Although statics only works for React.createClass, you can still write static methods in ES6 notation. If you are using ES7, then you can also write static properties.The statics object allows you to define static methods that can be called on the component class. For example:
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
render: function() {
}
});
MyComponent.customMethod('bar'); // true
Methods defined within this block are static, meaning that you can run them before any component instances are created, and the methods do not have access to the props or state of your components. If you want to check the value of props in a static method, have the caller pass in the props as an argument to the static method.
Also, You can write statics inside ES6+ classes this way:
class Component extends React.Component {
static propTypes = {
...
}
static someMethod(){
}
}
Or outside the class like this:
class Component extends React.Component {
....
}
Component.propTypes = {...}
Component.someMethod = function(){....}
I'm trying to use Flowtype in an AngularJS (1.5) project but it complains about the $inject annotation. What is the correct way to handle this?
Flow version 0.30.0
Example code
navigation/links-controller.js
export default class LinksController {
constructor(navigationService) {
this.availableLinks = navigationService.availableLinks;
}
}
LinksController.$inject = ['NavigationService'];
navigation/index.js
...
import NavigationService from './navigation-service';
import LinksController from './links-controller';
export default angular.module('app.links', [uirouter])
.config(routing)
.service('NavigationService', NavigationService)
.controller('LinksController', LinksController)
.name;
Example flowtype output
LinksController.$inject = ['NavigationService'];
^^^^^^^ property `$inject`. Property not found
The reason for this is that by creating a class you are defining its interface in Flow.
When you are assigning $inject to the class you are effectively adding a new property that was not defined in the class interface and that is a type error in Flow.
You have two options for making this type check in Flow:
Adding a static property type definition:
class LinksController {
static $inject: Array<string>;
constructor() {...}
}
LinksController.$inject = ['NavigationService'];
Adding a static property:
class LinksController {
static $inject = ['NavigationService'];
constructor() {...}
}
With the second option you are going to need to enable esproposal.class_static_fields=enable within the [options] section of your .flowconfig
Because this is a proposal not yet added to the JavaScript standard and not available in any browsers, you will also need to compile it with something like Babel (you'll need either the stage-2 preset or the transform-class-properties plugin).
If you are using external dependency injection then you might want to link the functions like below.
angular
.module('app',[])
.controller('LinkCtrl', LinkCtrl);
LinkCtrl.$inject = ['NavigationService'];
Can you please share your full snipper if you have exactly done like above?
I have some use cases for inheritance and decoration (as in Decorator pattern) of components and directives in Angular 2.
The example is component with basic template that doesn't suit the case, to the point when it easier to define a new template instead of modifying the DOM of existing one programmatically. The rest of component metadata should be inherited.
Basically it is
export const BASE_SOME_COMPONENT_METADATA = { ... };
#Component(BASE_SOME_COMPONENT_METADATA);
export class BaseSomeComponent { ... }
...
import { BaseSomeComponent, BASE_SOME_COMPONENT_METADATA } from '...';
#Component(Object.assign({}, BASE_SOME_COMPONENT_METADATA, { template: '...' });
export class SomeComponent extends BaseSomeComponent {}
And more complicated case is
#Component({ ... });
export class ThirdPartyComponent {
#Input() ...;
#Input() ...;
#Input() ...;
...
}
...
import { ThirdPartyComponent as BaseThirdPartyComponent } from '...';
#Component({
// may modify or replace any of the original properties
template: ...,
styles: ...
...
});
export class ThirdPartyComponent extends BaseThirdPartyComponent {}
Notice that ThirdPartyComponent has numerous inputs. There may be cases when it is beneficial to modify a component internally instead of wrapping it and modifying its behaviour from the outside. A wrapper component that enumerates them all in template and transfers them to ThirdPartyComponent may be WET and dirty:
<third-party inputs="inputs" that="that" should="should" be="be" enumerated="enumerated">
Extra layout elements that are introduced by wrapper components may be prohibited in some cases.
ThirdPartyComponent may be core component (a button) that is used by other third-party components. Then they should be affected too, so it may be necessary to 'decorate the decorator' all over the injector, not just extend it.
In Angular 1.x thirdPartyDirective is a service that gives full access to component DDOs, so they could be decorated, extended, deep fried, etc. What are direct counterparts to this approach in Angular 2? If this breaks some rules and voids the warranty, it's ok.
How can a component/directive that doesn't export its metadata be extended?
How can the metadata of existing component be modified?
Your inputs will be inherited from the parent class automatically. Regarding properties of the Component decorator itself, there is no native way to do that in Angular2. The Angular2 team won't provide support for this: https://github.com/angular/angular/issues/7968#issuecomment-219865739.
If you really want something like that, you need to implement with a custom decorator that updates annotations...
This article could interest you:
https://medium.com/#ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7#.8r5fuys6l