Using Promises in AngularJS Views - view

Mark Dalgleish wrote a nice little article about how to use promises in AngularJS views.
Some people asked questions about this in the comments, but Mark didn't answer them (yet). Because I'm asking me the same question, I will ask on StackOverflow instead to get an answer:
If you use promises in views, how do I handle "loading"/"waiting" indication, because they are async? Does a promise have something like a "resolved" or "withinRequest" property?
How do I handle errors? Normally they would arise in the second callback, but if I use a promise directly in the view I don't handle this case. Is there another way?
Thank you.

EDIT: as of angular v1.2 the resolution of promise in views is not activated by default.
The automatic resolution of promises in a view looks like a handy tool at first but it has number of limitations that need to be understood and evaluated carefully. The biggest issue with this approach is that it is AngularJS who will add callbacks to a promise and we've got little control over it.
Answering your questions:
1) As indicated, it is ultimately AngularJS who will add a success / error callbacks so we don't have much control here. What you could do is to wrap the original promise into a custom one that would track resolution. But this kind of deft the whole purpose of saving few keystrokes. And no, there is no things like 'resolved'. In short - there is no universal mechanism for tracking progress that would work for all promises. If your promises are $http-based you might use interceptors or pendingRequests property to track request in progress.
2) You can't. Once again, it is AngularJS that adds a handler inside the $parse service and it looks like this: promise.then(function(val) { promise.$$v = val; }); (see code here). You can see that only a success callback are added so all the failures are going to be silently ignored.
Those are not the only limitations of the automatic promise resolution in the view. The other problem is that promises returned by a function won't be resolved correctly. For example, if you would rewrite an example like so:
myModule.controller('HelloCtrl', function($scope, HelloWorld) {
$scope.messages = function() {
return HelloWorld.getMessages();
}
});
and try to use the following markup:
<li ng-repeat="message in messages()"></li>
things would work as expected, which might come as a surprise.
In short: while the automatic resolution of promises might seem like a handy shortcut it has number of limitations and non-obvious behaviors. Evaluate those carefully and decide if saving few keystrokes are worth it.

Related

Wait or know when is load the assets of entity completely

I would like to be able to identify when the object is already fully loaded in the engine
const box = new Entity()
box.getComponent(Transform).position.set(3, 1, 3)
var model=new GLTFShape()
box.addComponent(model)
engine.addEntity(box)
I mean something like this:
model.OnLoaded(()=>{/*The model load in Cache*/})
or
await engine.addEntity(box)
or
engine.addEntity(box,()=>{/*charging is complete*/})
I can't find a way to do this
any suggestions other than waiting without knowing what happens?
Unfortunately, as it stands now, there is no real concrete way to know when an asset is loaded. This feature (onLoading, onLoadComplete, ect) may be included in a future update. I believe it's on the roadmap. In the meantime, you may want to put a delay of some kind with either a setInterval or otherwise and set it to a few seconds, and then call whatever code you want.
There are workarounds, but they are crazy and I wouldn't recommend it, for your own sanity sake.

What is the best place to put onEnter, onExit callback functionality?

I am creating my first project that uses ui-router.
My project has about 10 views, each with their own controller and state. I am trying to modularise/encapsulate/decouple as best as possible but I am having trouble working out where to put the onExit and onEnter state callbacks.
The first option is to put it in app.js which is currently defining all of my states, however I feel that this would not be a good place as it could cause this file to blow up and become hard to read as more states are introduced and the logic gets more complex.
The second option I looked into was to put it into a controller (I have one for each state), however from researching it doesn't seem to be best practice to do this.
The third option is to create a service that is resolved, however with this option I would end up with either a giant service full of state change functions for each of the states (not decoupled) or an additional service per state which would contain the state change functionality, and I worry that would increase project complexity.
What is the standard way to achieve this?
Are there any other options that I am missing?
Our strategy for this has been to disregard the onEnter and onExit on the state object, because as you are discovering, they feel like they are in the wrong place in terms of separation of concerns (app.js).
For onEnter: we handle setup in an activate() function in each controller, which we manually execute inside the controller. This happens to also match the callback that will get executed in Angular 2.0, which was not an accident ;).
function activate() {
// your setup code here
}
// execute it. this line can be removed in Angular 2.0
activate();
For onExit: We rarely need an exit callback, but when we do, we listen for the $scope $destroy event.
$scope.$on("$destroy", function() {
if (timer) {
$timeout.cancel(timer);
}
});

Acting on an element in onRender doesn't work

