External promises don't trigger Angular2 change detection (UPDATE: they do) - javascript

I'm working on an electron application, whose client side is written in Angular2.
I'm having the classic problem that a lot of people have encountered (eg. here and here), namely that I'm updating my component's data, but the view doesn't get updated because Angular doesn't know that the data has changed. The solutions people suggest (see above and also here) is to manually run change detection, either on the whole component tree, or part of it (similar to Angular 1's $rootScope.$digest and $scope.$digest).
However, I'd like to avoid wrapping all my data changes in zone.run() calls, as it slightly defeats the purpose of using Angular.
I'm pretty sure I know why this happens: I'm creating Bluebird Promises outside of Angular, in electron code (ie, the main process), and these aren't Zone aware, so they don't notify Angular of the changes.
I don't know how to solve it, though. What can I do to create Zone aware promises in my electron code, to avoid having to manually run change detection all the time? Can I convert my Bluebird Promises to Zone aware Promises somehow?
EDIT: I think I was wrong, Bluebird promises aren't Zone aware even if created within angular. They're just not Zone aware in general.
EDIT 2: I was completely wrong in the previous edit. Bluebird promises work just fine with zones. However, in the context of an electron application, creating a promise in the main electron process and using it in the renderer process (where Angular lives), doesn't work, as the returned promise isn't zone-aware. Creating the promise from Angular code worked.

