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).
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.
Starting with Angular JS,I have read that components cannot access parent scopes, I mean, each component has its isolated scope. I read this is in order a component can modify its own data and not other components data. This helps to avoid situations where some data from a component can be modified from multiple inner components and would be difficult to resolve a problematic situation with data viewing and find which component is the problematic one.
So later, I read that you can pass in some data to a component with "bindings" from parent components, so the inner component cannot access the parent scope but gets information from the parent scope.So untill here, its ok
But I got surprised when I read that with "binding" in components, you can pass in a "property" by reference, so if in the inner component I modify this property, is not I can access the parent scope, but I am modifying the parent scope, right? So, the problematic situations can happen again, right?
Thanks
While accessing $parent in isolated scope is clearly an antipattern, this doesn't mean that modifying objects through bindings cannot cause problems.
It depends if parent component should be aware of changes that are made in this object. If it should, then the problem arises, because deep change detection (e.g. with $scope.watch(..., true)) is costly. In order to avoid this, child component can notify the parent of changes (scope events, & bindings), or use = binding with immutable object (the object is never mutated; any change should result in object being copied to a new one).
Working with Angular 1.5, and given a component hierarchy such as:
compA
compB
compC
Is there a better way to bubble events from nested child components to the interested parent component?
The two options I see are:
Call $scope.$emit() from compC. Use $scope.$on() in compA.
Pros
compC is decoupled and more likely to be re-usable
compB does not have to be made aware of this output in any way
Cons
Use of $scope, which I was hoping to avoid since it no longer exists in NG2. However, I'm not finding a better solution.
Pass callback from compA to compB and then to compC using a & binding
Pros
compC is decoupled and more likely to be re-usable
Cons
compB has to be involved, making this solution fragile
Maybe I should make an exception and allow $scope use for this scenario. Or, there is the option of emitting/listening on $rootScope instead.
EDIT: I'm finding $rootScope should be used only for truly global events, and it's common to forget to clean up your listeners, which is done automatically on $scope.
One more way of passing data from CompC to CompA (or in between any components) would be to use a service instead of event based message passing. You could define dependency in all the components on a service and use that service to pass data amongs them. This would again make the components reusable.
After executing get or put in indexeddb, the callback is updating the scope.
The problem is, no update is being triggered in the ui!
A common solution is to use apply or digest but that is wrong, you should NOT use those operations. Angular should update it automatically.
Now my guess, after reading some stuff, is that the callback is being executed in a different context, outside of the scope.
My question is basically: how can I execute an indexeddb callback in an angularjs scope context?
EDIT:
A rough look on how its built:
GetObjectStoreData(iDb, objectStore, function (res) {
$scope.result = res;
}
The call is made from inside the scope so the scope is used in the callback. The callback parameter is the contents of the object store
You asserted that you should not have to use Scope.$apply because AngularJS should do it for you.
This is true only for callbacks managed by Angular. For example, when you use $timeout in place of window.setTimeout, angular wraps your provided callback in a call to Scope.$apply, causing a digest cycle to run once your callback completes. If you interact directly with browser APIs rather than calling through AngularJS, it is your responsibility to call Scope.$apply at the appropriate time.
If you don't want to manage the scope directly, you could instead use a wrapper library like angular-indexedDB, which (amongst other things) handles the scope notifications when callbacks occur.
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?