Angular states and Browser back button - angular-routing

I am trying to adapt the following code:
Plunker
Here is the code:
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<title>AngularJS: UI-Router Quick Start</title>
<!-- Bootstrap CSS -->
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter- bootstrap/2.3.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" ui-sref="index">Quick Start</a>
<ul class="nav">
<li><a ui-sref="index">Home</a></li>
<li><a ui-sref="route1">Route 1</a></li>
<li><a ui-sref="route2">Route 2</a></li>
</ul>
</div>
</div>
<div class="row">
<div class="span6">
<div class="well" ui-view="LeftMenu"></div>
</div>
<div class="span6">
<div class="well" ui-view="Content"></div>
</div>
</div>
<!-- Angular -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.min.js"></script>
<!-- UI-Router -->
<script src="//angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
<!-- App Script -->
<script>
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider){
$stateProvider
.state('index', {
url: "",
views: {
"LeftMenu": {
template: '<ul><li><a ui-sref="index.LeftMenuMenu1">Index-Left Menu1</a></li><li><a ui-sref="index.LeftMenuMenu2">Index-Left Menu2</a></li><li><a ui-sref="index.LeftMenuMenu3">Index-Left Menu3</a></li></ul>'
},
"Content": {
template: "<div ui-view></div>"
}
}
})
.state('index.LeftMenuMenu1', {
template: "LeftMenu.Menu1 selected"
})
.state('index.LeftMenuMenu2', {
template: "LeftMenu.Menu2 selected"
})
.state('index.LeftMenuMenu3', {
template: "LeftMenu.Menu3 selected"
})
.state('route1', {
url: "/route1",
views: {
"LeftMenu": {
template: '<ul><li><a ui-sref="Route1.Menu1">Route1-Left Menu1</a></li><li><a ui-sref="Route1.Menu2">Route1-Left Menu2</a></li><li><a ui-sref="Route1.Menu3">Route1-Left Menu3</a></li></ul>'
},
"viewB": {
template: "route1.viewB"
}
}
})
.state('route2', {
url: "/route2",
views: {
"LeftMenu": {
template:'<ul><li>Route2-Left Menu1</li><li>Route2-Left Menu2</li><li>Route2-Left Menu3</li></ul>'
},
"viewB": {
template: "route2.viewB"
}
}
})
});
</script>
It definitely works fine. What I'd like to have is when I click on Back button it doesn't go back to the previous vertical state if they were selected, but instead goes back to the previous state on the horizontal menu.
Is that possible?
Thanks

Related

How to use v-owl-carousel in Nuxt 3?

I find myself creating the plugin named "owl.client.js" and enter this part to add a component consuming owl-carousel.
import carousel from 'v-owl-carousel'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.component('carousel', carousel)
})
However when I call this component within a page example:"App.vue" and enter this script it renders me as if the carousel component does not exist, despite checking in vue-devtools. For reference I say that in the same way I called it in an application in Nuxt2 where everything worked.
<template>
<div>
Hi
<section class="expertos-section">
<client-only>
<carousel
:rewind="false"
:items="3"
:margin="20"
:nav="false"
:dots="false"
:responsive="{
0: { items: 1, nav: false,stagePadding:50 },
768: { items: 2, nav: false },
992: { items: 3, nav: false },
}"
>
<div v-for="item in 6" :key="item">
<div class="post__inside">
<figure class="post__imagen">
<img src="https://www.10wallpaper.com/wallpaper/medium/1205/twilight_blue_moon_mountains-HD_Space_Wallpapers_medium.jpg" alt=" " />
</figure>
</div>
<div class="post__info">
<strong>Testeado</strong>
<h3 class="event-title">Description de target</h3>
</div>
</div>
</carousel>
</client-only>
</section>
</div>
</template>
<script>
const carousel = () =>{ typeof window !== "undefined" ? import("v-owl-carousel") : null}
export default {
components:{
carousel
}
}
</script>

angularfire and facebook login getting Cannot read property 'onAuth' of undefined

