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).
Related
Reproduction :
// #flow
type A = { key: string, value: string};
const a:A = {
key: 'a',
value: 'a'
};
const foo = ():Promise<A> => {
return new Promise(function(resolve, reject){
setTimeout(function(){
resolve(a);
}, 1000);
});
}
const bar = async ():A => {
const res:A = ((await foo()):any);
return res;
}
bar();
Try it on flow.org/try
Context :
When calling a function called 'foo' returning a promise with await, the type of the variable is still Promise.
Flow correctly interprets the value if we just return the variable, but triggers an error if we type the return of the function called 'bar'.
19: return res;
^ Cannot return `res` because property `key` is missing in `Promise` [1] but exists in `A` [2].
References:
[LIB] static/v0.75.0/flowlib/core.js:583: declare class Promise<+R> {
^ [1]
17: const bar = async ():A => {
^ [2]
Solutions tried :
Forcing the type to 'A' of the variable calling await
Casting with any then 'A' didn't seem to solve the error.
Issues Related :
https://github.com/facebook/flow/issues/5294
Purpose of this question:
I am mostly looking for a workaround
This seems to be a simple misunderstanding, but the error message from Flow isn't very useful.
You've declared bar as
const bar = async (): A => {
but async functions always return promises, so it should be
const bar = async (): Promise<A> => {
You can see it here on flow.org/try.
How can i force ui router to reload the resolves on my state without reloading the entire ui/controller since
I am using components and since the data is binded from the state resolve,
i would like to change some parameters (pagination for example) without forcing the entire ui to reload but just the resolves
resolve : {
data: ['MailingListService', '$transition$', function (MailingListService, $transition$) {
var params = $transition$.params();
var ml = params.id;
return MailingListService.getUsers(ml, params.start, params.count)
.then(function (result) {
return {
users: result.data,
totalCount: result.totalCount
}
})
}],
node: ['lists', '$transition$', function (lists, $transition$) {
return _.find(lists, {id: Number($transition$.params().id)})
}]
},
I would like to change $transition$.params.{start|count} and have the resolve updated without reloading the html.
What you requested is not possible out of the box. Resolves are only resolved, when the state is entered.
But: one way of refreshing data could be, to check for state parameter changes in $doCheck and bind them to the components by hand.
Solution 1
This could look something like this:
export class MyComponent {
constructor($stateParams, MailingListService) {
this.$stateParams = $stateParams;
this.MailingListService = MailingListService;
this.paramStart = null;
this.paramCount = null;
this.paramId = null;
this.data = {};
}
$doCheck() {
if(this.paramStart !== this.$stateParams.start ||
this.paramCount !== this.$stateParams.count ||
this.paramId !== this.$stateParams.id) {
this.paramStart = this.$stateParams.start;
this.paramCount = this.$stateParams.count;
this.paramId = this.$stateParams.id;
this.MailingListService.getUsers(this.paramId, this.paramStart, this.paramCount)
.then((result) => {
this.data = {
users: result.data,
totalCount: result.totalCount
}
})
}
}
}
Then you have no binding in the parent component anymore, because it "resolves" the data by itself, and you have to bind them to the child components by hand IF you insert them in the template of the parent component like:
<my-component>
<my-child data="$ctrl.data"></my-child>
</my-component>
If you load the children via views, you are obviously not be able to bind the data this way. There is a little trick, but it's kinda hacky.
Solution 2
At first, resolve an empty object:
resolve : {
data: () => {
return {
value: undefined
};
}
}
Now, assign a binding to all your components like:
bindings: {
data: '<'
}
Following the code example from above, where you resolve the data in $doCheck, the data assignment would look like this:
export class MyComponent {
[..]
$doCheck() {
if(this.paramStart !== this.$stateParams.start ||
this.paramCount !== this.$stateParams.count ||
this.paramId !== this.$stateParams.id) {
[..]
this.MailingListService.getUsers(this.paramId, this.paramStart, this.paramCount)
.then((result) => {
this.data.value = {
users: result.data,
totalCount: result.totalCount
}
})
}
}
}
And last, you check for changes in the child components like:
export class MyChild {
constructor() {
this.dataValue = undefined;
}
$doCheck() {
if(this.dataValue !== this.data.value) {
this.dataValue = this.data.value;
}
}
}
In your child template, you access the data with:
{{ $ctrl.dataValue | json }}
I hope, I made my self clear with this hack. Remember: this is a bit off the concept of UI-Router, but works.
NOTE: Remember to declare the parameters as dynamic, so changes do not trigger the state to reload:
params: {
start: {
dynamic: true
},
page: {
dynamic: true
},
id: {
dynamic: true
}
}
I found this working example of Inheritance Patterns that separates business logic and framework code. I'm tempted to use it as a boilerplate, but since it is an inheritance Pattern, then how can I extend the business logic (the methods in var Speaker)?
For instance, how can I extend a walk: method into it?
/**
* Object Speaker
* An object representing a person who speaks.
*/
var Speaker = {
init: function(options, elem) {
// Mix in the passed in options with the default options
this.options = $.extend({},this.options,options);
// Save the element reference, both as a jQuery
// reference and a normal reference
this.elem = elem;
this.$elem = $(elem);
// Build the dom initial structure
this._build();
// return this so we can chain/use the bridge with less code.
return this;
},
options: {
name: "No name"
},
_build: function(){
this.$elem.html('<h1>'+this.options.name+'</h1>');
},
speak: function(msg){
// You have direct access to the associated and cached jQuery element
this.$elem.append('<p>'+msg+'</p>');
}
};
// Make sure Object.create is available in the browser (for our prototypal inheritance)
// Courtesy of Papa Crockford
// Note this is not entirely equal to native Object.create, but compatible with our use-case
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {} // optionally move this outside the declaration and into a closure if you need more speed.
F.prototype = o;
return new F();
};
}
$.plugin = function(name, object) {
$.fn[name] = function(options) {
// optionally, you could test if options was a string
// and use it to call a method name on the plugin instance.
return this.each(function() {
if ( ! $.data(this, name) ) {
$.data(this, name, Object.create(object).init(options, this));
}
});
};
};
// With the Speaker object, we could essentially do this:
$.plugin('speaker', Speaker);
Any ideas?
How about simply using JavaScript's regular prototype inheritance?
Consider this:
function Speaker(options, elem) {
this.elem = $(elem)[0];
this.options = $.extend(this.defaults, options);
this.build();
}
Speaker.prototype = {
defaults: {
name: "No name"
},
build: function () {
$('<h1>', {text: this.options.name}).appendTo(this.elem);
return this;
},
speak: function(message) {
$('<p>', {text: message}).appendTo(this.elem);
return this;
}
};
Now you can do:
var pp = new Speaker({name: "Porky Pig"}, $("<div>").appendTo("body"));
pp.speak("That's all folks!");
Speaker.prototype.walk = function (destination) {
$('<p>', {
text: this.options.name + " walks " + destination + ".",
css: { color: "red" }
}).appendTo(this.elem);
return this;
}
pp.walk("off the stage");
Runnable version:
function Speaker(options, elem) {
this.elem = $(elem)[0];
this.options = $.extend(this.defaults, options);
this.build();
}
Speaker.prototype = {
defaults: {
name: "No name"
},
build: function () {
$('<h1>', {text: this.options.name}).appendTo(this.elem);
return this;
},
speak: function(message) {
$('<p>', {text: message}).appendTo(this.elem);
return this;
}
};
var pp = new Speaker({name: "Porky Pig"}, $("<div>").appendTo("body"));
pp.speak("That's all folks!");
Speaker.prototype.walk = function (destination) {
$('<p>', {
text: this.options.name + " walks " + destination + ".",
css: { color: "red" }
}).appendTo(this.elem);
return this;
}
pp.walk("off the stage");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
I have created a plugin with following codes:
var myplugin = {
init: function(options) {
$.myplugin.settings = $.extend({}, $.myplugin.defaults, options);
},
method1: function(par1) {
.....
},
method2: function(par1) {
.....
}
};
$.myplugin = function(method){
if ( myplugin[method] ) {
return myplugin[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if (typeof method === 'object' || !method) {
return myplugin.init.apply(this, arguments);
} else {
$.error( 'Method "' + method + '" does not exist in myplugin!');
}
};
$.myplugin.defaults = {
option1: 'test',
option2: '',
option3: ''
};
$.myplugin.settings = {};
$.myplugin();
This works well but the issue is that when I try to set more than 1 option and try to return its values afterwards, it gives empty; setting one option works well. For eg.
If on changing the first combo box value I call this:
$.myplugin({option1: 'first test'});
it works, but when I try to call another on second combo box it doesn't save the option, instead it reset to empty.
Is there any fix?
I would re-organize the plugin to use this structure:
var methods = {
settings: {
foo: "foo",
bar: "bar"
},
init: function(options) {
this.settings = $.extend({}, this.settings, options);
},
method1: function(par1) {
alert(this.settings.foo);
},
method2: function(par1) {
alert(this.settings.bar);
}
};
function MyPlugin(options) {
this.init(options);
return this;
}
$.extend(MyPlugin.prototype, methods);
$.myPlugin = function(options) {
return new MyPlugin(options);
}
/* usage */
// without parameters
var obj1 = $.myPlugin();
// with parameters
var obj2 = $.myPlugin({foo: "foobar"});
// each has it's own settings
obj1.method1();
obj2.method1();
Demo: http://jsfiddle.net/ypXdS/
Essentially $.myPlugin simply creates and returns a new instance of the MyPlugin class. You could get rid of it completely and use new myPlugin(options) in it's place.
I'm trying to use a Backbone-relational and CoffeeScript in a project.The following is an example in CoffeeScript of the type of things I'm trying to model :
class NestedModel extends Backbone.RelationalModel
defaults:
Description: 'A nested model'
NestedModel.setup()
class MainModel extends Backbone.RelationalModel
defaults:
Description: 'A MainModel description'
StartDate: null
relations: [
type: Backbone.HasOne
key: 'nestedmodel'
relatedModel: 'NestedModel'
includeInJSON: '_id'
reverseRelation:
type: Backbone.HasOne
includeInJSON: '_id'
key: 'mainmodel'
]
MainModel.setup()
nm = new NestedModel()
mm = new MainModel(nestedmodel: nm)
console.log mm.get("nestedmodel").get("mainmodel").get("Description")
return
That CoffeeScript produces the following JavaScript:
var MainModel, NestedModel, mm, nm;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
NestedModel = (function() {
__extends(NestedModel, Backbone.RelationalModel);
function NestedModel() {
NestedModel.__super__.constructor.apply(this, arguments);
}
NestedModel.prototype.defaults = {
Description: 'A nested model'
};
return NestedModel;
})();
NestedModel.setup();
MainModel = (function() {
__extends(MainModel, Backbone.RelationalModel);
function MainModel() {
MainModel.__super__.constructor.apply(this, arguments);
}
MainModel.prototype.defaults = {
Description: 'A MainModel description',
StartDate: null
};
MainModel.prototype.relations = [
{
type: Backbone.HasOne,
key: 'nestedmodel',
relatedModel: 'NestedModel',
includeInJSON: '_id',
reverseRelation: {
type: Backbone.HasOne,
includeInJSON: '_id',
key: 'mainmodel'
}
}
];
return MainModel;
})();
MainModel.setup();
nm = new NestedModel();
mm = new MainModel({
nestedmodel: nm
});
console.log(mm.get("nestedmodel").get("mainmodel").get("Description"));
return;
Which produces the following warning and error
Warning:
Relation= child
; no model, key or relatedModel (function MainModel() {
MainModel.__super__.constructor.apply(this, arguments);
}, "nestedmodel", undefined)
Error:
Uncaught TypeError: Cannot call method 'get' of undefined
Simply removing the 'NestedModel' variable from the 1st line of generated JavaScript
var MainModel, NestedModel, mm, nm;
Causes the correct behaviour. Obviously I can't keep removing the variable definition from the generated JavaScript. What am I doing wrong?
Ok, it appears to be a scoping issue. See the following jsFiddle example. But why can't I just refer to the classes in the local function scope?
But why can't I just refer to the classes in the local function scope?
Classes are implemented as Immediately Invoked Function Expressions
The key to understanding design patterns such as immediately-invoked function expressions is to realize JavaScript has function scope (but not block scope) and passes values by reference inside a closure.
References
Immediately Invoked Function Expressions