I'm trying to understand the master detail (MD) pattern in ionic with a sidemenu. The example code has 'Playlists' as master, and 'Playlist' as detail.
The states looks like this:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app.playlists', {
url: '/playlists',
views: {
'menuContent': {
templateUrl: 'templates/playlists.html',
controller: 'PlaylistsCtrl'
}
}
})
.state('app.single', {
url: '/playlists/:playlistId',
views: {
'menuContent': {
templateUrl: 'templates/playlist.html',
controller: 'PlaylistCtrl'
}
}
});
$urlRouterProvider.otherwise('/app/playlists');
});
But first off all, I don't understand why the Playlist state is called 'app.single'. I don't see the word 'single' anywhere else in the code. What if I would want to have a master detail pattern on page 1, and another different master detail pattern on page 2 (selected through the menu)? How would I call the detail state there?
And then my other question is related, I can't seem to get my own MD pattern to work. These are the codes that matter:
app.js:
.state('app.master', {
url: '/master',
templateUrl: 'templates/master.html',
controller: 'MasterCtrl'
})
.state('app.detail', {
url: '/master/:id',
templateUrl: 'templates/detail.html',
controller: 'DetailCtrl'
})
master.html:
<ion-view title="Master">
<ion-content>
<ion-list>
<ion-item class="item" ng-repeat="person in people">
{{person.text}}
</ion-item>
</ion-list>
</ion-content>
</ion-view>
menu.html:
....
<ion-item menu-close href="#/app/playlists">
Playlists
</ion-item>
<ion-item menu-close href="#/app/master">
Master
</ion-item>
....
The Playlists MD works all the way, but my own MD doesn't. I do get the Master list with items (controller and services work fine), but when I click an item in the list, nothing happens. Then when I swipe back (this should open the sidemenu), the detail page 'sort of' shows up without css or data. So somewhere there's a glitch. I just don't seem to understand the whole state/url thing, while I read enough documentation..
I guess, you are new to Ionic without any knowledge of AngularJS and Angular UI Router.
First, the dot-notation in state is to mark some children which will inherit from one specified parent view. As you read in the www/js/app.js, you will see a state .state('app'), and this is the parent view, menu.html, and the children views will be replaced in this tag, or so called Angular directive,
<ion-nav-view name="menuContent"></ion-nav-view>
The children or detail views needs to specify two things,
first, parent name, the word before the dot, as in example of sidemenu, app.[child_name]
second, the child view should specify which part of parent view to be fit in, through the views property, as in example above,
you see this menuContent,
views: {
'menuContent': {
templateUrl: 'templates/playlist.html',
controller: 'PlaylistCtrl'
}
}
Let's talk about Master-Detail Pattern, it is just a concept of how to display a list of items and the ability to access a single item from the list.
The sidemenu is an a complicated example for beginner.
The simple example is to have only two pages with no tie of anything (no sidemenu, no tabs...).
.state('cats', {
url: '/cats',
templateUrl: 'templates/catlist.html',
controller: 'CatListCtrl'
})
.state('cat', {
url: '/cats/:catId',
templateUrl: 'templates/catsingle.html',
controller: 'CatSingleCtrl'
})
About templates/catlist.html
<ion-view view-title="Cat Collection">
<ion-content>
<ion-list>
<ion-item ng-repeat="cat in cats" href="#/cats/{{cat.id}}">
{{cat.name}}
</ion-item>
</ion-list>
</ion-content>
</ion-view>
and templates/catsingle.html
<ion-view view-title="Cat">
<ion-content>
<h1>{{cat.name}}</h1>
<p>{{cat.description}}</p>
</ion-content>
</ion-view>
The point of accessing a single item from a list, is to specify the direction to the resource, in this case, the direction is via URL, #/cats/{{cat.id}}. So if the cat has id = 5 then the URL will be #/cats/5; ok cool, let's access the single page that displays information of the cat with ID is 5. That's how you interpret it in reality.
My suggestion for learning Ionic,
First, get to know basic AngularJS, https://angularjs.org/
Second, get in touch with Angular UI Router navigation system, http://angular-ui.github.io/ui-router/
Third, practice to create with very very small Ionic apps, make a complicated Hello Ionic.
Guess that my answer is what you're looking for.
The Playlists MD works all the way, but my own MD doesn't. I do get
the Master list with items (controller and services work fine), but
when I click an item in the list, nothing happens. Then when I swipe
back (this should open the sidemenu), the detail page 'sort of' shows
up without css or data. So somewhere there's a glitch. I just don't
seem to understand the whole state/url thing, while I read enough
documentation..
I can see that you read, but do you ask yourself if you know the stuff required for it?
On front page of Ionic framework, I can see AngularJS, I can see ngCordova, I also see SASS (which in case, you will ask the same questions as how your custom CSS won't work and reload)...Ooops, lots of pieces to know before getting your hand in Ionic, I guess.
One thing worth to point out that if you're not coming from Angular background, you might not know about the navigation system in Ionic since the Ionic docs don't tell developers about Angular UI Router, you only get to know about it from the Ionic Learn page, which looks more advanced for beginners.
I know, it is hard; I was in your situation before, I felt the same way.
Anyway, have fun learning Ionic :)
#ekussberg:
My final setup included:
.state('app.playlists', {
url: '/playlists',
templateUrl: 'templates/playlists.html',
controller: 'PlaylistsCtrl'
})
.state('app.playlist', {
url: '/playlists/:playlistId',
templateUrl: 'templates/playlist.html',
controller: 'PlaylistCtrl'
});
Notice how the playlist state is called and the url differs from yours.
#Cake:
i have already tried like this and it works, but the transition on iOS and Web are not same smooth master-detaul as in the default way. I think it depends on the "views" element of the routing.
This code for example works, but transition not:
.state('app.todo', {
url: '/todo',
views: {
'menuContent': {
templateUrl: 'templates/todo-list.html',
controller: 'TodoListCtrl'
}
}
})
.state('app.todo.single', {
url: '/:todoId',
views: {
'menuContent#app': {
templateUrl: 'templates/todo-single.html',
controller: 'TodoSingleCtrl'
}
}
})
Related
I'm using $state.go() other places in my app but it's not working in a simple function and I can't work out why. It's not showing an error or showing the right state called in $state.go().
I've tested it with $stateChangeStart, $stateChangeError, $viewContentLoading & $stateChangeSuccess. It reaches all the correct stages: $stateChangeStart, $viewContentLoading then $stateChangeSuccess with the right content in each however, doesn't load the page.
I originally tried it with ui-sref and that didn't work so I tried it with $state.go() and now that isn't working I'm really confused. I can't see any difference between this code and what I have in other places where $state.go() works.
HTML link:
<li ng-repeat="thing in things" class="item" ng-click="showThingDetails()">
Code in controller:
$scope.showThingDetails = function () {
$state.go('thingDetails');}
Code in state:
.state('thingDetails', {
url: '/thingDetails',
views: {
'menuContent': {
templateUrl: 'templates/thingDetails.html',
controller: 'thingDetails'
}
}
})
thingDetails.html
<ion-view view-title="Thing Details">
<ion-content>
<h1>{{title}}</h1>
</ion-content>
</ion-view>
thingDetails.js
angular.module('controllers')
.controller('thingDetails', function($scope, $stateParams) {
$scope.title = "thing";
});
I've just found the answer to my own problem. I had copied the state from another place in my app and defining the templateUrl and controller in the views object was causing it to play up. I took templateUrl and controller out of the views object and it worked.
Code in state is now as below and works:
.state('sweepstakeDetails', {
url: '/sweepstakeDetails',
templateUrl: 'templates/sweepstakeDetails.html',
controller: 'sweepstakeDetails'
})
If anyone can add a more detailed answer as to why then I'd appreciate it.
Do you have an ui-view called 'menuContent' where the view should be loaded?
Else you could try to use this state without the menuContent views
.state('thingDetails', {
url: '/thingDetails',
templateUrl: 'templates/thingDetails.html',
controller: 'thingDetails'
})
Is it possible to link from one tab to a nested view in another tab? I've tried two different methods and neither seem to work.
Here's my router config:
.config(function($stateProvider, $urlRouterProvider) {
// Ionic uses AngularUI Router which uses the concept of states
// Learn more here: https://github.com/angular-ui/ui-router
// Set up the various states which the app can be in.
// Each state's controller can be found in controllers.js
$stateProvider
// setup an abstract state for the tabs directive
.state('tab', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html'
})
// Each tab has its own nav history stack:
.state('tab.dash', {
url: '/dash',
views: {
'tab-dash': {
templateUrl: 'templates/tab-dash.html',
controller: 'DashCtrl'
}
}
})
.state('tab.chats', {
url: '/chats',
views: {
'tab-chats': {
templateUrl: 'templates/tab-chats.html',
controller: 'ChatsCtrl'
}
}
})
.state('tab.chat-detail', {
url: '/chats/:chatId',
views: {
'tab-chats': {
templateUrl: 'templates/chat-detail.html',
controller: 'ChatDetailCtrl'
}
}
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/tab/dash');
});
In templates/tab-dash.html I have a link to a particular chat detail page:
<ion-view view-title="Dashboard">
<ion-content class="padding">
<h1>My Chats</h1>
Chat #1
</ion-content>
</ion-view>
From here I can navigate the chat detail page, however if I click the "Chats" tab button at the bottom nothing at all happens. I'd like it to bring me to the main Chats page.
Another method I tried was using ui-sref instead of href:
<ion-view view-title="Dashboard">
<ion-content class="padding">
<h1>My Chats</h1>
<a ui-sref="tab.chat-detail{chatId:1}">Chat #1</a>
</ion-content>
</ion-view>
In this case I receive an error Could not resolve 'tab.chat-detail{chatId:1}' from state 'tab.dash'
What's the best way to link to a "detail" view from within another tab? My main is to make sure that clicking the tab button at the bottom always brings me to the parent page for that tab. Right now, in the first example, it gets stuck on the "detail" view.
I got a workaround. I am hiding the tab bar when navigating to nested view of another tab.
And showing it again when user navigates back to the root tab.
To know how to hide and show tab bar programmatically, check this link:-
https://stackoverflow.com/a/45002644/4527624
I am working with angular and using ui router. The structure of my views are something like this:
<div ui-view>
<div ui-view="left">
-- list of items
</div>
<div ui-view="right">
-- details of item
</div>
</div>
Now if i select an item from left panel, an api is called to get the details of that item. Now want to load that information in right view.
All the view states have their own controller. Can someone please help me to pass the returned data from api tosecond view?
This is a pretty straightforward and common use case with ui-router. The main concept is of nested states and multiple named-views.
I would set this up with some states that look something like:
$stateProvider
.state('user', {
url: '/user',
views: {
'left': {
templateUrl: '/templates/states/user-list.tpl.html'
},
'right': {
template: '<p>This is default content that would appear in the right view until a user is selected. This is nice, but is completely optional.</p>'
}
}
})
.state('user.details', {
url: '/:userId',
views: {
'right': {
templateUrl: '/templates/states/user-details.tpl.html'
}
}
});
In the user-list template, you would use links something like:
<a ui-sref="user.details({userId: user.id})">{{user.name}}</a>
Because user.details is a child state of user, the user-list stays loaded into the left view while navigating through different users in the right view.
I have a AngularJs controller in Ionic Framework.
.controller('LocationDetailCtrl', ['$scope','$cordovaGeolocation','$cordovaCamera', '$cordovaFile','$stateParams','Location', LocationDetailCtrl]);
function LocationDetailCtrl ($scope, $cordovaGeolocation, $cordovaCamera, $cordovaFile, $stateParams, Location) {
$scope.locationRow = {};
$scope.test = "";
$scope.images = [];
Location.getById($stateParams.locationId).then(function(result){
//alert("I'm in");
$scope.locationRow = result;
});
}
I have code in view somewhere that does this:
<ion-item class="item-remove-animate item-icon-right" ng-repeat="location in locations" type="item-text-wrap" href="#/locations/{{location.id}}/all">
<h2>{{ location.aplicant_name}}</h2>
<p>{{ location.form_type }}</p>
<i class="icon ion-chevron-right icon-accessory"></i>
<ion-option-button class="button-assertive" ng-click="remove(location)" translate>Delete</ion-option-button>
</ion-item>
In my stateprovider I have this:
.state('location-detail', {
url: '/locations/{locationId}',
abstract: true,
templateUrl: 'templates/location-detail.html',
controller: 'LocationDetailCtrl'
})
.state('location-detail.all', {
url: '/all',
views: {
'loc-detail-view': {
templateUrl: 'templates/location/location-map-all.html'
}
}
})
My problem is, on the first href click I get the values for database, its all alright. But when I go back and press another list time, I would get the same value I got earlier.
Turns out Location.getById() is not being called the second time around.
Never-mind, I found the answer.
Turns out my controller is being cached by default.
I modified the state provider with this code and it now refreshes the view with new model.
.state('location-detail', {
url: '/locations/{locationId}',
cache: false,
abstract: true,
templateUrl: 'templates/location-detail.html',
controller: 'LocationDetailCtrl'
})
The difference here is cache:false.
Cheers!
Ionic views are cached by default, However you can manually set the cache to false in the view, this will make the controller to load again.
read more here, How ever what you have done is also correct, But I personally prefer the method I mentioned here as it will give more control
If you want to keep your page cached for any reason you could wrap all of your function you need to run inside of another funciton and then on the event $ionicView.beforeEnter or afterEnter or enter, you can call that function. Then you can keep the page cached and still have all of your functions run everytime the page is entered. For example in an app i made I did not want to have the homepage uncached, but i need some funcitons to pull
fresh data everytime the page is entered. So I did this:
$scope.$on('$ionicView.beforeEnter', function () {
$scope.doRefresh();
});
That way the page can stay cached but my app still behaves like I want it to. Take a look at some more of the ionicView methods: http://ionicframework.com/docs/api/directive/ionView/
I looked online for a solution but I couldn't find one that suites my needs. Tried this and looked into this with no help.
My case is, I designed a mobile app with AngularJS and the app has a navigation bar. Every page has this navigation bar because of that, I decided to move the code to a directive. The navigation bar has a button inside of it and this button will be used to force a refresh on the app's data (similar to facebook's pull to refresh). The navigation bar is displayed in all pages and whenever the user are, if he click's on the button the app has to execute an update and redirects to a certain page to display the results.
The problem is, since the navigation is inside of a directive and the user can click on any screen how do I manage capturing the click on the button, calling the refresh function and redirecting to a page?
Here is my directive and one of the pages I used it.
myAppDirectives.directive('headerMenu', function() {
return {
restrict: 'A',
templateUrl: 'partials/templates/header-menu.html'
};
});
<div class="topcoat-navigation-bar">
<div class="topcoat-navigation-bar__item left quarter">
<a class="topcoat-icon-button--quiet" snap-toggle>
<span class="topcoat-icon icon-menu-stack"></span>
</a>
</div>
<div class="topcoat-navigation-bar__item right three-quarters">
<a class="topcoat-icon-button--quiet" href="" > <!-- THIS IS THE REFRESH BUTTON -->
<span class="topcoat-icon icon-refresh"></span>
</a>
</div>
Page example:
<div header-menu></div>
<div> MAIN CONTENT </div>
Thank you!
Update 2: I do appreciate the answers about using ui-router. Since my project is already running and almost ready, structural changes like that are not in question. The accepted answer was the one that solved my problem although the others might fit better other people.
Just put a ngClick on this button:
<a class="topcoat-icon-button--quiet" ng-click="redirect()">
And provide a redirect function on the directive's scope:
myAppDirectives.directive('headerMenu', function() {
return {
scope:{},
restrict: 'A',
templateUrl: 'partials/templates/header-menu.html',
link: function(scope){
scope.redirect = function(){
// redirect to...
}
}
};
});
When I need to do this, I use ui-router, and create a view hierarchy that keeps the navigation bar loaded for every page. That way I can have for example, a navbar.html partial, and a state setup like the following:
$stateProvider
.state('header', url: '', templateUrl: 'header.html', controller: 'HeaderCtrl' })
.state('header.page', url: '/somepage', templateUrl: 'page.html', controller: 'PageCtr' })
.state( ...
I agree with Matt Way, ui-router is great solution for syncing nav bars. Although, for my projects I've been using Multiple Named Views config with ui-router, and your able to get real-time sync between main app interactions and the nav bar, negating the need for "refresh buttons".
In addition, if the user wants to return to the previous page, ui-router will maintain app "state", forms/UX/etc will still be as before.
Have a look at the ui-router wiki https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views.
Basic demo with header sync http://plnkr.co/edit/kYPGZw?p=preview
Basic multi view config (viewC is Nav):
$stateProvider
.state('state1', {
url: "/",
views: {
"viewA": {
template: "1st Tab, index.viewA"
},
"viewB": {
template: '1st Tab, index.viewB<br><a ui-sref=".list">Show List</a>' +
'<div ui-view="viewB.list"></div>'
},
"viewC": {
template: '1st Tab, index.viewC <div ui-view="viewC.list"></div>'
}
}
})
.state('state1.list', {
url: 'list',
views: {
"viewB.list": {
template: '<h2>Nest list viewB</h2><ul>' +
'<li ng-repeat="thing in tab1things">{{thing}}</li></ul>',
controller: 'Tab1ViewBCtrl',
data: {}
},
"viewC.list": {
template: '1st Tab, list.viewC'
}
}
})