Override a setter, and the getter must also be overridden - javascript

class AbstractClass {
constructor() {
}
set property(value) {
this.property_ = value;
}
get property() {
return this.property_;
}
}
class Subclass extends AbstractClass {
constructor() {
super();
}
set property(value) {
super.property = value;
if (!(this.property_ instanceof SubclassAssociatedClass)) throw new TypeError();
}
//get property() {
// return super.property;
//}
}
Override the set method of an attribute and it appears the get method must be overridden also, otherwise undefined is returned (i.e., the get method is not inherited, uncomment the subclass get property() method above and everything works fine).
I assume this is a part of the spec., it would follow though possibly if the behaviour was a consequence of cross compiling. Just to be sure, is this the correct way to code overridden setters and getters (both at the same time or not at all)?

Yes, this is intentional (a part of the spec). If an object has an own property (.property in your example), this property will be used and not an inherited one. If that property is existent, but is an accessor property without a getter, then undefined will be returned.
Notice that this behaviour has not changed from ES5.

Related

How to detect data property on web component set or altered

HTML:
<comp-two></comp-two>
JS:
class CompTwo extends HTMLElement {
constructor() {
super()
this._options=[]
}
get options() {
return this._options
}
set options(val) {
this._options = val
}
}
const el = document.querySelector('comp-two')
el.options = ['one','two','three']
Is there an accepted method for notifying the code inside the webComponent that a property has been set? If it makes any difference I'm nesting web components.
I can see that setting an attribute would do it, but seems wasteful?
A property can be read directly:
console.log(this.propertyName)
But as the value could change any time after the component is created, the problem is to know when a change occurs, similar to how attributeChangedCallback is used to act on attribute updates.
A setter will trigger when the named property is written to. Just like attributeChangedCallback for attributes, the property's value has not necessarily changed.
set propertyName(val){
this._propertyName = val // keep a copy, name must be different
// underscore is popular method
this.doSomething(val) // act on the new property value
}
get propertyName(){
return this._propertyName
}
Note: After including a setter the property can no longer be read directly. Use a getter to return the local copy saved by the setter, or just read the local copy directly.
The penny finally dropped for me..

ES6 How to handle static property of sub class in super class

class Line extends Shape {
static howToDrag = { point: 0, update: 'xy' }
static config = { color: 'white' }
render() {
console.log('render based on this.config', this.config)
}
}
class Shape {
constructor({ config }) {
this.constructor.howToDrag = expandProperty1(this.constructor.howToDrag)
this.config = config || this.constructor.config
}
drag() {
console.log('drag based on this.constructor.howToDrag ', this.constructor.howToDrag)
}
}
function expandProperty1({ point, update }) {
if (typeof update === 'string') {
return {
point,
update: {
[point]: update
}
}
}
}
Here is the code, If you can understand what I am doing, skip the following explanation and provided some suggestions on what is the better way to make use of OOP because the current implementation is pretty wired from my perspective.
Check out http://canvas-chart.herokuapp.com/ to see what I am doing and I will explain the code above.
Let us talk about the static property config first. config should belong to each instance, i.e. each instance should have its own config, why I put it as static? The reason is that there is some common logic related to config, i.e. it should be the value passed from the constructor, otherwise, it has the default value. By setting the config as static, I am able to implement such common logic in 'Base' class, rather than write it over and over again in each Derived class
The other concern is howToDrag, it is a real static.However the static is used by base class but defined in the derived class, is it a normal thing? What is more, the static property in the derived class is mutated in the base class.
The current make logical sense but I always wonder if it is the way to do OOP because it relies on the fact this.constructor is base class point to the derived class.

Web Components: setter not being called

