Ionic nav views with side menus lose history when swtiching tab - angularjs

I'm trying to migrate a native app to Ionic but I'm experiencing one major issue which is almost certainly a blocker if I can't at least work around it.
Basically, I have an application with multiple tabs and each of them has a split view. I attached a plunker to demonstrate what a simplified app version would look like. My problem is that when switching tab, ion nav views do not keep their history. To be more specific about my example: after selecting a playlist from the side menu and switching tab to "Search" and then back to "Playlists", the navigation history on that tab is not preserved and gets reset to its "root" state.
I think this happens when the ion-nav-view element is not a direct child of the ion-tab element (as in my case, since it's inside a ion-side-menu-content), but I'm not quite sure.
Overall, I feel the app states in the example might need some love as well. What am I doing anything wrong? Is it a known Ionic limitation?
State provider configuration follows, below is the link for the full plunker example:
$stateProvider
.state('tabs', {
url: '/app',
abstract: true,
templateUrl: 'tabs.html',
controller: 'AppCtrl'
})
.state('tabs.search', {
url: '/search',
views: {
'search': {
templateUrl: 'search.html'
}
}
})
.state('tabs.playlists', {
url: '/playlists',
views: {
'menu': {
templateUrl: 'playlists.html',
controller: 'PlaylistsCtrl'
},
'menuContent': {
templateUrl: 'empty.html'
}
}
})
.state('tabs.playlist', {
url: '/playlists/:playlistId',
views: {
'menu': {
templateUrl: 'playlists.html',
controller: 'PlaylistsCtrl'
},
'menuContent': {
templateUrl: 'playlist.html',
controller: 'PlaylistCtrl'
}
}
});
Here's a plunker with the app: http://plnkr.co/edit/UZp3EE3l6X5IIdvpu477
Thank you!

According to This other question, every time you navigate through a tab, it creates a new history, so there's no back. You would need to handle this with your own code. They propose to use the goBack() directly. Something like:
$ionicHistory.viewHistory().backView
Also, you would need to handle the case when there's no backView because you just logged in into the app.

Related

What's wrong with my Ionic/Angular state inheritance?

I'm new to Ionic and AngularJS so I'm struggling with (hopefully) something simple. I have these two states:
.state('menu', {
url: '/mobile/beer',
controller: 'beerCtrl',
templateUrl: 'templates/beer-menu.html'
})
.state('detail', {
url: '/mobile/beer/:id',
controller: 'beerDetailCtrl',
templateUrl: 'templates/beer.html',
resolve: {
beer: function($stateParams, beerService) {
return beerService.findBeerById($stateParams.id)
}
}
})
The 'menu' state fetches all beers from a server and hand the list over to 'beer-menu.html' that contains an ion-list to display the list. When the user selects a beer in the list, the 'detail' states steps in to display more details about the beer.
It's all working but I want to improve it a bit by using an abstract state:
.state('beer', {
abstract: true,
url: '/mobile/beer'
})
.state('beer.menu', {
url: '',
controller: 'beerCtrl',
templateUrl: 'templates/beer-menu.html'
})
.state('beer.detail', {
url: '/:id',
controller: 'beerDetailCtrl',
templateUrl: 'templates/beer.html',
resolve: {
beer: function($stateParams, beerService) {
return beerService.findBeerById($stateParams.id)
}
}
})
It looks totally OK to me - esp because this is based on one of the 'Formulas' from the Ionic Framework website. Nevertheless the app stops working when I structure the states like that. I've tried to tweak it in various way but couldn't get it working. Do you see what's wrong?
As the UI-Router documentation you have to define also a template for abstract state 'beer':
Remember: Abstract states still need their own <ui-view/> for their children to plug into. So if you are using an abstract state just to prepend a url, set resolves/data, or run an onEnter/Exit function, then you'll additionally need to set template: "<ui-view/>".
See:
https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views#abstract-states

What's the purpose of adding the extra 'views:{'foo':{}}' when declaring a view in angular?

What's the purpose of declaring the a view with views:{} e.g.
.state('app.example', {
url: "/example",
views: {
'my-example': {
templateUrl: "views/example.html",
controller: 'myCtrl'
}
}
})
as opposed to this
.state('app.example', {
url: "/example"
templateUrl: "views/example.html",
controller: 'myCtrl'
}
})
It is used for having multiple parallel views for a single state. Example:
.state('app.example', {
url: "/example",
views: {
"example1": {
template: 'app.example.view1'
},
"example2": {
template: 'app.example.view2'
}
}
})
index.html
<ui-view = "view1">
</ui-view>
<ui-view = "view2">
</ui-view>
So effectively, your state has two parallel views. You can find the detailed explanation here.
It is used to show multiple views on a single page(state).We use the above method when we show multiple parallel views on a single page.Suppose you have a page in which you have to show different charts on different positions,we created multiple views and appended the chart on those positions.Hope this helps
Ok I figured it out and I created a codepen for this to get it to fit nicely inside other ionic app. This updates the child view when a button or a sidemenu item is pressed with more detail inside the codepen by making multiple child views share a name like so
views: {
'shared-child-view' :{
templateUrl: "[path to your children, in our case child1.html and child2.html]"
}
}`
where it would look something like this
.state('sidemenu.parent.child1', {
url: "/child1",
views: {
'shared-child-view' :{
templateUrl: "child1.html"
}
}
})
.state('sidemenu.parent.child2', {
url: "/child2",
views: {
'shared-child-view': {
templateUrl: "child2.html"
}
}
})
`
Where it can sit in a parent that sits in an abstract state like this (but it doesnt have to but It's likely that this is how your ionic app will be setup):
.state('sidemenu', {
url: "/sidemenu",
abstract: true,
templateUrl: "sidemenu.html"
})
.state('sidemenu.parent', {
url: "/parent",
views: {
'menuContent' :{
templateUrl: "parent.html"
}
}
})
You can alternate or change each child view inside a view to evrey view with the same name, in this case "shared-child-state"
<div ui-view name="shared-child-view"></div>
and it can be made clickable with
<a href="#/sidemenu/parent/child2" class="item">Child View 2
</a>
This doesnt work if you use ui-serf.
I hope this helps someone!

Content disappears when navigating to previous view/state on Ionic app

I've created a starter app with side menu using Ionic based on AngularJS.
The basic navigation layout of the app suppose to be:
main page
ion-side-menu that shows our company's service types as a list. (when item is clicked go to:)
company services list for the chosen type (when item is clicked, go to:)
specific service view
Problem is that when I hit the auto-generated "Back" button from inside a specific service view, and expect to get back to the services list for the chosen type, the app does routes back to the list, he content is seen to about half a second, but then - ALL the content (including the top navbar) is hidden, and though, still clickable!
This also happens not just for the "Back" button but also when clicking a link from the specific service view to any arbitrary services list view.
Since the previous view is seen before disappearing, I conclude that the routing implementation is valid, but yet tried to use ui-route ui-sref and other approaches to navigation but couldn't solve this.
It happens both on chrome browser and android device.
My stateProvider config looks like that:
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html',
controller: 'AppCtrl'
})
.state('app.main', {
url: '/main',
views: {
'menuContent': {
templateUrl: 'templates/main.html',
controller: 'MainCtrl'
}
}
})
.state('app.services', {
url: '/services/:tid',
views: {
'menuContent': {
templateUrl: 'templates/services.html',
controller: 'ServicesCtrl'
}
}
})
.state('app.service', {
url: '/service/:sid',
views: {
'menuContent': {
templateUrl: 'templates/service.html',
controller: 'ServiceCtrl'
}
}
})
Would really appreciate any help.

