In as3, I am adding event listener and then attaching the anonymous function to it:
myBox.addEventListener(MouseEvent.ROLL_OVER,
function(e:MouseEvent):void
{
Alert.show(count, 'Alert Box');
);
Now this whole piece of code is looped n times. Now, I have n myBoxes and whenever I roll my mouse over the box, it should alert the name. But, what I see is that the last value of count is used by each box.
How can I pass parameter or value to the anonymous function? ( as roll over , I believe, expects only one variable)
You need to create a new scope by executing a function:
for (var i:int = 0; i<10; i++)
{
(function(count:int):void
{
myBox.addEventListener(MouseEvent.ROLL_OVER,
function(e:MouseEvent):void { Alert.show(count, 'Alert Box'); });
})(i);
}
Rather than relying on an index, wouldn't it be simpler (and better) to get the currentTarget of the event and get the value of one of its members?
myBox.addEventListener(MouseEvent.ROLL_OVER,
function(e:MouseEvent):void
{
Alert.show(UIComponent(e.currentTarget).name, 'Alert Box');
);
If you absolutely have to reference the index, you could get that by
UIComponent(e.currentTarget).parent.getChildIndex(e.currentTarget)
And, now that I think of it, you don't even have to make this an anonymous function at all if you use the event model.
Related
I'm using ngrx/component-store and loving it so far. Having prior store knowledge building my own simple ones, the only real headache I've had so far is when I've had to update an array and figured out I have to always create a new one for the internal compare() pipe to realize the array got updated.
Anyway, reading through the documentation it talks about updater methods and patchState. To me they do exactly the same thing, but their creation is slightly different. You would call patchState inside of a method while this.updater() returns a method giving you a function you can expose in your service. Anytime I'm updating my state it's always after a network call. I assume there are plenty of scenarios where you'd want to update your state without a network call so this is why you would want to have an updater available to your component to call. The question is if an updater and patchState are really doing the same thing then is it a better practice to call an updater in an effect or use patchState, or maybe am I putting too much logic in my effect?
On a side note, the docs say an updater method is supposed to be a pure function. If you're using it to your push an object onto an array then is it really pure?
// adding the selectors so people know what components are subscribing to
readonly approvals$ = this.select(state => state.requestApprovals);
readonly registration$ = this.select(state => state);
readonly updateAssessment = this.effect(($judgement: Observable<{id: string, isApproved: boolean}>) => {
return $judgement.pipe(
switchMap((evaluation) => {
const state = this.get();
return this.requestApproval.patch(state.id, state.companyName, evaluation.id, evaluation.isApproved).pipe(
tapResponse(
(result) => {
// is it better to call patchState()?
this.patchState((state) => {
for(let i = 0; i < state.requestApprovals.length; i++) {
if(state.requestApprovals[i].id == result.id) {
state.requestApprovals[i].isApproved = result.isApproved;
}
}
// the take away is you must assign a whole new array object when you update it.
state.requestApprovals = Object.assign([], state.requestApprovals);
return state;
});
// or this updater?
// this.applyDecisionPatch(evaluation);
},
// oh look! another updater reassigning my array to the state so
// it propagates to subscribers to reset the UI
() => { this.reverseDecision(); }
)
);
})
);
});
// this is private to make sure this can only be called after a network request
private readonly applyDecisionPatch = this.updater((state, value: {id: string, isApproved: boolean}) => {
for(let i = 0; i < state.requestApprovals.length; i++) {
if(state.requestApprovals[i].id == value.id) {
state.requestApprovals[i].isApproved = value.isApproved;
}
}
state.requestApprovals = Object.assign([], state.requestApprovals);
return state;
});
Note: There's no tag for ngrx-component-store so couldn't tag it.
An updater can be compared to a reducer.
All the options to modify the state should change it in an immutable way.
A library like ngrx-immer can be used to make this easier.
The main difference is that updater receives the current state, and you can change the state based on it. E.g. a conditional update, or can be used with #ngrx/entity
While with setState and patchState, you just set state properties.
setState updates the whole state object, whereas patchState only sets the given properties and doesn't touch the rest of the state object.
These two methods are also easier to use when you just want to set the state, because you don't have to create an updater function.
To answer the side question, push is not immutable. Instead of creating a new instance, it updates the array instance.
I have an InterstitialAd QML object (QtQuick 2, QT 5.13) with onClosed event that is triggered with the interstitial ad is closed. I tried to show the interstitial ad before starting new game with the following QML code:
InterstitialAd {
id: iAd
property variant handlerFunc
onClosed: {
if (handlerFunc) {
handlerFunc
handlerFunc = null
}
}
}
function resetGameWithAd()
{
iAd.handlerFunc = Qt.binding(function() {
console.log("AdTest: calling resetGame()")
scene.resetGame()
})
console.log("AdTest: calling iAd.show()")
iAd.show()
}
where I tried to assign handlerFunc to a function that restarts the game when onClosed event is triggered, but got an effect that I did not expect. The console output of my app is:
qml: AdTest: calling resetGame()
qml: AdTest: calling iAd.show()
so obviously assigning handlerFunc to Qt.binding... actually calls the function (because resetGame is printed first), but I expected that it does only assignment. The similar technique is demonstrated here, with ':' but not with assignment.
What is wrong and what is the right way to implement this?
I also tried a code like this:
function resetGameHandler(params)
{
iAd.closed.connect(function() {
scene.resetGame(params)
iAd.closed.disconnect(/*...???...*/)
})
iAd.show();
}
but with no success because I can't disconnect it, without having a reference to the implicitly created function (as far as I see it means that I need a regular function with a name).
I haven't done any QML for some months, so I might be wrong. But if my memory is any good, this might help you.
To stay close to your approach:
variant is deprecated. Using var instead is recommended.
You won't need Qt.binding(). You can directly assign a function to that property.
Call the function in the property.
InterstitialAd {
id: iAd
property var handlerFunc <-- Use var instead of variant
onClosed: {
if (handlerFunc && typeof handlerFunc === "function") {
handlerFunc() <-- Call it!
handlerFunc = null
}
}
}
iAd.handlerFunc = function() { // doSomething cool here }
Alternatively you might be able to produce the same results with Binding and Connection-Objects in a more declarative way, but the right choice depends on what shall be done onClosed
I have a Meteor Helper that does a GET request and am supposed to get response back and pass it back to the Template, but its now showing up the front end. When I log it to console, it shows the value corerctly, for the life of mine I can't get this to output to the actual template.
Here is my helper:
UI.registerHelper('getDistance', function(formatted_address) {
HTTP.call( 'GET', 'https://maps.googleapis.com/maps/api/distancematrix/json? units=imperial&origins=Washington,DC&destinations='+formatted_address+'&key=MYKEY', {}, function( error, response ) {
if ( error ) {
console.log( error );
} else {
var distanceMiles = response.data.rows[0].elements[0].distance.text;
console.log(response.data.rows[0].elements[0].distance.text);
return distanceMiles;
}
});
});
In my template I pass have the following:
{{getDistance formatted_address}}
Again, this works fine and shows exactly what I need in the console, but not in the template.
Any ideas what I'm doing wrong?
I posted an article on TMC recently that you may find useful for such a pattern. In that article the problem involves executing an expensive function for each item in a list. As others have pointed out, doing asynchronous calls in a helper is not good practice.
In your case, make a local collection called Distances. If you wish, you can use your document _id to align it with your collection.
const Distances = new Mongo.collection(); // only declare this on the client
Then setup a function that either lazily computes the distance or returns it immediately if it's already been computed:
function lazyDistance(formatted_address){
let doc = Distances.findOne({ formatted_address: formatted_address });
if ( doc ){
return doc.distanceMiles;
} else {
let url = 'https://maps.googleapis.com/maps/api/distancematrix/json';
url += '?units=imperial&origins=Washington,DC&key=MYKEY&destinations=';
url += formatted_address;
HTTP.call('GET',url,{},(error,response )=>{
if ( error ) {
console.log( error );
} else {
Distances.insert({
formatted_address: formatted_address,
distanceMiles: response.data.rows[0].elements[0].distance.text
});
}
});
}
});
Now you can have a helper that just returns a cached value from that local collection:
UI.registerHelper('getDistance',formatted_address=>{
return lazyDistance(formatted_address);
});
You could also do this based on an _id instead of an address string of course. There's a tacit assumption above that formatted_address is unique.
It's Meteor's reactivity that really makes this work. The first time the helper is called the distance will be null but as it gets computed asynchronously the helper will automagically update the value.
best practice is not to do an async call in a helper. think of the #each and the helper as a way for the view to simply show the results of a prior calculation, not to get started on doing the calculation. remember that a helper might be called multiple times for a single item.
instead, in the onCreated() of your template, start the work of getting the data you need and doing your calculations. store those results in a reactive var, or reactive array. then your helper should do nothing more than look up the previously calculated results. further, should that helper be called more times than you expect, you don't have to worry about all those additional async calls being made.
The result does not show up because HTTP.call is an async function.
Use a reactiveVar in your case.
Depending on how is the formated_address param updated you can trigger the getDistance with a tracker autorun.
Regs
Yann
In WinJS the only way to get the count of items in a ListView object is with the method getCount().
But this method is asynchronous.
This make it very difficult to be used in a for loop for example when there is a need to loop through the items of the list.
var listView = document.getElementById("listView").winControl;
listView.itemDataSource.getCount().done(
function (numItems) {
for (var i = 0; i < numItems; i++) {
//do your stuff here
}
});
If I put this in any part of my code I can't return the value I read in the loop from any function because the getCount() return a promise, making my function also return a promise and so on...
So my question is why? Isn't the number of items in a list already known when the method is called?
Have you tried joining promises? If your concern is to iterate all of the items in a ListView by selecting each item by index and then performing some work on them, you can use WinJS.Promise.join to create a single promise that contains the results of all the operations.
For example:
var listView = document.getElementById("listView").winControl;
listView.itemDataSource.getCount().then(
function (numItems) {
var joinedPromises = [];
for (var i = 0; i < numItems; i++) {
joinedPromises.push(listView.itemDataSource.itemsFromIndex(i, 0, 0));
}
return WinJS.Promises.join(joinedPromises);
}).done(
function (results) {
// Operate on each item in the ListView's data source.
},
function (err) {
// Handle any errors from the joined promises.
});
The ListView's data contract allows for asynchronous data sources, and we include a base class VirtualizedDataSource that you can use for fancy scenarios like that. If you are using a WinJS.Binding.List as your data source that API is in fact synchronous and you should be able to say:
listView.itemDataSource.list.length
However, if you're writing generic code that deals with ListView's and doesn't know what kind of data source it will
Is there a way to get a callback for when an entire list renders?
I've tried
Template.articles.rendered = function() {
var lastChapter = Chapters.findOne({}, {
sort: {
createdTime: -1
}
})
if (lastChapter._id != this.data._id)
return
doSomething()
};
But this is unreliable because chapters are added 1 by 1 instead of all at once, so this actually fires multiple times.
Thanks.
rendered is called when a part of the template is re-rendered, so you should check inside your rendered method whether you want to do anything now. When does "the entire list renders" happen? You know that in your code, for instance by checking if the list is of an expected length yet.