Say I have a Web Component:
customElements.define("custom-list", class CustomList extends HTMLElement {
get filter() {
console.log("get filter");
return this.getAttribute("filter");
}
set filter(value) {
console.log("set filter");
this.setAttribute("filter", value);
}
});
I wanted to use the setter method to do some initial attribute validation, but the setter never gets called. I tried setting the attribute through the HTML:
<custom-list filter="some value"></custom-list>
Only when I use JavaScript to set a property programmatically does the setter get called:
var list = document.querySelector("custom-list");
list.filter = "some value";
list.setAttribute("filter", "some value"); // DOESN'T WORK EITHER
So, it seems like setting attributes through the HTML or using setAttribute doesn't trigger the setter, which I partly can understand. My questions are:
Is the setter only necessary when I want to set properties programmatically?
How could I do initial validation of an attribute? In the connectedCallback? Say I want to only accept a certain string, how would I detect that?
Since the property filter gets populated anyway, do I need the setter if I don't use JavaScript to set my attributes?
Is the setter only necessary when I want to set properties programmatically?
Yes, at least if you want/need to run some tests/filtering upon the value you want to set.
How could I do initial validation of an attribute? In the connectedCallback? Say I want to only accept a certain string, how would I detect that?
Yep, connectedCallback or even in the constructor.
Since the property filter gets populated anyway, do I need the setter if I don't use JavaScript to set my attributes ?
No, you don't
This being said if you need a clear control over your custom attributes, i would suggest creating an internal state that you populate once when your custom element is being created and then when attributeChangedCallback is being called. That would give you some advantages :
you get control over the values that value your custom attributes.
you get an internal state that you can use to re-render your component if you need to
Here is an example :
customElements.define("custom-list", class CustomList extends HTMLElement {
static get observedAttributes() { return ['filter']; }
constructor() {
super();
this.state = {
filter: null
};
this.setFilter(this.getAttribute("filter"));
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "filter") {
this.setFilter(newValue);
}
}
getFilter() {
console.log("get filter");
return this.state.filter;
}
setFilter(value) {
// You can add some logic here to control the value
console.log("set filter");
this.state.filter=value;
}
});
Then you can call the following to change your internal state :
list.setAttribute("filter", "some value");
Would be interrested to get some feedback on this from the community. Anyway, hope this helps :)
getters and setters allow your code to receive values other than strings. Attributes are always strings and can only be called by JavaScript.
You can emulate a non-string in an attribute by parsing the value. But they are always passed in as strings.
If you want to have code run when an attribute is changed then you need to add the attributeChangedCallback function and indicate which attributes you are watching in the observedAttributes static getter. Attributes can be set in JavaScript by calling setAttribute and removeAttribute. They are also set when the browser parses your HTML due to a page load or setting innerHTML. But even then the browser eventually calls setAttribute in the background.
customElements.define("custom-list", class CustomList extends HTMLElement {
static get observedAttributes() { return ['filter']; }
constructor() {
super();
this._filter = null;
}
attributeChangedCallback(attr, oldVal, newVal) {
if (oldVal != newVal) {
// Only set this value if it is different
this.filter = newVal;
}
}
get filter() {
console.log("get filter");
return this._filter;
}
set filter(value) {
if (value !== this._filter) {
console.log(`set filter ${value}`);
this._filter=value;
this.textContent = value;
// If you want the filter property to always show
// in the attributes then do this:
if (value !== null) {
this.setAttribute('filter', value);
} else {
this.removeAttribute('filter');
}
}
}
});
const el = document.querySelector('custom-list');
setTimeout(() => {
el.filter = 'happy';
}, 2000);
<custom-list filter="10"></custom-list>
Always check to see if your oldVal and newVal are different in the function attributeChangedCallback.
It is also recommended that you check for different values in your setters.
Setters also allow you to take specific data types. For example you could check to see if the value for value was a number and, if not, throw a TypeError.
Setters also allow you to make sure a value is valid. Maybe it must be a positive number or one of three possible strings. If it isn't you can throw a RangeError.
But you have to remember that attributes are always strings. And properties can be anything.

Calling regular function vs. arrow function before their declaration in a class

If I write a React class like this:
class SomeClass extends React.Component {
state = {
someState: this.someRegularFunction(),
someOtherState: this.someArrowFunction()
};
someRegularFunction() {
return "someText";
}
someArrowFunction = () => {
return "someOtherText";
};
}
Webstorm code assistance warns about the calling of arrow function this.someArrowFunction() saying:
Field 'someArrowFunction' is declared after 'state' and is possibly
not assigned yet
If does not warn about the call of the regular function this.someRegularFunction().
And Webstorm is correct, the execution fails when calling this.someArrowFunction() with:
TypeError: _this.someArrowFunction is not a function
I've been looking for some documentation explaining this behavior, but have been unable to find any.
Why can you call regular functions, but not arrow functions, before their declaration inside a class?
Because that code is functionally identical to this:
class SomeClass extends React.Component {
constructor(...args) {
super(...args);
this.state = {
someState: this.someRegularFunction(),
someOtherState: this.someArrowFunction()
};
this.someArrowFunction = () => {
return "someOtherText";
};
}
someRegularFunction() {
return "someText";
}
}
Field definitions are processed in source code order, when the instance is created. It's as though they were inserted into the constructor before any other code (in a base class) or just after the call to super (in a subclass).
In contrast, someRegularFunction is a method of the prototype, which is created when the class definition is evaluated, not later when the instance is created.
This is covered by the proposal for the class fields feature, in the specification text. (Reading spec text is not for the faint-hearted, though! :-) )
Side note: It's arguably a matter of style, but if you're doing that arrow function so that it can use this without your worrying about how it's called (for instance, as an event handler), you might consider making it a method and then using bind in the constructor (or effectively in the constructor) instead:
class SomeClass extends React.Component {
someFunction = this.someFunction.bind(this);
state = {
someState: this.someRegularFunction(),
someOtherState: this.someFunction()
};
someRegularFunction() {
return "someText";
}
someFunction() {
return "someOtherText";
}
}
That plays more nicely with testing code that may need to mock the function (by replacing it on the prototype).
But again, it's arguably a matter of style.

Babel.js method decorators applying to class instead of method

While attempting to apply a decorator to a class' method, It's seemingly applying it to the class instead. Me, being not all that familiar with decorators/annotations, It's more than likely user-error.
Here's a really quick example that I've whipped up:
class Decorators {
static x (y, z) {
return method => {
// do stuff with y, z, method
// method should be the method that I decorate
}
}
}
class Foo {
#Decorators.x('foo', 'bar')
static main () {
// ...
}
}
As you can see, inside of the decorator, the method should be equal to the static main method, but when I add a console.log to the decorator to see what the method is, it logs [Function: Foo] (which is the Foo class after transpilation via Babel)...
From what I can tell, Babel.js is applying the decorator to the class, even though it's set on the method. I could be wrong, though.
The first parameter is the target (it can be the class constructor - for statics, the prototype - for non-statics, and the instance, in case of properties). And the second one is the method's name:
return (target, name, descriptor) => {
console.log(name);
}

Categories