Skip to content

Commit

Permalink
Add class field support (#78)
Browse files Browse the repository at this point in the history
* Handle class field

* Add class fields docs

* Update docs for consistency with obeservable-object

* Use steal-conditional

* remove additional line break
  • Loading branch information
cherifGsoul authored Jun 11, 2020
1 parent 895b47c commit 138cbe9
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 1 deletion.
1 change: 1 addition & 0 deletions can-observable-array-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ QUnit.module("can-observable-array", function() {
require("./test/items-test")();
require("./test/propdefaults-test")();
require("./test/steal-import-test");
require('./test/class-field-test#?can-observable-array/class-fields-support');
});
12 changes: 12 additions & 0 deletions class-fields-support.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
let supportsClassFields;

try {
eval(`class Foo {
field = "value"
}`);
supportsClassFields = true;
} catch(e) {
supportsClassFields = false;
}

module.exports = supportsClassFields;
20 changes: 20 additions & 0 deletions doc/can-observable-array.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,26 @@ const listInstance = new MyArray(["a", "b"]);
listInstance.on( "length", function( event, newLength, oldLength ) { /* ... */ } );
```

`ObservableArray` [class fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields) are also observables, example:


```js
import { ObservableArray } from "can/everything";

class MyArray extends ObservableArray {
prop = ['foo', 'bar'];
}

const myInstance = new MyArray();

myInstance.on( "prop", ( event, newVal, oldVal ) => {
console.log( newVal ); //-> ['baz']
console.log( oldVal ); //-> ['foo', 'bar']
});

myInstance.prop = ['baz'];
```


## Mixed-in type methods and properties

Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"can-observation": "^4.2.0",
"jshint": "^2.9.1",
"steal": "^2.2.1",
"steal-conditional": "^1.1.3",
"steal-qunit": "^2.0.0",
"testee": "^0.9.1"
},
Expand All @@ -52,6 +53,9 @@
},
"browserslist": "ie 11",
"steal": {
"main": "src/can-observable-array.js"
"main": "src/can-observable-array.js",
"configDependencies": [
"./node_modules/steal-conditional/conditional.js"
]
}
}
26 changes: 26 additions & 0 deletions src/can-observable-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,32 @@ class ObservableArray extends MixedInArray {
for(let i = 0, len = items && items.length; i < len; i++) {
this[i] = convertItem(new.target, items[i]);
}
const obj = this;
// Define class fields observables
//and return the proxy
return new Proxy(this, {
defineProperty(target, prop, descriptor) {
if ('items' === prop) {
throw new Error('ObservableArray does not support a class field named items. Try using a different name or using static items');
}

const props = target.constructor.props;
let value = descriptor.value;

if (!value && typeof descriptor.get === 'function') {
value = descriptor.get();
}

if (value) {
if (props && props[prop]) {
obj[prop] = value;
return true;
}
// Make the property observable
return mixins.expando(target, prop, value);
}
}
});
}

static get [Symbol.species]() {
Expand Down
77 changes: 77 additions & 0 deletions test/class-field-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const ObservableArray = require("../src/can-observable-array");
const QUnit = require("steal-qunit");

QUnit.module('can-observable-array-class-fields');

QUnit.test("Class properties default value", function(assert) {
const done = assert.async();

class MyList extends ObservableArray {
/* jshint ignore:start */
prop = ['foo', 'bar'];
/* jshint ignore:end */
}

const aList = new MyList();
assert.deepEqual(aList.prop, ['foo', 'bar'], 'Default value works');

aList.on('prop', function(ev, newVal, oldVal) {
assert.deepEqual(oldVal, ['foo', 'bar'], 'Old value is correct');
assert.deepEqual(newVal, ['baz'], 'Value is updated');
assert.ok(ev, 'Age is observable');
done();
});

aList.prop = ['baz'];
});


QUnit.test('Throws on class field property named items', function (assert) {
class MyList extends ObservableArray {
/* jshint ignore:start */
items = ['foo', 'bar'];
/* jshint ignore:end */

static get items() {
return String;
}
}

try {
new MyList();
} catch (error) {
assert.ok(error, 'it throws');
assert.equal(
error.message,
'ObservableArray does not support a class field named items. Try using a different name or using static items',
'Message is correct'
);
}

});

QUnit.test('handle descriptor getter', function(assert) {
assert.expect(4);

const foo = new ObservableArray();

let _bar = "Hello";
Object.defineProperty(foo, "bar", {
get() {
return _bar;
},
set(val) {
_bar = val;
}
});

assert.equal(foo.bar, 'Hello');

foo.on('bar', function (ev, newVal, oldVal) {
assert.equal(oldVal, 'Hello', 'Old value is correct');
assert.equal(newVal, 'Hola', 'Value is updated');
assert.ok(ev, 'The class field is observable');
});

foo.bar = 'Hola';
});

0 comments on commit 138cbe9

Please sign in to comment.