How to use tabs in Ionic with a abstract state parameter / clear cache on navigation

I have a home screen with a list of groups. Each list item links to the same abstract state with a parameter (gid, from groupid).
.state('group', {
url: "/group/:gid",
abstract: true,
templateUrl: "templates/group.html"
})
.state('group.members', {
url: '/members',
views: {
'group-members': {
templateUrl: 'templates/group-members.html',
controller: 'MemberCtrl'
}
}
})
.state('group.events', {
url: '/events',
views: {
'group-events': {
templateUrl: 'templates/group-events.html',
controller: 'EventCtrl'
}
}
})
.state('group.settings', {
url: "/settings",
views: {
'group-settings': {
templateUrl: "templates/group-settings.html",
controller: 'SettingsCtrl'
}
}
})
In that abstract state I have 3 tabs that should work on the same parameter/group. While this works well for the tab that I link to from the home screen, the other tabs remember their history and after going back to the home screen and selecting another group only the linked to tab shows the details for the correct group. The other tabs will show details for the previously (cached) group.
I think this might be fixed with clearing the cache somewhere, but ideally I do not want to clear it each time for each tab. While navigating between tabs in the same group the cache should be used. Only when going back to the home screen and after a new group is selected should the cache of the tabs be cleared.
An alternative might be to somehow make the other views aware that a new group has been selected. For the tab that is linked to that is easy: the parameter is given in the url. But the others are not linked to from the home screen. Is there a way to pass an argument to ion-tabs in a template file?
What is the best approach for getting this to work? This must be a use case that occurs now and then and I suspect there is a preferred way for handling it. I am using Ionic 1.0.0. Thanks.
The group-members view was being updated, but the rest of the groups view was stale. By adding cache: false to the state in the codepen below it worked fine.
http://codepen.io/whiskeyjack/pen/BNmpMx
javascript
.state('group', {
url: "/group/:gid",
abstract: true,
cache: false,
templateUrl: "templates/group.html"
})
See also $ionicHistory.clearCache() at http://ionicframework.com/docs/api/service/$ionicHistory/
Use this way to clear cache in ionic
<ion-view cache-view="false" view-title="My Title!"> ... </ion-view>

