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...
Related
I am getting some strange behavior in my Vue application that I am developing.
In my view I define my data initially:
...
data() {
return {
organization: {
selectedOption: null,
options: [],
},
};
},
...
Intention is to populate this via a call to my backend API, which I do using => notation via axios:
// The following snippet is in my methods:
...
axios.get('http://localhost:8000/api/org/types')
.then((response) => {
Object.keys(response.data).forEach((k) => {
this.organization.options.push({
value: k,
text: response.data[k],
});
});
this.organization.selectedOption = this.organization.options[0].value;
});
...
The data comes in, and I can see it indeed does set the values until I go elsewhere within the view.
I initially called the method above in the beforeMount() method however I moved it to the created() method (due to data context/reactivity matters) and all seemed to be working just fine.
Now I am having an issue where when accessing the data where it is always seemingly set to initial data I have defined. I am verifying this via debug/console.
Within mounted():
console.log(this.organization); // Returns observer that shows the data I would expect to be there via Console, but initial data when accessing anything.
console.log(this.organization.selectedOption); // Returns null
Is there something I am not understanding how the Vue data method works? I was under the assumption that after the context has been created the underlying data can then be mutated for the life-cycle of that view.
EDIT:
I did attempt to return the promise on the axios call, but to no avail.
There are a couple of keys things to note here.
Firstly, when you log an object to the console it is live. You'll probably see a little blue 'i' icon after you expand the object that explains this. What this means is that the object properties are not copied. Instead the console just has a reference to the object. It only grabs the property values when you click on the object in the console to expand it. You can work around this by logging out console.log(JSON.stringify(this.organization)) instead.
The second point to note is that it really doesn't matter which hook you use to load the data. The hooks beforeCreate, created, beforeMount and mounted will all run synchronously at the relevant stages. Meanwhile, your data is being loaded asynchronously. Vue won't wait for it, there's no support for that. No matter which hook you use the data won't be loaded until after the initial rendering/mounting is complete. This is a common problem and you just need to write your component in such a way that it can cope with the data being missing when it first renders.
To be clear, I'm not saying that the hooks are interchangeable in general. They most definitely aren't. It's just that when you're loading data using an AJAX request it doesn't make any real difference which you use. The AJAX request will always come back after all of those hooks have been run. So performing the request in an earlier hook won't make the data available in the later hooks.
A common alternative is to load data in a parent component and only create the child once the data is loaded. That's usually implemented using a v-if and the data is then passed using a prop. In that scenario the child doesn't have to deal with the data being missing.
When I provide loadOptions to an Async control it loads options on mount.
If I pass autoload={false} then it doesn't load options neither on mount nor on open. But it loads options on the first close (or type, or blur).
If I pass onCloseResetsInput={false} then it doesn't load options until I type something. (showing "Type to search" in the menu)
Async provides onOpen handler, but I didn't find the way to use it in this situation. (and react-select#2.0.0-alpha.2 doesn't have it)
So the user needs to type a character, then delete it, to see the full list of options.
How can this be avoided?
Example sandbox: https://codesandbox.io/s/mjkmowr91j
Solution demo: https://codesandbox.io/s/o51yw14l59
I used the Async options loaded externally section from the react-select repo.
We start by loading the options on the Select's onFocus and also set the state to isLoading: true. When we receive the options we save them in the state and render them in the options.
I also keep track of optionsLoaded so that only on the first focus do we trigger the call to get options.
In our use case, we have several of these select inputs on a single page, all async, so the requests to the server will pile up, and are completely unnecessary in a lot of cases (users won't even bother clicking).
I found a workaround for this issue that'll work for my use case on 2.0.0-beta.6:
Include defaultOptions
Add 2 members to your class that will store the resolve/reject methods for the promise.
In your loadOptions function, check if the input is '', if so, create a new promise, and store the values of resolve/reject within your class members, and return that promise. Otherwise, just return the promise normally to get your results.
Add an onFocus handler, and within it call the function to get your results, but also add .then and .catch callbacks passing the resolve and reject functions you stored previously.
Essentially, this makes react-select think you're working on getting the results with a long-running promise, but you don't actually even try to load the values until the field is selected.
I'm not 100% positive there aren't any negative side effects as I just wrote this, but it seems like a good place to start.
Hope this helps someone. I may submit a feature request for this.
In order to load options when user focus first time, set defaultOptions={true}
Thanks, Alexei Darmin for the solution, I was struggling with this... while testing it I converted the solution to a react functional component and added real API fetching.
Here is a working demo, I hope it helps someone
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.
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.
I have seen numerous advice on stackexchange and all over the web suggesting that I not use jquery's live function. And at this point, it is deprecated, so I'd like to get rid of it. Also I am trying to keep my javascript in one place(unobtrusive)--so I'm not putting it at the bottom of the page. I am unclear though on how to rewrite the code to avoid live for elements that don't yet exist on the page.
Within the javascript file for my site I have something like this:
$(function() {
$('button.test').live('click', function(){
alert('test');
});
});
.on( doesn't work since the element doesn't exist yet.
The button is in a page I load in a colorbox pop-up modal window. I'm not sure exactly where that colorbox window sits in the DOM but I think it is near the top.
I could use delegate and attach this to the document--but isn't the whole point of not using live to avoid this?
What is the best way to get rid of live in this case?
You can use .on() - http://api.jquery.com/on/
$(document).on("click", "button.test", function() {
alert('test');
});
If you use live() you can use die().
You can also use on() and off().
They do about the same thing but its recomended to use on.
I ended up avoiding both live and an on attached at the document level. Here's how:
Wrap all of the jquery code specific to objects in a page which loads in the colorbox window in the function like so:
function cboxready(){
...
}
The code wrapped in this function can attach directly to the objects (instead of attaching at the document level) since it will only get run once the colorbox window is open.
Then just call this function using colorbox's callback when you attach the colorbox, like so:
$('a.cbox').colorbox({
onComplete:function(){ cboxready(); }
});