Is it possible to HIDE Javascript Object's prototype! What's the MYSTERY behind this? - oop

I'm using openui5. There is a constructor Function for UI control Button,unable to see the prototype properties of the Button but the same thing when executed in browser console, shows up!
sap.m.Button.prototype.Move = function(){
console.log('Move');
}
var oButton = new sap.m.Button({text:"Hello"});
oButton.Move(); // throws undefined function!
The same code when executed browser in console, it works!
jsbin --> http://jsbin.com/tepum/1/edit

After running the code I find that creating the first instance of sap.m.Button causes script to change the prototype of sap.m.Button. It's valid in JavaScript but not very smart if you ask me.
A first creation causes a synchronous request (no no as well) to fetch library-parameters.json.
If you run the code the second time it will have prototype.move because creating an instance of Button will not change the Button.prototype.
The capital M in Move would suggest a constructor function so I would advice changing it to lower case.
Since fetching the parameters is synchronous you can create the first instance and then set the prototype:
console.log("First Button creation changes Button.prototype");
var oButton = new sap.m.Button({text:"Hello"});
sap.m.Button.prototype.move = function(){
console.log('Move');
}
oButton.placeAt('content');
oButton.move(); // logs Move
My guess is that this is done to lazy load controls, if a Button is never created then the json config files are never loaded for these unused controls. It has a couple of drawbacks though.
You have to create an instance first before you can set the prototype.
The config files are synchronously loaded so when creating first instance of many controls with a slow connection would cause the app to be unresponsive.
A better way would be for a factory function to return a promise so you create the control the same way every time and the config files can be fetched asynchronously.
[update]
Looking at the config it seems to be config for the whole gui library so I can't see any reason why this is loaded only after creating a first instance. A library that changes it's object definitions when creating instances is not very easy to extend because it's unpredictable. If it only changes prototype on first creation then it should be fine but it looks like the makers of the library didn't want people to extend it or they would not make the object definition unpredictable. If there is an api documentation available then maybe try to check that.
[update]
It seems the "correct" way to extend controls is to use extend.

#HMR is right the correct way to extend a control is by using the extend function provided by UI5 managed objects, see http://jsbin.com/linob/1/edit
in the example below when debugging as mentoned by others you will notice that the control is lazy loaded when required, any changes you make prior are lost when loaded
jQuery.sap.declare("my.Button");
jQuery.sap.require("sap.m.Button");
sap.m.Button.extend("my.Button", {
renderer: {}
});
my.Button.prototype.Move = function() {
console.log('Move');
};
var oButton = new my.Button({
text: "Hello"
});
oButton.placeAt('content');
oButton.Move();

It's not hiding the prototype per se. If a constructor function exits normally then you get that function's prototype. But, if a constructor function actually returns some other object then you get that other object's prototype, so it's not valid to assume that just because you added to the Button prototype that when you call new Button() that you will see your method on whatever you get back. I'm sure if you de-obfuscate that code you'll find that the constructor you are calling has a "return new SomeOtherInstanceOfButton()" or similar at the end of it.
Edit: Ok it's a bit difficult to see what's really going on in that sap code but, it looks like they have code that overwrites the prototypes of controls to add features to them, such as: sap.ui.core.EnabledPropagator, and those things aren't run until you actually instantiate a button. So if you change your code to instantiate the button on the page, then add to it's prototype, then construct and call the method, it works fine. Like so:
http://jsbin.com/benajuko/2/edit
So I guess my answer is, when you run it from console it's finished mucking around with that prototype, whereas in your test you were adding to the prototype, then constructing the button for the first time (which changes the prototype again) then trying to call your old one, which is no longer there.

Related

see the list of event listeners currently attached