Hi i found a sample of auth on routing with angularfire, i just change it to support the new firebase sdk v4 and still using angularfire v1.
this is the link of the piece of code i used (with ui-router) :
angularfire docs
now this is my app.js and index.html
var config = {
"apiKey": "AIzaSyAUoM0RYqF1-wHI_kYV_8LKgIwxmBEweZ8",
"authDomain": "clubears-156821.firebaseapp.com",
"databaseURL": "https://clubears-156821.firebaseio.com",
"projectId": "clubears-156821",
"storageBucket": "clubears-156821.appspot.com",
"messagingSenderId": "970903539685"
};
firebase.initializeApp(config);
var app = angular.module("sampleApp", [
"firebase",
"ui.router"
]);
app.factory("Auth", ["$firebaseAuth",
function ($firebaseAuth) {
return $firebaseAuth();
}
]);
// UI.ROUTER STUFF
app.run(["$rootScope", "$state", function ($rootScope, $state) {
$rootScope.$on("$stateChangeError", function (event, toState, toParams, fromState, fromParams, error) {
if (error === "AUTH_REQUIRED") {
$state.go("home");
}
});
}]);
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/home");
$stateProvider
.state('home', {
url: "/home",
template: "<h1>Home</h1><p>This is the Home page</p>",
resolve: {
"currentAuth": ["Auth", function (Auth) {
return Auth.$waitForAuth();
}]
}
})
.state('profile', {
url: "/profile",
template: "<h1>Profile</h1><p>This is the Profile page</p>",
resolve: {
"currentAuth": ["Auth", function (Auth) {
return Auth.$requireSignIn();
}]
}
})
});
app.controller("MainCtrl", ["$scope", "Auth",
function ($scope, Auth) {
$scope.auth = Auth;
console.log(Auth);
$scope.auth.$onAuth(function(authData) {
$scope.authData = authData;
console.log(authData);
});
}
]);
app.controller("NavCtrl", ["$scope", "Auth",
function ($scope, Auth) {
$scope.auth = Auth;
console.log(Auth);
$scope.auth.$onAuth(function(authData) {
$scope.authData = authData;
});
}
]);
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
</head>
<body>
<div ng-app="sampleApp">
<div ng-controller="MainCtrl">
<nav class="navbar navbar-default navbar-static-top" ng-controller="NavCtrl">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="home">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li ui-sref-active="active">
<a ui-sref="home" href="#">Home</a>
</li>
<li ui-sref-active="active" ng-show="authData">
<a ui-sref="profile" href="#">
My Profile
</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li ng-hide="authData">
<a href="#" ng-click="$parent.auth.$authWithOAuthPopup('facebook')">
<span class="fa fa-facebook-official"></span>
Sign In with Facebook
</a>
</li>
<li ng-show="authData">
<a href="#" ng-click="$parent.auth.$unauth()">
<span class="fa fa-sign-out"></span>
Logout
</a>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<div ui-view ng-show="authData"></div>
<div class="login-screen" ng-hide="authData">
<div class="jumbotron text-center">
<h1>Sweet login, brah.</h1>
<p class="lead">This is a pretty simple login utilizing AngularJS and AngularFire.</p>
<button class="btn btn-primary btn-lg" ng-click="auth.$authWithOAuthPopup('facebook')">
<span class="fa fa-facebook-official fa-fw"></span>
Sign in with Facebook
</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.0.0/firebase.js"></script>
<script src="app.js"></script>
<script src="https://cdn.firebase.com/libs/angularfire/1.1.3/angularfire.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"></script>
</body>
</html>
now the problem is that i getting an error : Cannot read property 'onAuth' of undefined.
i think its problem of the version of the new SDk and i looked in the proposed solution here in stackoverflow but none of them fix me the problem.
please help...
Ok i figure out the problem and fix it by changing the versions of angular and angularfire. and a little change to migrate to the new sdk.
this is new code.
my problem now is that i don't get all the scope that i want from facebook. for example i want birthday and i cannot see it comes back.
someone have a suggestion ?
var config = {
"apiKey": "AIzaSyAUoM0RYqF1-wHI_kYV_8LKgIwxmBEweZ8",
"authDomain": "clubears-156821.firebaseapp.com",
"databaseURL": "https://clubears-156821.firebaseio.com",
"projectId": "clubears-156821",
"storageBucket": "clubears-156821.appspot.com",
"messagingSenderId": "970903539685"
};
firebase.initializeApp(config);
var app = angular.module("sampleApp", [
"firebase",
"ui.router"
]);
app.factory("Auth", ["$firebaseAuth",
function ($firebaseAuth) {
return $firebaseAuth();
}
]);
//var provider = Auth.FacebookAuthProvider();
//provider.addScope('user_birthday');
//
//Auth.signInWithRedirect(provider);
// UI.ROUTER STUFF
app.run(["$rootScope", "$state", function ($rootScope, $state) {
$rootScope.$on("$stateChangeError", function (event, toState, toParams, fromState, fromParams, error) {
if (error === "AUTH_REQUIRED") {
$state.go("home");
}
});
}]);
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/home");
$stateProvider
.state('home', {
url: "/home",
template: "<h1>Home</h1><p>This is the Home page</p>",
resolve: {
"currentAuth": ["Auth", function (Auth) {
return Auth.$waitForSignIn();
}]
}
})
.state('profile', {
url: "/profile",
template: "<h1>Profile</h1><p>This is the Profile page</p>",
resolve: {
"currentAuth": ["Auth", function (Auth) {
return Auth.$requireSignIn();
}]
}
});
});
app.controller("MainCtrl", ["$scope", "Auth",
function ($scope, Auth) {
$scope.auth = Auth;
console.log(Auth);
$scope.auth.$onAuthStateChanged(function (authData) {
$scope.authData = authData;
console.log(authData);
});
}
]);
app.controller("NavCtrl", ["$scope", "Auth",
function ($scope, Auth) {
$scope.currentUser = null;
$scope.currentUserRef = null;
$scope.currentLocation = null;
$scope.auth = Auth;
console.log(Auth);
/**
* Function called when clicking the Login/Logout button.
*/
// [START buttoncallback]
$scope.SignIn = function () {
if (!Auth.currentUser) {
$scope.auth.$signInWithRedirect('facebook', {
scope: 'email, public_profile, user_birthday'
}).then(function (authData) {
// never come here handle in $onAuthStateChanged because using redirect method
}).catch(function (error) {
if (error.code === 'TRANSPORT_UNAVAILABLE') {
$scope.$signInWithPopup('facebook', {
scope: 'email, public_profile, user_friends'
}).catch(function (error) {
console.error('login error: ', error);
});
} else {
console.error('login error: ', error);
}
});
} else {
// [START signout]
Auth.signOut();
// [END signout]
}
};
// [END buttoncallback]
//
// $scope.updateUserData = function () {
// $scope.currentUserRef.set($scope.currentUser);
// };
$scope.auth.$onAuthStateChanged(function (authData) {
$scope.authData = authData;
console.log('after login');
console.log($scope.authData);
});
}
]);
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>
<div ng-app="sampleApp">
<div ng-controller="MainCtrl">
<nav class="navbar navbar-default navbar-static-top" ng-controller="NavCtrl">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="home">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li ui-sref-active="active">
<a ui-sref="home" href="#">Home</a>
</li>
<li ui-sref-active="active" ng-show="authData">
<a ui-sref="profile" href="#">
My Profile
</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li ng-hide="authData">
<a href="#" ng-click="SignIn()">
<span class="fa fa-facebook-official"></span>
Sign In with Facebook
</a>
</li>
<li ng-show="authData">
<a href="#" ng-click="SignIn()">
<span class="fa fa-sign-out"></span>
Logout
</a>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<div ui-view ng-show="authData"></div>
<div class="login-screen" ng-hide="authData">
<div class="jumbotron text-center">
<h1>Sweet login, brah.</h1>
<p class="lead">This is a pretty simple login utilizing AngularJS and AngularFire.</p>
<button class="btn btn-primary btn-lg" ng-click="SignIn()">
<span class="fa fa-facebook-official fa-fw"></span>
Sign in with Facebook
</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.0.0/firebase.js"></script>
<script src="app.js"></script>
<script src="https://cdn.firebase.com/libs/angularfire/2.3.0/angularfire.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/1.0.3/angular-ui-router.min.js"></script>
</body>
</html>

