Observing private properties in Sproutcore - javascript

Is it possible to observe private (underscored ) properties from
within the object itself?
I need to know when _view_layer is getting set, so that I can apply
some jQuery even handlers. Unfortunately init() and render() are
really early, so _view_layer is undefined.
Unfortunately, observing _view_layer doesn't seem to work as well.
Please, tell me what I can do. Basically, if there is another possible
solution, I am open to see that as well

In Sproutcore the underscore is only a convention that the property/method is private. Its not actually private.
In Sproutcore, the views have life-cycle methods. This one might be of interest (taken from SC 1.4.5 code in view):
didCreateLayer: the render() method is used to generate new HTML.
Override this method to perform any additional setup on the DOM you
might
need to do after creating the view. For example, if you need to
listen
for events.
Views have changed drastically in SC 1.6 and later, but I believe that didCreateLayer is still recognized.

(function() {
var value = obj._view_layer;
delete obj._view_layer;
var callback = function() {
/* observation logic */
}
Object.defineProperty(obj, "_view_layer", {
get: function() {
return value;
},
set: function(val) {
value = val;
callback(val);
},
writable: true,
enumerable: true
});
})();
Requires an ES5 browser.
Only recommended to use for debugging. You can also use .watch when debugging in firefox.

Related

Getter functions

I've peeked into many plugins' code (for educational purposes) and basically every one of them (which deals with prototypes), has bunch of functions like this:
myMarker.prototype.getPosition = function() {
return this.latlng;
};
//OR
myMarker.prototype.getObject = function() {
return this;
};
What's the reason behind this?
Why not just to use someObject.latlng instead of someObject.getPosition()?
One common reason for doing this is to avoid coupling the object's internal data storage to the API; in this example you could change the way the position is stored internally, and then add some processing to getPosition() to return a backwards compatible result.
For example, version 1.1 of this library might look like this, and calling code wouldn't need to be changed:
myMarker.prototype.getPosition = function() {
return this.latitude + this.longitude;
};
It is possible to accomplish this using computed properties with ES5 get and set, but only if the code doesn't need to run on Internet Explorer 8 and below.
When you say like this.
myMarker.prototype.getPosition = function() {
return this.latlng;
};
You are defining function getPosition which available to all instance to class myMarker.
So,all object of this class share this method without replication.
For someObject.latlng,there is nothing wrong.
But assume, this object is accessible to all which are in the current scope.So,it can be modified/accessible to anyone.
When you go through prototype you are trying to define some pattern,which gives restriction for access and modification of property

Calling one helper from another helper within the context of a template (Meteor 0.9.4)

As of Meteor 0.9.4, defining Template.MyTemplate.MyHelperFunction() is no longer valid.
We deprecated the Template.someTemplate.myHelper = ... syntax in favor of Template.someTemplate.helpers(...). Using the older syntax still works, but it prints a deprecation warning to the console.
This seemed fine to me, as it would (at the least) save some mis-typing and duplicated text. However, I soon discovered that the way I was building Meteor apps had leaned on an ability that this new version has deprecated. In my apps, I've been defining helpers/functions with the old syntax, then calling those methods from other helpers. I found it helped me keep my code clean and consistent.
For example, I might have a control like this:
//Common Method
Template.myTemplate.doCommonThing = function()
{
/* Commonly used method is defined here */
}
//Other Methods
Template.myTemplate.otherThing1 = function()
{
/* Do proprietary thing here */
Template.myTemplate.doCommonThing();
}
Template.myTemplate.otherThing2 = function()
{
/* Do proprietary thing here */
Template.myTemplate.doCommonThing();
}
But this does not appear to be available with the new method Meteor suggests (which makes me think I was wrong all along). My question is, What is the preferred way to share common, template-specific logic between a template's helpers?
Sorry if I'm being dull, but couldn't you declare the function as an object and assign it to multiple helpers? For instance:
// Common methods
doCommonThing = function(instance) // don't use *var* so that it becomes a global
{
/* Commonly used method is defined here */
}
Template.myTemplate.helpers({
otherThing1: function() {
var _instance = this; // assign original instance *this* to local variable for later use
/* Do proprietary thing here */
doCommonThing(_instance); // call the common function, while passing in the current template instance
},
otherThing2: function() {
var _instance = this;
/* Do some other proprietary thing here */
doCommonThing(_instance);
}
});
By the way, if you notice you're constantly duplicating the same helpers across multiple templates, it might help to use Template.registerHelper instead of assigning the same function to multiple places.