I want to check the list of event listeners that are added. For example, I used the code cy.on('pan zoom resize', update); and added function called update in for loop. I do this many times. I also call cy.off('pan zoom resize', update); to remove the event listeners but I want to be sure about it.
The only think I can think of is using console.log but this method might not be helpful.
I also think that in some places people forgot to remove the event listeners and just always added. With too many repetitions this might cause problems.
There is a data field in the private cytoscape object called listeners. You can see that if you:
console.log() the cy object,
navigate to _private,
then open the emitter object
and lastly go to listeners
This is the array listing all the default and user defined event listeners with some metadata like the event, type and scope of the listener.
You can access this in your code by simply calling
cy.emitter().listeners
The question now is, why do you need this information in the first place? Normally, you should be just fine if you call cy.off('eventXY', ...) before using any cy.on('eventXY', ...). Are you sure you need this for your application to work? Maybe elaborate more on the core problem (why you want these information in the first place).
Thanks and have a great day!

Override a method in dojo - dojo.store.Memory

Is there a way how to run my own function before a dojo method is spawned?
Specifically I need to refresh data in dojo.store.Memory before query() function is spawned. My idea is to put there a callback (that will be spawned before query()), fetch new data from server and then set the data to Memory instance. Then just call
this.inherited(arguments)
I've tried override query method with declare, but I'm still getting some unrelated errors. 4 hours but no luck...
Is there a another way?
Thanks
Yes, you can fire callbacks before, after or around any method. Just use dojo/aspect
Something like this should work :
require(["dojo/store/Memory", "dojo/aspect"], function(Memory, aspect){
aspect.before(Memory, "query", function(){
// do something
});
});
However, for your specific use case, if I understood correctly, what you want is to have a store linked to a server-side controller. In that case, you should use dojo/store/JsonRest rather than dojo/store/Memory. No need to fire any methods before the query...

Triggering Backbone model events don't register in my Jasmine spies

I am trying to test that a view method gets called when my model triggers an event. But this isn't working - and I have run out of ideas why this would be. Here's the code that isn't working:
View:
class View extends Backbone.View
initialize: ->
#.listenTo #model, 'request', #disableForm, #
disableForm: ->
console.log 'disableForm'
Jasmine Test:
describe "AJAX events", ->
it "when starting an AJAX request, disable the form", ->
model = new Backbone.Model()
view = new Backbone.View( { model: model })
view.render()
spyOn(view, 'disableForm')
view.delegateEvents()
model.trigger 'request'
expect(view.disableForm).toHaveBeenCalled()
This code works in the browser fine.
Also - the console.log does print 'disableForm' when I run the tests - so the model event is triggering the call to disableForm, but my spy is not picking this up (my expectation fails). I have tried putting it into a waitsFor method, but his too did not make a difference.
Any ideas where I am going wrong?
The problem is that spyOn will replace the function disableForm in your view with a spy function. But at this time the model was bound to the original function, so replacing the function in the view has no effect on the function that was bound to the event listener. When you trigger the event on the model the original function will be called and not the spy.
Might be a concurrency issue. Rather than using an integration test, it might be easier to unit test the disableForm method directly and then test that the initialize makes the correct binding (or better yet, use the Backbone.View.events hash).
Also, the call to view.delegrateEvents() appears to be unnecessary in your test. This is due to the fact that Backbone will call delegateEvents by default on view instantiation and it only interacts with the events hash, which does not appear to be used.
The answers above correctly clarify why this is happening: the function is bound on initialization, the spy replaces the function after binding so the original function is called when the event is triggered.
A solution less cumbersome than replacing the prototype's function on test setup (before initialization) is to bind an anonymous function to the event and call the view's function inside of it.
#listenTo(model, event, => #viewFunction())
I'm not sure what the performance tradeoffs are though.

Win8 JS App: How can one prevent backward navigation? Can't set WinJS.Navigation.canGoBack