Promises don't make Angular run change detection with ChangeDetectionStrategy.OnPush except when you use the async pipe ... | async.
When code is initialized outside Angular2 it runs outside Angulars zone. What you can do is to move initialization inside your Angular2 code or use ´zone.run(...)to move execution inside Angulars zone. There is nothing bad aboutzone.run(...)`.
If the code executed only changes local properties of a component you can use ChangeDetectorRef.detectChanges() to run change detection for this component.
If the code causes changes in other components (like for example this.router.navigate(...), then detectChanges() is not sufficient. For this case zone.run() should be used.
setTimeout(...) triggers change detection for the whole application and should be avoided as a way to invoke change detection manually.

As explained in EDIT #2, the problem was that creating a promise in the main electron process made the promise non-zone-aware. Creating the promise from Angular solved it.

Related

What are some practical examples of MutationObserver use?

The most confusing thing in this API is for me the reason why use it. I know ReactJS and RxJS and I'm used to the concept when view reacts to data change. So watching changes to DOM, which happens definitely after some mutations to data, I can't see much sense in it. So my question is when (not) use it?
You're thinking of the problem with a situation where you are already one step ahead. If you are using React/RxJS then the actual value of MutationObserver will most likely be very small.
Even within this, however, there is a clear possibility to leverage this. Suppose you are attempting to use a library within your React application that is not explicitly built for it, and modifies the DOM in some way, but want to extend this further or capture something from it. The best example for this would be augmenting FancyGrid further.
Currently, in a component, you would invoke such a library in componentDidMount, the same way the component above is built. However, this is simply fire-and-forget - you don't know when it is done executing, you don't even know what is happening on the "outside".
Enter MutationObserver. With it, before binding such a library to an element, you can use an observer to be notified of when elements are created, track them, and track property changes. The simplest use case for this would be to make a spinner above a (particularly time-consuming on load) grid.

Achieving UI/logic separation when the logic requires callback functions

As far as I understand, in good practice, the UI code should invoke the logic whenever needed, but the logic should know nothing about the GUI ("loose coupling", see for example How can I separate the user interface from the business logic while still maintaining efficiency?).
I am currently writing a chrome web app that uses the chrome.serial api. Most functions from this api are non-blocking and instead invoke a callback function when their work is done. For example
chrome.serial.getDevices(callback)
searches for devices and than calls callback with a list of found devices.
Now, after chrome.serial.getDevices is called from the logic part of my code, its results ultimately have to be communicated back to the UI code.
How do I achieve clean UI/logic separation in this case? Does my UI need to register callback funcions with my logic code for every call it makes? That seems to violate the above principle of loose coupling and feels like it becomes very confusing very quickly.
You can use Promises. Initiate them in your controller code and pass them to the view. The view will then call its .then() method and display the result.
For example:
//controller.js
myAsyncTask = new Promise(resolve,reject=>{
chrome.serial.getDevices(resolve)
})
view(myAsyncTask);
//view.js
function view(myAsyncTask){
myAsyncTask.then(render);
}
If you are using build tools, such as Webpack or Browserify, then you can have your "logic object" extend Node's EventEmitter (there are other implementations that work in-browser, such as https://github.com/Olical/EventEmitter, if you don't want to bundle Node APIs in with a build tool).
Your "logic object", which is a specialized EventEmitter, operates the chrome async API, which contacts the serial devices, then processing the results according to your data layer rules, and then emits its own events when it has something useful for the UI.
The UI listens both listens to, and emits, events on your "logic object", depending on what's happening. Bonus: this event emitter can also be used by separate UI objects to communicate to each other, via events.
EventEmitter is the key that will make this kind of separation feel clean, simple, and extendable.

RxJs Store in Angular 5 - Saving application parameters / data

I am using Angular 5 in a project and I am not sure what Redux / RxJs Store.
I've read about it but I'm not sure I understand it's use yet.
I know it's used so you know the application state, but what does the application state mean...
Does is mean that it saves all parameters and data ... what is it keeping a history of?
State, in short is:
if ($('.someDiv').hasClass('.active') {
// make it glow
}
It just checks the state of the .someDiv element.
And writing your application you start with initial state and later your code, in general, keeps changing various states. You build functions over functions, modules over modules etc. And they all keep some states at particular time. And those states determines what actions must be taken.
When application is simple its easy to catch needed states and run those actions, but in complex apps you have many functions that are spread all over your application but are 'hired' for the some job. In such case, you might run into issues if ordering goes wrong, if some function changes some state and gets some weird / unexpected behaviour - that is hard to debug.
Redux main feature is that it controls that flow and is always 'close' to these functions. Its like a main headquarters of your army of functions - it controls the state and notifies functions squads (subscribers) when it is the moment to take actions. It also benefits that you dont need to pass those states fourth and backwards in your application - store (headquarters) is always 'close'.
But I personally haven't yet needed to use Redux and it is a common mistake to use Redux when it is not needed.

When do I have to wait for a promise in Protractor?

I know there is similar questions on here about this, but I cannot make sense of them for the life of me.
Here's an example, where I need to click a button and check the url.
My initial thought is I would write it as
element(by.id('button')).click();
expect(browser.getCurrentUrl()).toContain('asyncisconfusing');
I know the expect handles its promise but what about the .click? Shouldn't I have to write it like this?
element(by.id('button')).click().then(() => {
expect(browser.getCurrentUrl()).toContain('asyncisconfusing')
})
Or is protractor/webdriver auto-magically doing this?
In theory, since Protractor maintains a queue of promises via Control Flow and works in sync with an AngularJS application under test, you should not resolve promises explicitly unless you need a real value for further processing. In other words, this should be the prefferred form:
element(by.id('button')).click();
expect(browser.getCurrentUrl()).toContain('asyncisconfusing');
In practice though, explicitly resolving click() promises, or adding explicit waits via browser.wait() helps to deal with occasional and random timing issues.
http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/promise.html
The first section talks about how the control flow is used to manage promises without having to chain together every single command.

testing non-angular app with protractor and webdriver fundamentals

I am writing e2e tests for a non-angular app using protractor and while there is quite a bit of information on how to accomplish this, it seems there are many different ways and so I have some fundamental questions.
When testing non-angular sites some folks say to just use webdriver calls, 'browser.driver.get()' for instance so that protractor does not expect angular to finish loading on a page etc. Other responses say that you can set browser.ignoreSynchronization = true, and make calls to browser.get() with no problem (this seems like the ideal solution so you can rely on one api call if dealing with angular and non-angular). Are these two methods equivalent and if not please explain differences.
Does protractor still respect promises when dealing with webdriver or browser.ignoreSynchronization = true? For instance if I make a 'get' call, will protractor wait before executing next step until that 'get' request is fulfilled?
Related to #2, Do I have to use promise chaining when dealing with webdriver/non-angular app to ensure user interactions are executed in order? For example if I use sendKeys() to enter a name and then click() to submit info, do I have to nest the click() inside of sendKeys().then?
How exactly does webdriver know all elements of a page are ready after a get request (every solution I see involves using a timeout or relying checking if an element exists first)? What if that page has embedded apps (like a google map for instance) and I want to simulate a user clicking on 'view larger map' for my e2e test?
No, they are not equivalent. browser.get() is a wrapper on top of browser.driver.get(). It will throw an error if it does not find angular library on page load. So, use browser.driver.get() for non angular apps.
No, it does not respect promises and will not wait.
No, webdriver manages that using promise library and control flow.

Categories