ionic routing issue, shows blank page

I started building ionic app on top of the sidemenu starter app. The starter app has a base state 'app' which is abstract and all the sidemenu pages are children of the app for example app.search, app.browse, app.playlists etc.
I have similar hierarchy. However, I want the start page to be some other page, which means it is at the app level.
The states look like this:
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('join', {
url: "/join",
views: {
'menuContent' :{
templateUrl: "templates/join.html",
controller: 'joinCtrl'
}
}
})
.state('app.search', {
url: "/search",
views: {
'menuContent' :{
templateUrl: "templates/search.html",
controller: 'searchCtrl'
}
}
})
.state('app.results', {
url: "/results",
views: {
'menuContent' :{
templateUrl: "templates/results.html",
controller: 'resultsCtrl'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/join');
When I run the app, the url defaults to
http://192.168.1.4:8100/#/join
and shows a blank page. Obviously, the join.html is not blank. Also, the console.log messages in joinCtrl are not outputted.
I am not able to figure out why is it not loading the join page. When I change the otherwise to point to '/app/search', everything works.
Any idea what's going on? How do I load the initial page by default and then navigate to the 'app.search' state?
I would expect that because the app is abstract - it is there for a reason. To be parent/layout state. In its template should most likely live all other states.
If yes - check this working example I created to demonstrate that. What we need is to mark the join as a child of the app state. Then the 'menuContent' placeholder will be properly searched in the app template:
.state('join', {
parent: 'app',
url: "^/join",
views: {
'menuContent': {
templateUrl: "tpl.join.html",
controller: 'joinCtrl'
}
}
})
There is a working plunker
The definition url: "^/join", is there to support the idea, that the url defined like this:
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/join');
will work even for nested state (join is child of app). See:
Absolute Routes (^)
If you want to have absolute url matching, then you need to prefix your url string with a special symbol '^'.
This is just one way... we can do the similar stuff if the join is not nested state, but then it should target the unnmaed view '' instead of 'menuContent'

Resources