I have a handful of directives on a page that all share the same data. I have a heavy API call that needs to be made somewhat frequently to update these directives. Each directive contains the data in three states:
A display state showing the results and,
A loading state indicating that the call to the API is being made
An error/empty state
How the state is shown is directive specific and is not always a matter of updating some css or a variable. Sometimes it requires the calling of a function.
Trying to do this it seems I would have to listen for my trigger with a watch, then broadcast that I'm 'in loading' to all my directives. Then broadcast that the resource was loaded to resolve the loading state if it succeeded, else broadcast that there'd been an error. That's a lot of different broadcasts and 'on's in all my directives. Is there a more efficient way invoking direct methods on child directives?
Related
I have a bunch of hierarchically arranged components, namely:
partner component, that works with partner organizations, knows how to update or remove them, etc.;
leader component, that works with leader organizations, knows how to deal with leader organizations;
list component, than displays to the user both partner and leader organizations;
a service - when partner or leader components removes an organization, info on this organization is passed to the list component so that these changes are reflected in front immediately.
I cannot provide code samples on this, it's too big, moreover my question mostly requires a conceptual advice rather than code issues.
Right at the moment it perfectly works - components are doing their job sending data to the service:
IndexCollection.setIndexes(
vm.leaderIndex, vm.partnerIndex, 'added_l', response.id
);
the service does it's job and pushed changes to a variable, which I $watch in the list component to trigger respective actions like this:
$scope.$watch(() => IndexCollection.indexes, function() {
let indexes = IndexCollection.indexes;
switch (indexes.message) {
case 'deleted_p':
removePartner(indexes);
break;
case 'deleted_l':
removeLeader(indexes);
break;
case 'added_l':
addLeader(indexes);
break;
}
});
My question is of a more theoretical essence. Is there a possibility to trigger real-time actions from service in the list component without using$watch, $emit, $broadcast and other standard tools we usually use in this regard?
Can I somehow achieve the same result by means of using callbacks? I mean, when a change in service occurs, it triggers immediate action in the respective controller?
While using $watch may solve the problem, it is not the most efficient solution.
You might want to change the way you update and retrieve the data of your service.
The component controllers should manipulate the data stored in your service with functions in your service based on actions/events triggered from your component and you inject the service in the component.
MyDataService.getIndexCollection() {}
MyDataService.putIndexCollection() {}
Then you pass the data down to all your directives and components via require or bindings for components or isolated scopes for your directives.
For example once the partner components edits the data on the service you fetch the data again from your service and the updated data will be passed to your list component and update the view via $apply() if needed.
In Angular 1.x, the digest cycle use to take care of updating view when data is changed on scope. How does property binding and interpolation works in Angular 2 when data is changed in component?
Angular2 has its own version of Zone (called NgZone) that take care of detecting changes. When NgZone detects a change is called an event called onTurnDone. Angular has a component called ApplicationRef that listens for this event to occur and when it does the tick() method (of the ApplicationRef) is called. The tick() method, at this point, is the responsible for the changeDetection cycle that updates the view.
If I've understood the question you have made, then maybe you'd like to read a post that goes deeper into this subject of the change detection strategy:
https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html
I'm struggling to understand why you'd need to use $emit rather than using the controller as syntax and directly accessing & updating data on the parent scope. What are the use cases?
It's obviously more efficient to call a method on a parent scope directly, rather than using $emit (or $broadcast) to dispatch an event. But there are some valid reasons to do it.
Here's some reasons that come to mind:
You want to emit an event that more than one application component can receive/process.
You want to de-couple your components so that in the future the message may be processed by a different controller (or maybe a service or a directive, etc.).
You want to emit an event that a service/factory can consume (these are not associated with any view/$scope, but you can inject the $rootScope into them).
My AngularJS+Bootstrap app includes a badge in the navbar intended to give the user a count of messages awaiting their attention. I have wrapped the navbar in its own NavController associated with the div that contains the navbar.
The main functions of my app are handled by other controllers rendered in via a route provider, such that every page should have two controllers active: the NavController, and the controller specific to that page (e.g. Edit). Messages get generated and queued by these other controllers. My problem is that I cannot get the navbar badge to update to reflect the new count.
I can "cheat" and use Jquery to update the DOM directly, but that just seems wrong.
What is the "Right Way" to manage navbar state in Angular when you need it to update displays in response to state changes elsewhere in the app?
I can tell you ways to manage global state.
First one would be to raise events, using $emit or $broadcast. I am not sure how your views are setup, but $rootScope.$broadcast just works with event broadcasting throughout the app. You can raise events that signify what happened, such as a new message got generated, message count updated. Something like
$rootScope.$broadcast('MessageCountUpdated', {messageCount:30});
Catch it anywhere with $scope.$on.
The other way is to use a service, which at any given time tracks the messages, in queue and provides useful metric that any view can bind to.
module.factory('MessageQueue',function(){
var service={};
service.queue=function(msg) {};
service.dequeue=function() {};
service.messageCount=function(){};
});
The service such as above can be injected anywhere, and can be bound to the view.
Our clients add our JS tag to the head of their page. Our tag needs to know when reactJS has finished before it modifies the page.
I have tried using jQuery's $(document).ready() but this fires before reactJS has finished. I can use $(window).load() but if there are a lot of images on the page, this would be too slow.
What are the options to bind to the completion of React.renderComponent?
Set off the execution of your code via the optional callback that you can pass to React.renderComponent. This callback won't execute until React.renderComponent has finished. If you don't have access to the code that is executing React.renderComponent (which I've just realized is almost definitely the case) then there is no definitive/reliable way to listen for the execution of a function unless of course that function exposes some sort of event that you can listen for, or even guarantees that "x" will be the case once it has finished. Sadly for your case, this doesn't appear to be an option with React.renderComponent.. Let me know if you have any further question.
If you don't have direct control over the React code, you could always see if you can sniff for relevant changes via DOM Mutation events.