Child states not rendering with parent state in Ionic V-1

enter image description hereI'm new to Ionic v-1. I tried implementing navigation using parent-child relationship. The parent state is "tab" while the active child state is "home". When I load the app, the home state contents do not load.
angular.module('starter', ['ionic'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
if(window.cordova && window.cordova.plugins.Keyboard) {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
// Don't remove this line unless you know what you are doing. It stops the viewport
// from snapping when text inputs are focused. Ionic handles this internally for
// a much nicer keyboard experience.
cordova.plugins.Keyboard.disableScroll(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
.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",
controller: 'HomeTabCtrl'
}
}
})
.state('tabs.addFriend',{
url:"/addFriend",
views:{
'addFriend-tab':{
templateUrl:"templates/addFriend.html",
controller:'addFriendTabController'
}
}
})
.state('tabs.message',{
url:"/message",
views:{
'message-tab':{
templateUrl:"templates/message.html",
controller:'messageTabController'
}
}
})
.state('tabs.notifications',{
url:"/notifications",
views:{
'notifications-tab':{
templateUrl:"templates/notifications.html",
controller:'notificationsTabController'
}
}
})
.state('tabs.profile',{
url:"/profile",
views:{
'profile-tab':{
templateUrl:"templates/profile.html",
controller:'profileTabController'
}
}
});
$urlRouterProvider.otherwise("/tab/home");
})
.controller('HomeTabCtrl',function($scope){
console.log("Home tab");
})
.controller('addFriendTabController',function($scope){
console.log("addFriend tab");
})
.controller('messageTabController',function($scope){
console.log("message tab");
})
.controller('notificationsTabController',function($scope){
console.log("notifications tab");
})
.controller('profileTabController',function($scope){
console.log("profile tab");
});
<body ng-app="starter">
<ion-pane>
<div class="bar bar-positive item-input-inset headerBorder">
<label class="item-input-wrapper positive-bg" id="headerSearch">
<i class="icon ion-ios-search placeholder-icon searchIcon"></i>
<input type="search" placeholder="People, jobs, posts and more...">
</label>
<i class="icon ion-grid placeholder-icon searchIcon"></i>
</div>
<ion-nav-view></ion-nav-view>
<script id="templates/tabs.html" type="text/ng-template">
<div class="tabs-stripped tabs-top tabs-background-positive tabs-color-light">
<div class="tabs tab-top">
<a href="#/tab/home" class="tab-item active">
<i class="icon ion-home"></i>
</a>
<a href="#/tab/addFriend" class="tab-item">
<i class="icon ion-person-stalker"></i>
</a>
<a href="#/tab/message" class="tab-item">
<i class="icon ion-chatboxes"></i>
</a>
<a href="#/tab/notifications" class="tab-item">
<i class="icon ion-android-notifications"></i>
</a>
<a href="#/tab/profile" class="tab-item">
<i class="icon ion-person"></i>
</a>
</div>
</div>
</script>
<script id="templates/home.html" type="text/ng-template">
<ion-content class="has-tabs-top">
<div class="list card">
<div class="item item-thumbnail-left">
<img src="img/ionic.png">
<h2 class="listCss">Name</h2>
<h3>Followers</h3>
<p>Time</p>
</div>
<div class="item item-image">
<img src="img/NIKHIL.jpg">
</div>
Click here...
</div>
</ion-content>
</script>
<script id="templates/addFriend.html" type="text/ng-template">
</script>
<script id="templates/message.html" type="text/ng-template">
</script>
<script id="templates/notifications.html" type="text/ng-template">
</script>
<script id="templates/profile.html" type="text/ng-template">
</script>
</ion-pane>
</body>
if you want to use multiple named views you've to do like so for exmaple:
IN you HTML:
<ion-tabs class="tabs-icon-bottom tabs-color-light tabs-background-brown">
<ion-tab title="LAST SCAN" icon-off="fwd-last-scan-off" icon-on="fwd-last-scan-on" ui-sref="tab.lastscan">
<ion-nav-view name="tab-left"></ion-nav-view>
</ion-tab>
<ion-tab title="SCAN" icon-off="fwd-total-scan-off" icon-on="fwd-total-scan-on" ui-sref="tab.scan">
<ion-nav-view name="tab-center"></ion-nav-view>
</ion-tab>
<ion-tab title="MENU" icon-off="fwd-menu-off" icon-on="fwd-menu-on" ui-sref="tab.menu">
<ion-nav-view name="tab-right"></ion-nav-view>
</ion-tab>
<ion-tab title="EXTRA" icon-off="fwd-extra-off" icon-on="fwd-extra-on">
<ion-nav-view name="tab-off"></ion-nav-view>
</ion-tab>
</ion-tabs>
In your JS:
"use strict";
angular.module("gestione")
.config(["$stateProvider", "$urlRouterProvider", "$ionicConfigProvider", "$httpProvider", "$ionicNativeTransitionsProvider",
function ($stateProvider, $urlRouterProvider, $ionicConfigProvider, $httpProvider, $ionicNativeTransitionsProvider) {
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise("/tab/login");
$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.lastscan", {
url: "/lastscan",
cache: false,
nativeTransitions: {
"type": "filp",
"direction": "up"
},
views: {
'tab-left': {
templateUrl: "templates/tab-lastscan.html",
controller: "LastScanCtrl",
controllerAs: "lastscan"
}
}
})
.state("tab.menu", {
url: "/menu",
views: {
'tab-right': {
templateUrl: "templates/tab-menu.html",
controller: "MenuCtrl"
}
}
})
.state("tab.settings", {
url: "/settings",
cache: false,
views: {
'tab-off': {
templateUrl: "templates/settings.html",
controller: "SettingCtrl",
controllerAs: "settingCtrl"
}
}
})
;
}]);

