Plnkr: https://plnkr.co/edit/Brmcxd2LjGVJ6DXwuJtF?p=preview
Architecture:
$login
-> $container
(contains $dashboard
and $feed
), dashboard
contains: $tickers
, $tags
, $viewHeader
and $socialMedia
components.
Goal:
tags
needs to communicate and send a tag object into the viewHeader
and socialMedia
states.
Expected:
After Login, selecting a button in the Tags list should send that tag
into the ViewHeader and SocialMedia states/components.
Results:
After Login, selecting a button in the Tags and going to $state dashboard
the $states for the ViewHeader and SocialMedia refresh but the view model variable {{ term }}
does not update with the appropriate tag in either component.
Design explanation:
The problem I'm trying to solve with a mix of states, components, sibling components and views is that every component in the app always refreshes/re-onInit when any state changes. $state.go('dashboard'...
This is not ideal, what I want is to make sure that only the components that need to refresh, refresh.
IE: When I select a Ticker or a Tag I do NOT want the Feed component to refresh every single time. However the user should be able to take an action in the Feed component and it will update all the others.
This is why I created a container
state to hold both the dashboard
and feed
states. Originally I had everything in the dashboard
state, and anytime $state.go('dashboard'
happened the feed
component would also refresh. If there is a better way to solve this issue lmk! :)
PS: Started learning about state management using ui-router to remove the reliance on $rootScope. So far my real app is much more stable, however has the annoying feature that every component refreshes/re-onInits.
In this screenshot below I can clearly see that the correct tag
is being passed into the correct $states, but yet {{ term }}
does not update.
Code snippets (Full code in Plnkr)
app.container.html (container state)
<div>
<dashboard-module></dashboard-module>
<feed-module></feed-module>
<!--<div ui-view="dashboard"></div>-->
<!--<div ui-view="feed"></div>-->
</div>
dashboard.html (dashboard state)
<div class="jumbotron text-center">
<h1>The Dashboard</h1>
</div>
<div class="row">
<tickers-module></tickers-module>
<!-- Tags component goes here -->
<div ui-view></div>
<view-module></view-module>
<social-module></social-module>
</div>
^ The reason I'm using a <div ui-view>
above to load the tags state is that so far has been the only way I could initialize the tags module and show the tags list from the tickers component.
tickers component controller:
tickers.component('tickersModule', {
templateUrl: 'tickers-module-template.html',
controller: function($scope, $state) {
console.log('Tickers component', $state.params);
$scope.tickers = [
{ id: 1, ticker: 'AAPL' },
{ id: 2, ticker: 'GOOG' },
{ id: 3, ticker: 'TWTR' }
];
$state.go('tags', { ticker: $scope.tickers[0] }); // <---
$scope.clickTicker = function(ticker) {
$state.go('tags', { ticker: ticker });
}
}
^ That <--- above allows the tags state to load the tags component element and wire up the tags list, based on the state of ticker
var tags = angular.module('tags', ['ui.router'])
tags.config(function($stateProvider) {
const tags = {
parent: 'container',
name: 'tags',
url: '/tags',
params: {
ticker: {}
},
template: '<tags-module></tags-module>',
controller: function($scope, $state) {
console.log('Tags state', $state.params);
}
}
$stateProvider.state(tags);
})
tags.component('tagsModule', {
templateUrl: 'tags-module-template.html',
controller: function($scope, $state) {
console.log('Tags component', $state.params);
const tags_model = [
{
ticker: 'AAPL',
tags : [{ id: 1, term: 'iPhone 7' }, { id: 2, term: 'iPhone 8' }, { id: 3, term: 'Tim Cook' }]
},
{
ticker: 'GOOG',
tags : [{ id: 4, term: 'Pixel' }, { id: 5, term: 'Pixel XL' }, { id: 6, term: 'Chrome Book' }]
},
{
ticker: 'TWTR',
tags : [{ id: 7, term: 'tweet' }, { id: 8, term: 'retweet' }, { id: 9, term: 'moments' }]
}
];
function matchTags(ticker, model) {
return model.filter(function(obj){
if (obj.ticker === ticker) { return obj; }
});
}
$state.params.ticker = $state.params.ticker || {};
$scope.tags_model = matchTags($state.params.ticker.ticker, tags_model)[0];
$scope.clickTag = function(tag) {
console.log(' Tag clicked', $state);
$state.go('dashboard', { tag: tag });
}
}
});
The click function in the Tags list:
$scope.clickTag = function(tag) {
console.log(' Tag clicked', $state);
$state.go('dashboard', { tag: tag });
}
socialMedia controller:
social.component('socialModule', {
templateUrl: 'social-module-template.html',
controller: function($scope, $state) {
console.log('Social component', $state.params);
$scope.term = $state.params.tag.term;
}
});
viewHeader controller:
view.component('viewModule', {
templateUrl: 'view-module-template.html',
controller: function($scope, $state) {
console.log('View component', $state.params);
$scope.term = $state.params.tag.term;
}
});
Strange doubling up of components
Just noticed this, after clicking on a tag and breaking inside of the social.component. It looks like the dashboard component is being re-rendering in place of the tags component for a split second: