Preserving data in dynamic tabs – AngularJS/Angular Material/UI-grid-open source projects callemall/material-ui
In the main view of my app, I have a section dedicated to customer information and and one section containing a (ng-)grid of products. Above the main view is a navbar which contains navlinks (not tabs, just links), to various states of the app – such as list of sales, list of customers, so on so forth.
A sample of my app.js (containing configuration) reads as follows -:
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
})
.state('base', {
templateUrl: 'views/base.html',
controller: function BaseCtrl($scope, SaleTabs){
$scope.tabData = SaleTabs.data;
$scope.saleTabSelected = function(tab){
SaleTabs.saleTabSelected(tab);
};
}
})
.state('base.sale', {
url: '/sale/:saleId',
views: {
'main-view': {
templateUrl: 'views/sale.html',
controller: 'SaleCtrl'
}
}
})
.state('base.sales', {
url: '/sales',
views: {
'main-view': {
templateUrl: 'views/sales.html',
controller: 'SalesCtrl'
}
}
})
The base.sale
state is the main view. Now I need to implement a couple of things -:
-
When
base.sale
is loaded with a saleId (upon which it fetches all the data from the server and puts it into the customer info section and the UI grid), I want this state to be associated with amd-tab
. Basically a tab needs to open if I go to this state via the URL. -
When I create a sale from the
base.sale
view, I trigger a state change to/sale/:saleId
. I need this state to open in a new tab. -
When switching between tabs, I need any data that I changed in that tab’s state to be preserved – this is crucial in differentiating between navlinks which simply load state on clicking, and tabs.
Since I needed the tabs functionality in both my BaseCtrl
and my SaleCtrl
, I decided to make it a service. This is what it looks like currently -:
angular.module('myApp')
.factory('SaleTabs', [
'$state',
function($state){
var SaleTabs = this;
SaleTabs.data = {
tabs: [],
selectedIndex: 0
};
this.addSaleTab = function(title, saleId){
// Check if our tab already exists
var result = SaleTabs.data.tabs.filter(function(tab){
return tab.saleId === saleId;
});
if(result.length > 0){
// If it exists, select it
SaleTabs.saleTabSelected(result[0]);
} else {
// If not, create it
var tab = {
title: title,
saleId: saleId,
index: SaleTabs.data.selectedIndex
};
SaleTabs.data.selectedIndex += 1;
SaleTabs.data.tabs.push(tab);
SaleTabs.saleTabSelected(tab);
}
};
SaleTabs.removeSaleTab = function(tab){
var index = SaleTabs.data.tabs.indexOf(tab);
SaleTabs.data.tabs.splice(index, 1);
SaleTabs.data.selectedIndex = index - 1;
};
SaleTabs.saleTabSelected = function(tab){
SaleTabs.data.selectedIndex = tab.index;
$state.go('base.sale', {saleId: tab.saleId});
};
return SaleTabs;
}
]);
Ignoring the erroneous logic for switching between tabs (that’s a topic for another post I suppose), this is how I’ve written my tabs definition in the template for my base controller (BaseCtrl
, in the app.js snippet above) -:
{{ tab.title }}
Note that I had to use ng-click
instead of md-on-select
because it doesn’t seem to work in angular-material 0.8.3 (I will be upgrading soon to 0.9.4). The mdi
bit is the Material Design Icons library in action – I was trying to create closable tabs.
Currently, I am able to create a tab when a sale is created from the base.sale
view, but all the tab does is function like a navlink. How would I go about achieving the goals listed above?