Polymer conditional class derived from child component

I'm learning Polymer;
I can't get a conditional class name to appear in my tabs (parent) component. A 'active' class should be added to a <li> element depending on the 'selected' property of a child component.
I'm not really sure my way of communicating between parent and child component is right in the first place. It is working, but it doesn't feel right..
My index.html file
<link rel="import" href="components/tabs.html">
<link rel="import" href="components/tab.html">
<ikb-tabs>
<ikb-tab heading="Tab #1">
<p>Content of the first tab</p>
</ikb-tab>
<ikb-tab heading="Tab #2" selected>
<p>Content of the second tab</p>
</ikb-tab>
</ikb-tabs>
My components/tabs.html file
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module id="ikb-tabs">
<template>
<style>
.active button {
color: red;
}
</style>
<nav>
<ul>
<template is="dom-repeat" items="{{tabs}}">
<li>
<button on-tap="openTab">{{item.heading}}</button>
</li>
</template>
</ul>
</nav>
<content></content>
</template>
<script>
Polymer({
is: 'ikb-tabs',
properties: {
activeTab: Number
},
ready: function () {
this.tabs = Polymer.dom(this).children;
},
openTab: function (e) {
Polymer.dom(this).children.forEach(function (tab, index) {
tab.selected = index === e.model.index;
});
}
});
</script>
</dom-module>
My components/tab.html file
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module id="ikb-tab" attributes="heading">
<template>
<template is="dom-if" if="{{selected}}">
<div>
<content></content>
</div>
</template>
</template>
<script>
Polymer({
is: 'ikb-tab',
properties: {
heading: String,
selected: {
type: Boolean
}
}
});
</script>
</dom-module>
I've figured it out myself, there were two main issues with my code:
Updating an Array didn't trigger an update:
Only adding, removing are replacing items in an Array triggers an update. I changed a property of an Object inside an Array. Polymer only checks the reference to that Object, that reference remained unchanged. So no change got triggered. Solution: update the Array with the set function (see code below).
Computed classes need a special syntax:
My class="{{getClassName(item.selected}}" wasn't adding any classes. I now know the correct syntax is class$="{{getClassName(item.selected)}}".
More info: https://www.polymer-project.org/1.0/docs/devguide/data-binding#native-binding
The simplified working code:
<dom-module id="ikb-tabs">
<template>
<nav>
<ul>
<template is="dom-repeat" items="{{tabs}}">
<li class$="{{getClassName(item.selected)}}">
<button on-tap="openTab">{{item.heading}}</button>
</li>
</template>
</ul>
</nav>
<div>
<content></content>
</div>
</template>
<script>
Polymer({
is: 'ikb-tabs',
properties: {
tabs: {
type: Array
}
},
ready: function () {
this.tabs = Polymer.dom(this).children;
},
openTab: function (e) {
var self = this;
this.tabs.forEach(function (tab, index) {
self.set('tabs.' + index + '.selected', index === e.model.index)
});
},
getClassName: function (isSelected) {
return isSelected ? 'active' : null;
}
});
</script>
</dom-module>