When I try to act on some HTML elements in the onRender method (or in a item:rendered callback), it fails.
Example:
Bars.EditGallery = Backbone.Marionette.ItemView.extend
template: 'bars/edit_gallery'
className: 'edit-gallery'
onRender: ->
# If I just write #$('select').chosen(), it doesn't work
# despite the jQuery object contains what I expect.
# To get it working, I have to write:
callback = -> #$('select').chosen()
setTimeout(callback, 0)
It's the same with others actions, like giving the focus to a field.
How do you deal with that? The trick with setTimeout works but it is not very elegant.
I've seen this happen when the templates used for rendering are loaded asynchronously. I thought I a pull request had fixed this in a recent release. What version of Marionette are you using?
But it looks like you're using JST, anyways, so that shouldn't be the problem. Is there anything else in your setup that is causing the render to happen asynchronously?
It seems likely that there is some asynchronous issue happening, though. Since using setTimeout fixes the problem, that makes me think the rendering is not completing before the onRender method is called.
Also - it can be hard tell if the jQuery selector is actually returning the object you want, right away. If you're using console.log to check the selector, this may be giving false results. console.log is itself asynchronous (in most browsers, anyways... not sure about all) which means the request to log the item gets queued up at the end of the event loop. It's likely that the DOM element is available by the time the logging happens.
FWIW: I use onRender for this exact purpose on a regular basis, and I've never had to use setTimeout to make it work. So my assumption is something funny going on with the rendering process, related to async stuff.
This problem is caused by Chosen, as mentioned in this issue.

Webtrends Analytics implementation - Using variables in an async tracking call/pass variable as value

Does anyone here have experience doing a Webtrends implementation? According to their documentation, their asynchronous event tracking call is made by sending key-value string pairs into their tracking method, like this:
dcsMultiTrack('DCS.dcsuri', 'page.html', 'WT.ti', 'NameOfPage');
However, that model does not lend well to supporting dynamic data. What I would like to do is something like this, so that I can dynamically create the key-value pairs based on the user interaction I am capturing:
var wtString = "'DCS.dcsuri', 'page.html', 'WT.ti', 'NameOfPage'";
dcsMultiTrack(wtString);
In my proof of concept, though, that does not work. The actual webtrends JS mangles the data and the call is not made. (Sifting through their code, it looks like something breaks when assigning the arguments to the Webtrends object. Anyway, I can't edit their code because then they won't support it, so I stopped investigating that end of things.)
So the question is, how can I pass the JS variable as its value? I've done a lot of searching and tried things that I thought would both work and not work: String(), .toString(), .value(), closures, and even the dreaded eval(), but to no avail.
Any help would be MUCH appreciated. I'm at my wits end with this one.
It looks like JavaScript's apply function could help here:
var wtArguments = ['DCS.dcsuri', 'page.html', 'WT.ti', 'NameOfPage'];
dcsMultiTrack.apply(this, wtArguments);
This is effectively the same as calling:
dcsMultiTrack('DCS.dcsuri', 'page.html', 'WT.ti', 'NameOfPage');

Browser history for Flash (or AJAX)

What is the best tool / practice to enable browser history for Flash (or AJAX) websites?
I guess the established practice is to set and read a hash-addition to the URL like
http://example.com/#id=1
I am aware of the Flex History Manager, but was wondering if there are any good alternatives to consider. Would also be interested in a general AJAX solution or best practice.
SWFAddress has been widely used and tested. It makes it almost trivial (given you plan ahead) to handle deeplinking in Flash. It provides a JS and AS library that work together and make the whole process pretty foolproof. You'd want to look at something like RSH for AJAX.
I've used swfadress for some small stuff.
For AJAX, something like Really Simple History is great.
This will seem a bit roundabout, but I'm currently using the dojo framework for that. There's a dojo.back that was very useful when my UI was mostly JS/HTML. Now that I've gone to flex for more power, fluid animations, and browser stability, the only thing I've need to keep using has been the back URL.
FlexBuilder seemed to have it's own browser history in default projects.
Also, the Flex 3 Cookbook has a recipe for using mx.managers.HistoryManager to create your own custom history management. I have plans to give this a try someday to remove our dependence on the dojo.back, but haven't had time yet.
I've rolled my own solutions that were ultra-simple like this:
(function() {
var oldHash, newHash;
function checkHash() {
// Grab the hash
newHash = document.location.hash;
// Check to see if it changed
if (oldHash != newHash) {
// Trigger a custom event if it changed,
// passing the old and new values as
// metadata on the event.
$(document).trigger('hash.changed', {
old: oldHash,
new: newHash
});
// Update the oldHash for the next check
oldHash = newHash;
}
}
// Poll the hash every 10 milliseconds.
// You might need to alter this time based
// on performance
window.setInterval(checkHash, 10);
})(jQuery);
Then you just need to have event handlers for the 'hash.changed' event to respond accordingly based on what the new value is. The approach works will in super simple cases.

Resources