I have an app with a tabbed interface, which consists of three tabs. When the user takes certain actions on tab1, it dynamically creates elements that should appear on the second tab. The problem is when I try to dynamically create that content within my javascript, I'm getting cannot set attribute of "null", which I'm assuming is because the second tab isn't loaded into the dom yet. If I first navigate to the second tab, then back to the first tab, it works fine. I'm not really using angular the way it's normally probably used. Everything is done in javascript. I just have a barebones app.js for the underlying UI.
Essentially, I just want to have all three tabs loaded into the DOM on app startup so I can dynamically modify the contents of any tab from any other tab. Not sure how to do this. Here's what my app.js looks like:
var myApp = angular.module('starter', ['ionic','ngCordova'])
myApp.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
$cordovaStatusbar.overlaysWebView(true)
$cordovaStatusbar.styleHex('#4a87ee')
$ionicConfigProvider.views.forwardCache(true);
});
});
myApp.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tabs', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
})
.state('tabs.home', {
url: "/home",
views: {
'home-tab': {
templateUrl: "templates/home.html"
}
}
})
.state('tabs.favorites', {
url: "/favorites",
views: {
'favorites-tab': {
template: "templates/favorites.html",
controller: 'AppCtrl'
}
}
})
.state('tabs.settings', {
url: "/settings",
views: {
'settings-tab': {
templateUrl: "templates/settings.html"
}
}
});
$urlRouterProvider.otherwise("/tab/home");
})
myApp.controller('AppCtrl', function($scope) {
})
And my basic app structure/html looks like this:
<ion-nav-bar class="bar-positive"></ion-nav-bar>
<ion-nav-view></ion-nav-view>
<script id="templates/tabs.html" type="text/ng-template">
<ion-tabs class="tabs-positive tabs-icon-only">
<ion-tab title="Videos" icon-on="ion-ios-home" icon-off="ion-ios-home-outline" href="#/tab/home">
<ion-nav-view name="home-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="Favorites" icon-on="ion-ios-star" icon-off="ion-ios-star-outline" href="#/tab/favorites">
<ion-nav-view name="favorites-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="Settings" icon-on="ion-ios-gear" icon-off="ion-ios-gear-outline" href="#/tab/settings">
<ion-nav-view name="settings-tab"></ion-nav-view>
</ion-tab>
</ion-tabs>
</script>
<!-- **************************** HOME TAB **************************** -->
<script id="templates/home.html" type="text/ng-template">
<ion-view view-title="Lmao!Tube">
<ion-content has-bouncing="true" overflow-scroll="true" class="iframe-wrapper">
<div id="videoList"> </div>
</ion-content>
</ion-view>
</script>
<!-- **************************** FAVORITES TAB **************************** -->
<script id="templates/favorites.html" type="text/ng-template">
<ion-view view-title="Favorites" >
<ion-content has-bouncing="true" overflow-scroll="true" class="iframe-wrapper">
<div id="favoriteList"></div>
</ion-content>
</ion-view>
</script>
<!-- **************************** SETTINGS TAB **************************** -->
<script id="templates/settings.html" type="text/ng-template">
<ion-view view-title="Settings">
<ion-content>
</ion-content>
</ion-view>
</script>
Actually after rereading your question, you want to do it a different way. Once you click on the tab, it is placed on the stack and the content is rendered, that's why it only works when you push back and forward. What would be a better way is utilizing AngularJS and the application framework by saving changes like boolean values in a Service. A service is an object instantiated only once to hold information and pass it around from controller to controller. You can use the service to hold values that the controller dynamically creates content from, and in your templates you can use AngularJS's directives like ng-show and ng-if etc...
For instance in services.js you could have:
angular.module('starter.services', [])
.service('MyService', function() {
var Stuff = {};
this.set = function(key, value) {
Stuff[key] = value;
};
this.get = function(key) {
return Stuff[key];
}
});
And in your home controller
.controller('HomeCtrl', function(MyService, $scope) {
MyService.set('home', true);
})
And in your favorites controller
.controller('FavCtrl', function(MyService, $scope) {
if (MyService.get('home') === true) {
// set a variable or return list or query or whatever you want
// lets say it's an array
$scope.list = [1,2,3];
} else {
$scope.list = [4,5,6];
}
})
And in your template you could do something like:
<div ng-repeat="i in list">
{{i}}
</div>
And that should print out 1 2 3 if they clicked home tab, and 4 5 6 if not.
If you're new to angular go here: http://campus.codeschool.com/courses/shaping-up-with-angular-js/intro?utm_source=google&utm_medium=cpc&utm_campaign=course&gclid=CjwKEAjw9bKpBRD-geiF8OHz4EcSJACO4O7TsJ3Mx9m9DOH47-6rmFshzUkkkHzJFhJcNiPl1it9JRoCN_fw_wcB
Pretty nifty course that gets you knowing some things about AngularJS pretty fast to work with Ionic. Ionic is a great framework.
Related
I have a Ionic app based on the tabs template. The general navigation structure of my app is:
-- users (/users)
-- user (/users/[id])
-- todos (/todos)
-- todo (/users/[id])
-- settings (/settings)
There are some nested views there (user and todo). They appear in the same nav view, like so:
<ion-tabs class="tabs-icon-only tabs-color-assertive">
<ion-tab icon="ion-ios-pulse-strong" href="#/tab/users">
<ion-nav-view name="users-tab"></ion-nav-view>
</ion-tab>
<ion-tab icon="ion-ios-pulse-strong" href="#/tab/todos">
<ion-nav-view name="todos-tab"></ion-nav-view>
</ion-tab>
<ion-tab icon="ion-ios-pulse-strong" href="#/tab/settings">
<ion-nav-view name="settings-tab"></ion-nav-view>
</ion-tab>
</ion-tabs>
With states being defined thusly:
.state('tabs', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html'
})
.state('users', {
url: '/users',
views: {
'users-tab': {
templateUrl: 'templates/tab-users.html',
controller: 'UsersCtrl'
}
}
})
.state('tabs.user', {
url: '/users/:userId',
views: {
'users-tab': {
templateUrl: 'templates/user-detail.html',
controller: 'UserCtrl'
}
}
})
.state('todos', {
url: '/todos',
views: {
'todos-tab': {
templateUrl: 'templates/tab-todos.html',
controller: 'TodosCtrl'
}
}
})
.state('tabs.todo', {
url: '/todos/:todoId',
views: {
'todos-tab': {
templateUrl: 'templates/todo-detail.html',
controller: 'TodoCtrl'
}
}
})
.state('tab.settings', {
url: '/settings',
views: {
'settings-tab': {
templateUrl: 'templates/tab-settings.html',
controller: 'SettingsCtrl'
}
}
})
In my index.html file, I have the following:
<ion-nav-bar class="bar-light">
<ion-nav-back-button></ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
I make use of the ion-nav-back-button to handle de master-detail views users and todos. This works completely automatically and Ionic handles this very well. Except for one case. When I refer to a detail view of a todo (e.g. /todos/3) from within another view (e.g. users/1), the back button does not appear, and there is no way to go back to the todos overview (/todos). A click on the tab button has no effect but going to /todos/3).
I guess this is expected behavior, but is there any way to show the back button anyway, or to make the tab button go to the todos view (/todos) at all times?
I found a solution. It's not ideal, but it works.
Instead of going directly to the detail state:
$state.go('tabs.todos', {
id: 1
});
I first go to the master state, and in the promise I go to the detail state:
$state.go('tabs.todos').then(function() {
setTimeout(function() {
$state.go('tabs.todos', {
id: 1
});
}, 100);
});
The timeout is not strictly necessary, but I've found that sometimes it fails without it.
I'm created one side menu project. It has 3 HTML files: firstPage.html contains button NEXT (go to secondPage.html). Same as secondPage.html that contains button NEXT (go to thirdPage.html) and for thirdPage.html NEXT button (go to firstPage.html). Every html has their own controller.
I followed this link in handling my back button action for Android device. It worked but for already registered back button didn't destroy after I changed my state.
Based on my codes below, the result was when I clicked android back button at firstPage.html, its prompt the alert. Then I clicked NEXT button in firstPage.html then directly go to secondPage.html. When I clicked android back button in that page, its also prompt me the alert. Eventhough the 'TheSecondController' didn't have '$ionicPlatform.registerBackButtonAction'. Same with thirdPage.html.
Here is my code:
Javascript: app.js
.state('app.firstPage', {
url: '/firstPage',
views: {
'menuContent': {
templateUrl: 'templates/firstPage.html',
controller: 'TheFirstController'
}
}
})
.state('app.secondPage', {
url: '/secondPage',
views: {
'menuContent': {
templateUrl: 'templates/secondPage.html',
controller: 'TheSecondController'
}
}
})
.state('app.thirdPage', {
url: '/thirdPage',
views: {
'menuContent': {
templateUrl: 'templates/thirdPage.html',
controller: 'TheThirdController'
}
}
})
HTML: firstPage.html
<ion-view view-title="First">
<ion-content>
<h1>First</h1>
<button class="button button-block button-positive" ui-sref="app.secondPage">Next</button>
</ion-content>
</ion-view>
HTML: secondPage.html
<ion-view view-title="Second">
<ion-content>
<h1>Second</h1>
<button class="button button-block button-positive" ui-sref="app.thirdPage">Next</button>
</ion-content>
</ion-view>
HTML: thirdPage.html
<ion-view view-title="Third">
<ion-content>
<h1>Third</h1>
<button class="button button-block button-positive" ui-sref="app.firstPage">Next</button>
</ion-content>
</ion-view>
Javascript: TheFirstController.js
.controller('TheFirstController', function($scope, $ionicPlatform, $ionicPopup) {
var deregisterFirst = $ionicPlatform.registerBackButtonAction(
function() {
if (true) { // your check here
$ionicPopup.confirm({
title: 'System warning',
template: 'Are you sure you want to exit?'
}).then(function(res) {
if (res) {
ionic.Platform.exitApp();
}
})
}
}, 100
);
$scope.$on('$destroy', deregisterFirst);
})
Javascript: TheSecondController.js
.controller('TheSecondController', function() {
//Allow user to go to previous page
})
Javascript: TheThirdController.js
.controller('TheThirdController', function() {
//Allow user to go to previous page
});
Please anyone help me. Thank you.
The $ionicPlatform.registerBackButtonAction() function registers the action for the whole Application, not specific to a page. $ionicPlatform is a service created by Ionic so it is a singleton (as all angular services are) therefore there is only one instance of it in the app.
So when you set the registerBackButtonAction in one controller it sets it for the whole app.
If you want it to exit only if you're on the first app, you have to add an if() on the state name.
Something like
if($state.current.name === 'app.firstPage'){
//your code here
}
The same way the guide you followed did it.
I am developing and app in Ionic and I need that after you click in button it takes you to another template. This template is called area.html and is not in the sidemenu, I added as a state of it in app.js . Here you have some code:
app.js
.state('app.area', {
url: "/area",
views: {
'menuContent': {
templateUrl: "templates/area.html",
controller: 'AreaCtrl'
}
}
});
firsttemplate.html
<ion-view view-title="First Template">
<ion-content class="background">
<button class="button button-positive"
ng-click="changeToArea()">Current Position</button>
</ion-content>
</ion-view>
controller.js
.controller('FirstTempCtrl', function ($scope){
$scope.changeToArea = function() {
console.log("Area Func");
window.location = "#/app/area.html";
})
The thing is that when you click in the button it take you to another template, to the :
$urlRouterProvider.otherwise('/app/log');
in the app.js and what I want to load is area.html
window.location = "#/app/area.html";
will reload your app and therefore all controllers
you need to use ui-router with for exemple : $state.go('app.area');
i'm new in ionic ( just a little less in angularjs ) .
i'm trying to do a simple switch between two views:
HTML
<body ng-app="starter" animation="slide-left-right-ios7">
<!--
The nav bar that will be updated as we navigate between views.
-->
<ion-nav-bar class="bar-stable nav-title-slide-ios7">
<ion-nav-back-button class="button-icon icon ion-ios7-arrow-back">
Back
</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view name="intro"></ion-nav-view>
<ion-nav-view name="login"></ion-nav-view>
<ion-nav-view name="home"></ion-nav-view>
<ion-nav-view name="pizze"></ion-nav-view>
<ion-nav-view name="sponsor"></ion-nav-view>
<ion-nav-view name="scontrino"></ion-nav-view>
</body>
APP.js
.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
.state('intro', {
url: '/intro',
views: {
'intro': {
templateUrl: 'templates/intro.html',
controller: 'IntroCtrl'
}
}
})
.state('login', {
url: '/login',
views: {
'login': {
templateUrl: 'templates/login.html',
controller: 'LoginCtrl'
}
}
})
$urlRouterProvider.otherwise('/intro');
});
Controllers.js
angular.module('starter.controllers', [])
.controller('IntroCtrl', function($scope,$location) {
$location.url("/login");
})
.controller('LoginCtrl', function($scope,$location) {
})
Intro is shown correctly but when it tries to change location to "login.html" it says:
TypeError: Cannot read property 'querySelectorAll' of undefined
at cancelChildAnimations (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:30611:21)
at Object.leave (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:30176:11)
at Object.leave (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:38411:24)
at updateView (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:41540:31)
at eventHook (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:41501:17)
at Scope.$broadcast (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:21190:28)
at $state.transition.resolved.then.$state.transition (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:33975:22)
at wrappedCallback (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:19894:81)
at http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:19980:26
at Scope.$eval (http://localhost:8000/www/lib/ionic/js/ionic.bundle.js:20906:28)
What could be the problem???
Thanks!
Try this
angular.module('starter.controllers', [])
.controller('IntroCtrl', function($scope,$state) {
$state.transitionTo("login");
})
.controller('LoginCtrl', function($scope,$location) {
})
You are using the view names wrong.
In a state, the view's name
views: {
'_name_': {
}
is used for different navigation histories for different views.
Say you have two tabs, home and pizza, and you want both to have several pages, then the view name comes in handy.
For your example it is important to know, how you want the views to be used.
I set up an example for you, making the views accessible in tabs.
See here for that example: http://plnkr.co/edit/Yd5ehQd0wnwlPzP0KYnp?p=preview
I have a set of tabs in Ionic framework which show a list of movies:
<script id="tabs.html" type="text/ng-template">
<ion-tabs tabs-style="tabs-icon-top" tabs-type="tabs-positive">
<ion-tab title="Movies" icon="ion-film-marker" href="#/tab/movies">
<ion-nav-view name="movies-tab"></ion-nav-view>
</ion-tab>
</ion-tabs>
</script>
<script id="movies.html" type="text/ng-template">
<ion-view title="'Movies'">
<ion-content has-header="true" padding="false">
<div class="list">
<a ng-repeat="item in movies" href="#/tab/movies/{{item.id}}" class="item item-thumbnail-left item-text-wrap">
<img ng-src="{{ item.image }}">
<h2>{{ item.title }}</h2>
<h4>{{ item.desc }}</h4>
</a>
</div>
</ion-content>
</ion-view>
</script>
Each of the items in the list is linked to #/tab/movies/{{item.id}}, for example, #/tab/movies/27. My movies are defined in the controler
.controller('MyCtrl', function($scope) {
$scope.movies = [
{ id: 1, title: '12 Rounds', desc: 'Detective Danny Fisher discovers his girlfriend has been kidnapped by a ex-con tied to Fisher\'s past, and he\'ll have to successfully complete 12 challenges in order to secure her safe release.', image: ''}
];
My pages are routed as below:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tabs', {
url: "/tab",
abstract: true,
templateUrl: "tabs.html"
})
.state('tabs.movies', {
url: "/movies",
views: {
'movies-tab': {
templateUrl: "movies.html",
controller: 'MyCtrl'
}
}
})
$urlRouterProvider.otherwise("/tab/movies");
})
What I need to do now is when each item on the above list is clicked, it takes it to it's own page, #/tab/movies/{{item.id}}, where I can display things like item.title or item.image, along with a back button to go to the list.
In order to do this, from what I can tell, I need to create a blank ng-template with a placeholder for the information, and then some how pass this information to it when clicked, but I'm not sure how to do this.
In your stateProvider config, you need to add a placeholderFor for the movie id, something like:
.state('tabs.movies', {
url: "/movies/:id",
views: {
'movies-tab': {
templateUrl: "movies.html",
controller: 'MyCtrl'
}
}
})
see https://github.com/angular-ui/ui-router/wiki/URL-Routing#stateparams-service
What are you looking for is probably service/factory where you can store list of movies and then retrieve full list for MyCtrl or just a single movie object for movie page.
angular.module('myAppName')
.factory('MovieService', function () {
return {
MoviesList: [
{movie object}
],
GetAllMovies: function () {
return this.MoviesList;
},
GetMovieById: function (id) {
//iterate MoviesList and return proper movie
}
}
}
that service can be then injected into your controllers
.controller('MyCtrl', function($scope, MoviesService) {
$scope.movies = MoviesService.GetAllMovies();
}
and same goes for a movie view controller:
.controller('ShowMyMovie', function($scope, MoviesService) {
$scope.movie = MoviesService.GetMovieById(//retrieve_id_from_routing_service);
}
then in template for this view you can simply use {{movie.title}} to display informations