jQuery Plugin public methods not working when applied to multiple elements

I posted a similar issue earlier, but it was flagged as a duplicate. However, this referenced article did not answer my question, so I'll try this again, this time using the solution of said article in my example.
The solution provided in this article creates the same issue I had before: when there is more than one element, I cannot call any of the public methods of the plugin.
Since no working example was provided, let's start with the code the article gave:
(function($){
$.fn.myPlugin = function(options) {
// support multiple elements
if (this.length > 1){
this.each(function() { $(this).myPlugin(options) });
return this;
}
// private variables
var pOne = '';
var pTwo = '';
// ...
// private methods
var foo = function() {
// do something ...
}
// ...
// public methods
this.initialize = function() {
// do something ...
return this;
};
this.bar = function() {
// do something ...
};
return this.initialize();
}
})(jQuery);
I LOVE the internal loop so that it's applied to each instance of the element, but I feel the repeated "return this" is redundant. I think if we removed every single one of them, this plugin would work exactly the same. But, for the sake of argument, I'm going to leave them in my working example.
As you can see in this jsfiddle example, it works fine when there is only one element. The public method runs fine.
However, if I were to comment the other 4 elements back in like here, it throws an error in the console: "undefined is not a function". This, of course, makes sense since I'm attempting to run the public method on a reference to all elements on not an individual element.
Well, then I use .eq(0) to run the method only on the first instance of the element here, but I get the exact same error in the console.
So, why isn't calling the public method on the individual element working? Is this a scoping issue?
Please advise. Thanks!
Ok, so I think I've answered my own question. The issue is that I'm not applying a jQuery plugin to a DOM element. I'm applying it to a jQuery element. So, if I were to apply the jQuery plugin to a jQuery element, referenced like $element or $('.element'), I can then run any public methods because the scope is the same. But, if I were to reference it in a different way, like say $parentelement.eq(0), I'm using a difference reference, one that did not get the plugin applied to it, so naturally, it would not have the defined method. I think I'm getting the concept right. Still a little shaky on this. Maybe someone else can explain it better.
Nevertheless, while the above code does technically work, public methods are not practical on a jQuery plugin. I suggest instead using a Custom Event to make the jQuery plugin do something. Like this:
(function($) {
$.fn.extend({
myTestPlugin: function() {
if (this.length > 1) {
this.each(function() { $(this).myTestPlugin(); });
}
this.done = function() {
$(this).html('Done!');
};
var alsoDone = function() {
$(this).html('Also done!');
};
this.html('Replace me!');
this.on('alsoDone', alsoDone);
}
});
})(jQuery);
Here is an example where I am using trigger to make the plugin do something on an individual element, which works, but the method still fails as expected.
I hope this helps other people with similar issues.

Protecting a Global Javascript "API" Object