Ionic : Navigate in nested states breaks history and ion-nav-back-button

It seems impossible to navigate to a child state of a sibling or to a child state of an ancestor.
The workaround I used was to put all the states on the same level, which allows me to navigate to any state I need (navigate from a push notification to a nested state, navigate from one nested state to a state inside another parent, etc ...).
The problem with that method is that states and controllers do not inherit any code, leading to code duplication. Moreover, there are cases where the navigation is simply broken and the ion-nav-back-button do not behave as it should.
TLTR: What structure must be used to have a fully navigable application (check out the pen), when you use tabs and nested states ?
Here is the pen describing the problem : http://codepen.io/ruslan-fidesio/pen/LkyAkm
HTML :
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link href="http://code.ionicframework.com/1.3.1/css/ionic.min.css" rel="stylesheet">
<script src="http://code.ionicframework.com/1.3.1/js/ionic.bundle.min.js"></script>
</head>
<body ng-app="app">
<ion-nav-view></ion-nav-view>
<script id="home.html" type="text/ng-template">
<ion-view view-title="Home">
<ion-content>
<br/>
<a ui-sref="app.tabs.themes.details">Go to Child : broken</a>
<br/>
<br/>
<a ui-sref="app.other">Go to Other : broken</a>
</ion-content>
</ion-view>
</script>
<script id="tabs.html" type="text/ng-template">
<ion-nav-bar class="bar-positive">
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
<ion-tabs class="tabs-positive">
<ion-tab title="home" ui-sref="app.tabs.home">
<ion-nav-view name="tabs-home"></ion-nav-view>
</ion-tab>
<ion-tab title="themes" ui-sref="app.tabs.themes.list">
<ion-nav-view name="tabs-themes"></ion-nav-view>
</ion-tab>
</ion-tabs>
</script>
<script id="themes/abstract.html" type="text/ng-template">
<div class="bar bar-subheader bar-dark" sticky>
Themes subheader
</div>
<ion-nav-view></ion-nav-view>
</script>
<script id="themes/list.html" type="text/ng-template">
<ion-view view-title="Themes">
<ion-content class="has-subheader">
<p>Parent View</p>
<a ui-sref="app.tabs.themes.details">Go to Child : OK !</a>
</ion-content>
</ion-view>
</script>
<script id="themes/details.html" type="text/ng-template">
<ion-view view-title="Theme X">
<ion-content class="has-subheader">
Child View
</ion-content>
</ion-view>
</script>
<script id="other.html" type="text/ng-template">
<ion-view view-title="Other">
<ion-nav-bar class="bar-positive">
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
<ion-content>
<br/>
Other View
<br/>
<a ui-sref="app.tabs.themes.details">Go to Child : broken</a>
</ion-content>
</ion-view>
</script>
</body>
</html>
JS :
var app = angular.module(
'app', [
'ionic'
]
);
app.run(
function($ionicPlatform, $rootScope) {
$ionicPlatform.ready(
function() {
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
StatusBar.styleDefault();
}
}
);
}
);
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
template: '<ion-nav-view></ion-nav-view>'
})
.state('app.tabs', {
url: '/tabs',
abstract: true,
templateUrl: 'tabs.html'
})
.state('app.tabs.home', {
url: '/home',
views: {
'tabs-home': {
templateUrl: 'home.html'
}
}
})
.state('app.other', {
url: '/other',
templateUrl: 'other.html'
})
.state('app.tabs.themes', {
url: '/themes',
abstract: true,
views: {
'tabs-themes': {
templateUrl: 'themes/abstract.html'
}
}
})
.state('app.tabs.themes.list', {
url: '/list',
templateUrl: 'themes/list.html'
})
.state('app.tabs.themes.details', {
url: '/details',
templateUrl: 'themes/details.html'
});
$urlRouterProvider.otherwise('/app/tabs/home');
});
app.config(
['$ionicConfigProvider', function($ionicConfigProvider) {
$ionicConfigProvider.tabs.position('bottom');
$ionicConfigProvider.navBar.alignTitle('center');
}]);
After some research it is related to IonTabs and separated ion-nav-views.
(check out this picture : http://ionicframework.com/img/diagrams/tabs-nav-stack.png )
In this case, it is better to replace tabs with custom "tabs" implementation using only one ion-nav-view as shown here : http://codepen.io/ruslan-fidesio/pen/RRgpjL
HTML :
<ion-nav-bar class="bar-positive">
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
<better-tabs style="positive">
<better-tab state="app.tabs.home" title="Home"></better-tab>
<better-tab state="app.tabs.themes.list" root-state="app.tabs.themes" title="Themes"></better-tab>
</better-tabs>
JS :
app.directive(
'betterTabs',
function() {
return {
restrict: 'E',
compile: function(elem, attrs) {
var footer = angular.element('<ion-footer-bar></ion-footer-bar>');
var tabs = elem.find('better-tab');
elem.append(footer);
footer.append(tabs);
if (attrs.style) {
footer.addClass('bar-' + attrs.style);
}
}
};
}
);
app.directive(
'betterTab',
['$state', function($state) {
return {
scope: {
state: '#',
rootState: '#',
title: '#'
},
restrict: 'E',
required: ['^betterTabs'],
link: function(scope) {
scope.$state = $state;
},
template: function() {
return '<a ui-sref="{{ state }}" ng-class="{active: $state.includes(\'{{ rootState ? rootState : state }}\')}">{{ title }}</a>';
}
};
}]
);