Is there a way to have kendo.observable add non enumerable properties to the object it returns? For example:
var someObject = {};
someObject.x = 5;
Object.defineProperty(someObject, "someString", {
enumerable: false,
configurable: false,
writable: true
});
someObject.someString = "This is the base";
var foo = kendo.observable(someObject)
"x" in foo; //true
"someString" in someObject; //true
"someString" in foo; //false, but I want this to be true
Interesting question. Actually when you create new observable object out of a object that has a property which is not enumerable. The initialization actually skips to create such property.
Instead you can create observable object out of a regular object (where all properties are enumerable) and then you can change the enumerable property to be false.
Here is what I mean:
var bar = kendo.observable( { blabla : 123 });
Object.defineProperty(bar, 'blabla', { enumerable: false });
var isEnumerable = Object.getOwnPropertyDescriptor(bar, 'blabla').enumerable // returns false
'blabla' in bar //returns true
Related
I have this custom validator:
export const mealTypesValidator = (mealSelected: boolean) => {
return (control: FormControl) => {
var mealTypes = control.value;
if (mealTypes) {
if (mealTypes.length < 1 && mealSelected) {
return {
mealTypesValid: { valid: false }
};
}
}
return null;
};
};
If I use it like this it works:
ngOnInit() {
this.findForm = this.formBuilder.group({
categories: [null, Validators.required],
mealTypes: [[], mealTypesValidator(true)],
distanceNumber: null,
distanceUnit: 'kilometers',
keywords: null,
});
}
The catch is, mealSelected is a property on my component - that changes when the user selects and deselects a meal.
How I call the validator above is using static true which can never change.
How can I get the validator to work when I use the component.mealSelected value as the parameter eg:
ngOnInit() {
this.findForm = this.formBuilder.group({
categories: [null, Validators.required],
mealTypes: [[], mealTypesValidator(this.mealSelected)],
distanceNumber: null,
distanceUnit: 'kilometers',
keywords: null,
});
}
Because if i do it as above, it evaluates this.mealSelected instantly which is false at the time - and then when the user selects a meal, it doesn't then go ahead and pass true into the custom validator.
Solution was to move the validator inside my component and use this.mealSelected to check against. Then I had an issue with the validator not being triggered when a meal was selected/deselected and I used this.findForm.controls['mealTypes'].updateValueAndValidity(); to trigger the validation.
Code (can probably be refactored to remove the parameter from the custom validator):
ngOnInit() {
this.findForm = this.formBuilder.group({
categories: [null, Validators.required],
mealTypes: [[], this.mealTypesValidator(true)],
distanceNumber: null,
distanceUnit: 'kilometers',
keywords: null,
});
}
mealTypesValidator = (mealSelected: boolean) => {
return (control: FormControl) => {
var mealTypes = control.value;
if (mealTypes) {
if (mealTypes.length < 1 && this.mealSelected) {
return {
mealTypesValid: { valid: false }
};
}
}
return null;
};
};
However It would still be nice to be able to have a seperate validation module to centralise validation, so if anyone knows how to have a changing parameter value such as a component field as a parameter to a custom validator - like I initially asked, then I'd appreciate an answer that goes with that technique.
This check used to pass:
expect(array).toContain(value)
Array:
[
{"_t":"user","id":1073970419,"email":"email3#example.org","name":"Spectator"},
{"_t":"user","id":4464992042,"email":"email4#example.org","name":"Collaborator"},
{"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"}
]
Value:
{"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"}
But no longer passes. Whats the new way to write the same test?
The syntax you need is:
const obj = {"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"};
expect(array).toContain(jasmine.objectContaining(obj));
See fiddle: https://jsfiddle.net/bblackwo/4o5u5Lmo/16/
It won't contain that object (remember, two objects with the same properties are not the same object for the purposes of equality), so toContain will never pass.
You need to use another test, like toEqual or (if you only want to check for a subset of properties), toEqual combined with jasmine.objectContaining.
Here's the toEqual example from the Jasmine documentation on that page:
describe("The 'toEqual' matcher", function() {
it("works for simple literals and variables", function() {
var a = 12;
expect(a).toEqual(12);
});
it("should work for objects", function() {
var foo = {
a: 12,
b: 34
};
var bar = {
a: 12,
b: 34
};
expect(foo).toEqual(bar);
});
});
Note now foo equals bar.
Here's their example using jasmine.objectContaining:
describe("jasmine.objectContaining", function() {
var foo;
beforeEach(function() {
foo = {
a: 1,
b: 2,
bar: "baz"
};
});
it("matches objects with the expect key/value pairs", function() {
expect(foo).toEqual(jasmine.objectContaining({
bar: "baz"
}));
expect(foo).not.toEqual(jasmine.objectContaining({
c: 37
}));
});
// ...
});
Note how the object with several properties matches the partial object supplied to jasmine.objectContaining.
#T.J.Crowder already explained precisely the problem. Just to help you a tad more, if you want to adapt your example, you'd need something like this:
var userA = {"_t":"user","id":1978569710,"email":"email5#example.org","name":"Manage"}
array =
[
{"_t":"user","id":1073970419,"email":"email3#example.org","name":"Spectator"},
{"_t":"user","id":4464992042,"email":"email4#example.org","name":"Collaborator"},
userA
]
expect(array).toContain(userA);
I an using ES6 classes plus Object.observer, using the MaxArt2501 implementation. I have this code below:
const READY = Symbol("Ready");
const RUNNING = Symbol("Running");
class Foo {
constructor() {
this.value = 1;
this.status = READY;
}
bar() {
this.status = RUNNING;
console.log("bar", this.status );
this.value = this.value + 10;
this.status = READY;
console.log("bar", this.status );
}
}
let foo = new Foo();
Object.observe(foo, function(changes) {
changes.forEach(function(change) {
console.log("foo: ", change);
});
});
My expectation was that when I execute the bar() method, the observer function would be called 3 times: When changing foo.status from READY to RUNNING, when changing foo.value and when changing foo.status from RUNNING to READY.
However, this is what I got when running bar() (In Chrome console):
> foo
Foo {value: 1, status: Symbol(Ready)}
> foo.bar()
bar Symbol(Running)
bar Symbol(Ready)
undefined
foo: Object {name: "value", type: "update", object: Foo, oldValue: 1}
> foo
Foo {value: 11, status: Symbol(Ready)}
The undefined is the return from bar() method. But any way, it looks like just the changing in foo.value is really observed, but not foo.status.
If I change foo.status value "manually" it is observed:
> foo.status = RUNNING
Symbol(Running)
foo: Object {name: "status", type: "update", object: Foo, oldValue: Symbol(Ready)}
> foo
Foo {value: 11, status: Symbol(Running)}
> foo.status = READY
Symbol(Ready)
foo: Object {name: "status", type: "update", object: Foo, oldValue: Symbol(Running)}
> foo
Foo {value: 11, status: Symbol(Ready)}
I could say that when a property class is changed inside the class it does not notify its observer. However, the foo.value change in bar() method was observed.
Does someone have any idea about what is happening here?
Thanks,
Rafael Afonso
UPDATE - 06/06
I did a workaround which, although not exactly very elegant, I could use as base in my real implementation:
const READY = Symbol("Ready");
const RUNNING = Symbol("Running");
class Foo {
constructor() {
this.value = 1;
this.status = READY;
}
bar() {
// this.status = RUNNING;
console.log("bar", this.status );
this.value = this.value + 10;
// this.status = READY;
console.log("bar", this.status );
}
}
let foo = new Foo();
Object.observe(foo, function(changes) {
changes.forEach(function(change) {
if(change.name === 'value') {
change.object.status = RUNNING;
}
console.log("foo: ", change);
});
});
When I run, I got this:
> foo.bar()
bar Symbol(Ready)
bar Symbol(Ready)
undefined
foo: Object {name: "value", type: "update", object: Foo, oldValue: 1}
foo: Object {name: "status", type: "update", object: Foo, oldValue: Symbol(Ready)}
As you see, I change the foo.status in observable method. However, apparently just after the foo execution, both properties changes are notified.
I was thinking if would not be better use a approach such as Observable and Observer in Java. Does somebody know some JS library which implements in this way?
UPDATE - 06/06 (2)
As suggested, I tried use proxies:
const READY = Symbol("Ready");
const RUNNING = Symbol("Running");
class Foo {
constructor() {
this.value = 1;
this.status = READY;
}
bar() {
this.status = RUNNING;
// console.log("bar", this.status );
this.value = this.value + 10;
this.status = READY;
// console.log("bar", this.status );
}
}
let observer = {
set: function(obj, prop, value) {
let oldValue = obj[prop];
let result = Reflect.set(obj, prop, value);
console.log(prop + ": " + oldValue.toString() + " -> " + value.toString());
return result;
}
}
let foo = new Proxy(new Foo(), observer);
When I runned, I got this:
> foo.bar()
status: Symbol(Ready) -> Symbol(Running)
value: 1 -> 11
status: Symbol(Running) -> Symbol(Ready)
undefined
> foo.bar()
status: Symbol(Ready) -> Symbol(Running)
value: 11 -> 21
status: Symbol(Running) -> Symbol(Ready)
undefined
Indeed, this is what I was looking for. Although the Object.observer goals to intercept all kind of changes in the target, I am interested just in the properties setting.
A quick skim of that repo suggests that updates are only performed once per frame (in environments with such a notion) or once every 17ms.
Because JavaScript is single-threaded, if you change a property and and then change it back within a single uninterrupted function (i.e. not a generator etc), it will not be observable by this package.
Keep in mind that Object.observe is no longer on track to become part of the language. An actual implementation of the proposed spec could have given you notifications for each event, but it's not possible to fake for this specific situation (at least without wrapping every object in a proxy).
Can not find Rx.Observable.pairs in Rxjs5,
what I need just convert an object into Observable and inspect the change for each property.
any ideas?
var a = { aa: "aa", bb: "bb" };
function pairs(obj) {
// List of object's key-value pairs
var keyValuePairs = Object.keys(obj).map(key => ({ key, value: obj[key] }));
// Convert to an Observable and return
return Rx.Observable.from(keyValuePairs);
}
var xxx = pairs(a);
xxx.subscribe(x => {
console.log(x);
})
a.aa = "mm";
You can accomplish this from scratch:
function pairs(obj) {
// List of object's key-value pairs
var keyValuePairs = Object.keys(obj).map(key => ({ key, value: obj[key]}));
// Convert to an Observable and return
return Rx.Observable.from(keyValuePairs);
}
I have this Parse.Object that I want to save to the server, but I'd like to whitelist the attributes of this object that get saved.
Parse.Object.extend('someObject', {
defaults: {
foo: 1,
bar: 2,
computedProperty: function() {
return this.get('foo') + this.get('bar')
}
},
get: function(attr) {
var value = Parse.Object.prototype.get.call(this, attr)
return _.isFunction(value) ? value.call(this) : value
}
})
As you can see, this object has a computed property among its attributes. I would like to filter out the computedProperty when I save this Parse.Object. Is that possible?
So, we've figured out a way to filter the list of attributes that get saved.
If you wanna do it, you have to override a private, undocumented method on the Parse.Object called _getSaveJSON, so the complete model above would be:
Parse.Object.extend('someObject', {
defaults: {
foo: 1,
bar: 2,
computedProperty: function() {
return 1+2
}
},
get: function(attr) {
var value = Parse.Object.prototype.get.call(this, attr)
return _.isFunction(value) ? value.call(this) : value
},
_getSaveJSON: function() {
var model = this
var json = _.clone(_.first(this._opSetQueue))
Parse._objectEach(json, function(op, key) {
json[key] = op.toJSON();
});
var whitelistedAttributes = ['foo', 'bar']
return _.pick(json, whitelistedAttributes)
}
})