Fairly new to developing for Windows 8, I'm working on an app that has a rather flat model. I have looked and looked, but can't seem to find a clear answer on how to set a WinJS page to prevent backward navigation. I have tried digging into the API, but it doesn't say anything on the matter.
The code I'm attempting to use is
WinJS.Navigation.canGoBack = false;
No luck, it keeps complaining about the property being read only, however, there are no setter methods to change it.
Thanks ahead of time,
~Sean
canGoBack does only have a getter (defined in base.js), and it reflects the absence or presence of the backstack; namely nav.history.backstack.
The appearance of the button itself is controlled by the disabled attribute on the associated button DOM object, which in turn is part of a CSS selector controlling visibility. So if you do tinker with the display of the Back button yourself be aware that the navigation plumbing is doing the same.
Setting the backstack explicitly is possible; there's a sample the Navigation and Navigation History Sample that includes restoring a history as well as preventing navigation using beforenavigate, with the following code:
// in ready
WinJS.Navigation.addEventListener("beforenavigate", this.beforenavigate);
//
beforenavigate: function (eventObject) {
// This function gives you a chance to veto navigation. This demonstrates that capability
if (this.shouldPreventNavigation) {
WinJS.log && WinJS.log("Navigation to " + eventObject.detail.location + " was prevented", "sample", "status");
eventObject.preventDefault();
}
},
You can't change canGoBack, but you can disable the button to hide it and free the history stack.
// disabling and hiding backbutton
document.querySelector(".win-backbutton").disabled = true;
// freeing navigation stack
WinJS.Navigation.history.backStack = [];
This will prevent going backward and still allow going forward.
So lots of searching and attempting different methods of disabling the Back Button, finally found a decent solution. It has been adapted from another stackoverflow question.
Original algorithm: How to Get Element By Class in JavaScript?
MY SOLUTION
At the beginning of a fragment page, right as the page definition starts declaring the ready: function, I used an adapted version of the above algorithm and used the resulting element selection to set the disabled attribute.
// Retrieve Generated Back Button
var elems = document.getElementsByTagName('*'), i;
for (i in elems)
{
if((" "+elems[i].className+" ").indexOf("win-backbutton") > -1)
{
var d = elems[i];
}
}
// Disable the back button
d.setAttribute("disabled", "disabled");
The code gets all elements from the page's DOM and filters it for the generated back button. When the proper element is found, it is assigned to a variable and from there we can set the disabled property.
I couldn't find a lot of documentation on working around the default navigation in a WinJS Navigation app, so here are some methods that failed (for reference purposes):
Getting the element by class and setting | May have failed from doing it wrong, as I have little experience with HTML and javascript.
Using the above method, but setting the attribute within the for loop breaks the app and causes it to freeze for unknown reasons.
Setting the attribute in the default.js before the navigation is finished. | The javascript calls would fail to recognize either methods called or DOM elements, presumably due to initialization state of the page.
There were a few others, but I think there must be a better way to go about retrieving the element after a page loads. If anyone can enlighten me, I would be most grateful.
~Sean R.

Existing widgets not available in a widget template in some (strange) cases

This took me a while. A long while. I battled two problems at once (circular dependencies, fixed with refactoring, and this problem). To get this problem into a JSFiddle required a LOT of work... but I think it was worth it.
So:
http://jsfiddle.net/EVbTL/3/
I define three widgets:
r.AppMainScreen -- This is the main app's widget. Easy: just a bunch of tabs, and a button which contains a simple button, which goes:
// SUbmit form
this.form.onSubmit = function(e){
e.preventDefault();
console.log("HERE");
dialog = new r.RetypePasswordDialog();
dialog.show();
return false;
}
Pretty uninteresting.
r.RetypePasswordDialog() -- A templated widget which represents a dialog box. The only interesting thing about it is:
< input name="password" id="${id}_password" data-dojo-attach-point="password" data-dojo-type="app.ValidationPassword" />
It's a simple custom widget, defined in this very file, which does validation. NOTE: I know there is no point in having a subclass here for this little work. Please keep in mind that this is an example.
r.ValidationPassword()
An augmented ValidationTextBox with some extra validation.
If you click on the button, you get:
Uncaught Error: Could not load class 'app.ValidationPassword
...?!? app.ValidationPassword has definitely been defined. It ought to be available there. At the beginning, I thought it was because of aa circular dependency (it was very fun, yesterday: I had to learn about AMD circular dependencies WHILE trying to figure out this problem...)
If you uncomment this line, executed within the script:
TEST = new r.RetypePasswordDialog();
The whole thing works. It's a meaningless instance, and I cannot figure out why on earth this would or should make a difference.
Explanations most welcome... I couldn't find any!
Thank you,
Merc.
app = new r.AppMainScreen( {});
You redefine the global app variable here, but are trying to use it elsewhere as the base object for your type system. Use var to scope variables to the function.