I currently have a Web Application that runs off a global Javascript-based API, and it is initialized like this:
var Api = {
someVar: "test",
someFunction: function() {
return "foo";
}
}
This API is shared across many "Widgets" that live in the Web Application, and they should all run off this single Api instance so they can pass data to each other.
AJAX is currently used to load these Widgets, for example in widgets/mywidget.html, and it's placed in, say, <div id='widget_<random number>'>...</div>
Certain other parts of the code may choose to add more functionality to Api, and it's currently done like this:
Api.myExtension = {
myNewFunction: function() {
return "bar";
}
}
However, some issues arise from this kind of usage:
Problem One: What if one Widget (these may be provided by third-parties) decides to hide some code within, and does something similar to Api = {}, destroying the global Api var everything lives on, and breaking the whole Application? Is it possible to protect this Api variable from being overwritten from outside? Only "extending" is allowed (adding new things), but "removing/changing" is not allowed. i.e.:
Api.foo = { test: "bar" } // allowed
Api.someVar = "changing the existing someVar"; // not allowed
The following code is located "inside" Api, for example:
var Api = {
Debug: {
Messages = new Array,
Write: function() {
Api.Debug.Messages.push("test"); // allowed
}
}
}
Api.Debug.Messages.push("test 2"); // not allowed
Probable Solutions I've Thought Of:
Suppose we simply use frames to resolve this issue. The Apis provided are now separate from each other. However, there's additional overhead when loading Api again and again if I have many Widgets running, and they can no longer communicate with the "Host" of the widgets (the page where frames reside in), for example, I may want to tell the host to show a notification: Api.Notify.Show("Test"), but it cannot do so because this Api is completely independent from other instances, and it cannot communicate with the "Host"
Using something like a "getter" and "setter" function for the Api to be read and written. I'm unsure on how to implement this, so any help on directions on how to implement this is welcome!
A mixture of 1/2?
There's no good way to prevent having a "third party" widget overwrite the a global variable. Generally it is the responsibility of whoever is putting together the final application to ensure that whatever JavaScripts they are using aren't littering the global namespace and conflicting. The best thing you can do in that direction is give your "Api" a nice, unique name.
What I think can help you a lot is something like the "revealing pattern", which would be a way of doing the "getters and setters" you mentioned, plus more if you needed it.
A simple, useless example would be like the following:
var Api = (function () {
// private variable
var myArray = [];
return {
addItem: function (newItem) {
myArray.push(newItem);
},
printItems: function () {
console.log("lots if items");
}
};
})();
Api.addItem("Hello, world");
Api.extensionValue = 5;
I think you should make a clear delineation of what is shared, or "singleton" data, and keep those items private, as with myArray in my example.
Make it a constant:
const Api = "hi";
Api = 0;
alert(Api); //"hi"
Take a look at
Object.freeze
More info here
Here is a code example from Mozilla's page:
var obj = {
prop: function (){},
foo: "bar"
};
// New properties may be added, existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
var o = Object.freeze(obj);
assert(Object.isFrozen(obj) === true);
// Now any changes will fail
obj.foo = "quux"; // silently does nothing
obj.quaxxor = "the friendly duck"; // silently doesn't add the property
// ...and in strict mode such attempts will throw TypeErrors
function fail(){
"use strict";
obj.foo = "sparky"; // throws a TypeError
delete obj.quaxxor; // throws a TypeError
obj.sparky = "arf"; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty will also throw
Object.defineProperty(obj, "ohai", { value: 17 }); // throws a TypeError
Object.defineProperty(obj, "foo", { value: "eit" }); // throws a TypeError
However browser support is still partial
EDIT: see Kernel James's answer, it's more relevant to your question (freeze will protect the object, but not protect reassigning it. however const will) same issue with limited browser support though.
The only way (at least that I can think of) to protect your global variable is to prevent the Widgets from having a direct access to it. This can be achieved by using frames functions, as you suggested. You should create an object that contains all the functions that the Widgets should be able to use, and pass such to each Widget. For example:
var Api = {
widgetApi = {
someFunction: function(){
// ...
}
},
addWidget:function(){
var temp = this.widgetApi.constructor();
for(var key in this.widgetApi)
temp[key] = clone(this.widgetApi[key]);
return temp;
}
// Include other variables that Widgets can't use
}
This way, the Widgets could execute functions and communicate with the host or global variable Api. To set variables, the Widget would be editing its private object, rather than the global one. For every frame (that represents a Widget), you must initialize or create a copy of the widgetApi object, and probably store it inside an array, in such a way that an instance of a Widget is stored in the main Api object.
For example, given <iframe id="widget"></iframe>
You would do the following:
var widget = document.getElementById("widget");
widget.contentWindow.Api = Api.addWidget();
widget.contentWindow.parent = null;
widget.contentWindow.top = null;
Additionally, in every frame you would need to set the parent and top variables to null so that the Widgets wouldn't be able to access the data of the main frame. I haven't tested this method in a while, so there might be ways to get around setting those variables to null.

Replacement for Prototype.js Class system

We have a set of classes created that depend on Prototype's Class implementation (and some Object.extend).
The problem is prototype is creating trouble when integrating with the rest of our applications (even with "noconflict" adapters and the such).
Does anybody know of a compatible Class implementation that does not mess with the global scope? Or has anybody been able to "extract" Prototype's to use it alone?
I wrote one a couple of years back (I should go revisit it, and give it a proper name) because I didn't like Prototype's handling of calling methods on the "superclass", which involves creating a function every time an overridden method is called (yes, really). It's very similar to Prototype's except for how you make supercalls; you can readily drop it in and search for super in your code and change it up. My implementation also makes it a bit easier to use named functions rather than anonymous ones, which is useful for many reasons, not least because it helps your tools help you. It also makes private "class" methods trivial. Details below.
But you don't have to use mine. There are other options that will require slightly more work to migrate your code to, but probably not a lot more:
John Resig's simple inheritance
Dean Edwards' mechanism
My issue with both of them is that they use function decompilation (so does Prototype's Class stuff), and function decompilation (e.g., calling toString on a function) has never been standardized and does not work on some mobile browsers. Resig's mechanism continues to work if function decompilation doesn't work, but it adds overhead to every method in that case (rather than only ones that make supercalls). My mechanism doesn't use function decompilation at all, adds no overhead to method calls, and even makes supercalls highly-efficient.
If you use my mechanism and your Prototype code looks like this:
var SuperThingy = Class.create({
foo: function(arg) {
console.log("SuperThingy: " + arg);
this._pseudoPrivate();
},
_pseudoPrivate: function() {
console.log("I'm not really private.");
}
});
var Thingy = Class.create(SuperThingy, {
foo: function(super, arg) {
console.log("Thingy: " + arg);
super(arg);
}
});
You can make minimal changes:
var SuperThingy = Helper.makeClass({
foo: function(arg) {
console.log("SuperThingy: " + arg);
this._pseudoPrivate();
},
_pseudoPrivate: function() {
console.log("I'm not really private.");
}
});
var Thingy = Helper.makeClass(SuperThingy, {
foo: function(arg) {
console.log("Thingy: " + arg);
this.callSuper(arguments, arg);
}
});
...or you can make slightly larger changes and get the benefit of a speed increase (callSuper uses arguments.callee, which is slow), properly-named functions (for debugging and such), and truly private functions:
var SuperThingy = Helper.makeClass(function() {
function SuperThingy_foo(arg) {
console.log("SuperThingy: " + arg);
trulyPrivate.call(this);
}
function trulyPrivate() {
console.log("I'm truly private.");
}
return {foo: SuperThingy_foo};
});
var Thingy = Helper.makeClass(SuperThingy, function() {
function Thingy_foo(arg) {
console.log("Thingy: " + arg);
foo.$super.call(this, arg);
}
return {foo: Thingy_foo};
});
The Prototype source tries to be modular so you can download lang/class.js separately. However a quick glance at the code shows it depends on functions from lang/array.js, lang/object.js, lang/function.js - it would be safer to grab prototype.js and all of the lang directory. That gives you the core of Prototype without the conflicting DOM stuff.
I too find the Class class too useful to do without. I was recommended to try Sugar which is nice but still has no inheritence. It seems the only real alternative currently is MooTools' class.
You should have a look at :
MyJS class system
I think you will enjoy !
After trying out ComposeJS, I couldn't find a way to invoke superclass constructors explicitly (they are invoked automatically), so I had to abandon it. I finally settled on JsFace which has many features (inheritance, mixins, static properties, AOP), although a slightly weird syntax to invoke the super class constructor: this.$class.$super.call(args). It also seems to be the fastest implementation, according to its website and also this benchmark, totally blowing away the Resig implementation, which is quite slow. The caveat to watch out for is is that this.$class.$super is always the super class of the final child class, so you may need to do something like this:
var C = Class(A, {
constructor: function (x) {
C.$super.call(this, x);
},
...
}
instead of
var C = Class(A, {
constructor: function (x) {
this.$class.$super.call(this, x);
},
...
}
which is like the examples, if you have multiple levels of inheritance, otherwise you'll get an infinite recursion.

Categories