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.
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.
I have an IOT application that communicates with ble devices. So I use react-native-ble-manager.
In this package, you have to use event listeners such as;
bleManagerEmitter.addListener( 'BleManagerDidUpdateValueForCharacteristic', this.handlerCharListenerSettings);
(For more information you can check this)
So I use these event listeners in different screens, but it's not a proper way. Because In every screen that has event listener loads this addListener and this causes problems. In componentWillUnmount event you can clear these listeners, but the correct way is to control all these listeners on one logical place such as App.js or anywhere similar.
My problem begins here:
I have different screens, I want to set their states from App.js. I mean when I change a value from App.js event listener, I want to make the other screens affected by this value. How I can do this or is this a proper way of using listeners?
My RN version is 0.58
If you want to share the listener either use the Context API as mentioned or create a wrapping component. Much like the App.js create a new component:
<Wrapper>
<YourApp/>
<Wrapper/>
class Wrapper extends React.Component {
componentWillMount() {
bleManagerEmitter.addListener( 'BleManagerDidUpdateValueForCharacteristic', this.handlerCharListenerSettings);
}
}
I am subscribing a user to events using pusher-js, and am having an issue with the information I am passing in the callback not staying updated. I use react router and have a 'location' context on my components.
connect() {
let {pusher, channelName, eventName} = this.props;
let pusher_channel = pusher.subscribe(channelName);
pusher_channel.bind(eventName, () => console.log(this.context.location.pathname));
}
For demonstration purposes I am just console logging the current url pathname the user is on when the event comes through. This component gets loaded and stays loaded when the user loads that application. The problem is that, if a user navigates to another page in the app and an event comes in, the pathname does not reflect the actual page the user is on but rather the page the user was on when they initially loaded the app.
Is there a way to keep the callback updated as to any context or prop changes that have occurred?
Updating Context
Don't do it.
React has an API to update context, but it is fundamentally broken and you should not use it.
Read more.
Use React Router's location descriptors instead.
I'm trying to wrap my head around Facebook's Flux...
Say I have an app with a side menu that can be hidden and shown via a button in the header.
My header is one component, my side menu is another component.
Currently my Header component just sets a class on the HTML div side menu element which gets animated to hidden by CSS.
What's the general idea here?
ReactJs doesn't really care about how it gets its data (how it's data is getting passed in or how that data should be handled across the web application). That's where Flux comes in, it creates a functional approach on how data is handled. Data-flow is essentially:
Action -> Data Store -> Component
Mutation of data happen through calling Actions. The Data Stores themselves have to listen on the actions and mutate the data within the store. This keeps the data structure and logic flat.
In your case, your dataflow would probably look something like this:
Header --> User Click --> Fires action --> Updates store --> Side menu listening and responding to that store change.
Your case is a simple example which you probably don't really need Flux. I think it's easier if you have a parent component that maintains the view state logic, and use props/callbacks for the 2 children components (side menu and header). But a more advanced example that you need to make ajax calls and maintain session, Flux would become useful. Like if you have a Login Component, and you want to show different side-menu options and header options depending on user:
Login Component --> User Logins --> Calls Action #signIn --> Showing Loading State
--> Dispatcher handles action (make api call to authenticate user and load user data)
On success (for the api call), alert sessionStore, and populate store with data
On error, maybe fire another action that says login failed or something
SessionStore ---> Header Component (Listens to Store) --> Update view based on store information
---> Side Menu Component (Listens to Store) --> Update
speaking more general:
flux is a software architecture for a unidirectional Dataflow. It's Chain is Action -> Delegation -> Store -> View... The action - for example a Button Click - gets delegated to stores where your applicationlogic and data is kept... here your action and data will be changed and processed. The store eventually emits an event which views (for example react components) previously registered on with a callback. In this callback you can GET your data from your stores. It is important to mention that you can only access the stores READ-Only.
So for your case... if you want component A to affect component B you will have to register component B to the store eventEmitter and GET the desired data from the store. Once component a triggers an action it gets delegated to the store, your functions are performed and eventually the event gets thrown that starts component B's callback.
Hope this got clear enough... its way cooler with some nice drawings.
I'm trying to build a reusable component that I can use to load ads via Doubleclick (DFP) asynchronously. The problem I'm running into is there are several dependencies before an ad can actually load, and I'm not sure the best way to handle the communication and sequence. Because the ad components render anywhere on the page and don't render as part of a parent, I'm not sure how to handle it since I can't embed ads inside of a parent to communicate.
In this article:
http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html#step-4-identify-where-your-state-should-live
There is this potential solution, but after some searching I wasn't able to find an example that doesn't tie the rendering together.
"If you can't find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component."
Here are the details/steps:
1). I first need to load the JS http://www.googletagservices.com/tag/js/gpt.js
This creates a global googletag object.
2). I need to set global targeting (i.e. site name, content type, etc) that will apply to all ad tags (configurable).
Example
googletag.pubads().setTargeting('site', window.location.hostname);
3). I need to define the ad slots and targeting for individual ad slots that React rendered.
googletag.defineSlot('/12345/zone', [[300, 250], [300, 600]], 'div-gpt-ad-300x250-0').addService(googletag.pubads()).setTargeting('pos', 'sidebar_300_1');
4). I need to specify settings globally
googletag.pubads().collapseEmptyDivs();
googletag.pubads().enableSingleRequest();
googletag.enableServices();
5). I need to render the ad slots (with the option to render individually or globally)
googletag.cmd.push(function() {
googletag.display(divIdAttribute);
});
or just
googletag.display();
I Initially created two components: a DFPManager that loads the required Javascript once, and then a DFPAd that actually handles the individual ads. I couldn't figure out the communication.
I also need to be able to reload individual ads based on external events (i.e. ajax page change) or create new ads as a user scrolls into infinite scroll content.
Is the best solution to manage my own events system where I put listeners in my components to depend on external events, or is there a better way to manage this? In that case I wouldn't necessarily need a DFPManager since no rendering takes place there and I could trigger an event when ready. This is a similar problem for analytics tracking where I need to track pageviews based on external events, but only after first loading an initial javascript (i.e. Google Analytics tracking js).
<div id="300-250-ad"></div>
<script type="text/jsx">
/** #jsx React.DOM */
React.renderComponent(
<DFPAd size={[[300, 250], [300, 600]]} targeting={[["pos", "sidebar_300_1"]]} />,
document.getElementById('300-250-ad')
);
</script>
I may be overthinking this. Appreciate any suggestions.
Turning DFPManager into just an object which fires change events sounds like a good plan. It wouldn't be a React component itself any more. The individual ad components could accept the store object as a prop, then subscribe to the change events in componentDidMount, and unsubscribe in componentWillUnmount. The onChange handler could call setState with the current data from the store, which will trigger a re-render.
The store could be implemented like the store in the Flux TodoMVC example, which uses an EventEmitter base class. If there is no interactivity, using a full dispatcher is probably overkill, since there won't be any user actions by the sounds of it.