Controller being called twice in Ionic (AngularJS) - javascript

In my angular project the user accepts a EULA then get automatically redirected to their dashboard, however, on this redirect the DashboardController seems to be being called twice, the DashboardController is being called on the route itself, I have checked to see if I have accidently called it again in the template but I havn't. Below is my route & controller. It doesn't appear to matter if I access the URL directly or via the redirect on the EULA controller, I get the same result.
The routes
.config(function($httpProvider, $stateProvider, $urlRouterProvider) {
$httpProvider.interceptors.push('httpRequestInterceptor');
$urlRouterProvider.otherwise('/');
$stateProvider
.state('login', {
url: '/',
templateUrl: 'templates/login.html',
data: {
requireLogin: false
}
})
.state('eula', {
url: '/eula',
templateUrl: 'templates/eula.html',
data: {
requireLogin: true
}
})
.state('dashboard', {
url: '/groups',
templateUrl: 'templates/dashboard.html',
data: {
requireLogin: true
}
})
});
The controller:
App.controller('DashboardController', ['$scope', 'RequestService', '$state', '$rootScope', function($scope, RequestService, $state, $rootScope){
alert('test');
}]);
Any ideas?
ADDED MY HTML AS PER COMMENTS
index.html
<body ng-app="App">
<ion-nav-bar class="bar-positive nav-title-slide-ios7" align-title="center">
<ion-nav-back-button class="button-icon ion-arrow-left-c"></ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view class="slide-left-right"></ion-nav-view>
<ui-view></ui-view>
</body>
dashboard.html
<div class="groups" ng-controller="DashboardController">
<ion-view title="App">
<ion-nav-buttons side="right">
<a ui-sref="groupcreate"><span class="icon ion-ios-plus-outline"></span></a>
</ion-nav-buttons>
<ion-content class="padding">
<div class="row">
<div class="col-50" ng-repeat="group in groups">
{{ group }} 1
</div>
</div>
</ion-content>
</ion-view>
</div>

If you are using ui-router you don't have to use ng-controller. You have used it in your dashboard.html, another is generated by ui-router - that's why it is hit twice.

Ok so after a long time debugging and check stuff out, I found out that it was an issue relating to the Nav Bar in ionic, essentially, I was calling <ui-view></ui-view> & <ion-nav-view></ion-nav-view> on the same page, so basically doubling up on my views which in turn was calling the controller twice.

I know this has been answered already as well, but I wanted to add my fix for the exact same problem.
My controllers were also being called twice, but in my case I had to comment out the ng-controller settings in various files:
My config function in the main app.js
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('splash', {
url: "/",
templateUrl: "app/splash/splash.html"
// controller: 'SplashCtrl'
})
Since I was already calling it in the markup:
<ion-view view-title="TickerTags" ng-controller="SplashCtrl as splash">
<ion-content class="splash">
The controller key inside of my Directives
angular
.module('tagsPanelDirective', [])
.controller('TagsPanelCtrl', TagsPanelCtrl)
.directive('tagsPanel', tagsPanel);
function tagsPanel() {
var directive = {
templateUrl: "app/tags/tagsPanel.html",
restrict: "E",
replace: true,
bindToController: true,
// controller: 'TagsPanelCtrl as tagsPanel',
link: link,
scope: false
};
return directive;
function link(scope, element, attrs) {}
}
Again since I was already calling it from within the template markup:
<section class="tags-panel" ng-controller="TagsPanelCtrl as tagsPanel">

Related

AngularJS: UI Router doesn't load the inline template and controller

This is my first time using UI Router inside AngularJS project. I have a problem where when I click a link to view a post, it doesn't show up.
The post template is not showing and I'm still at the home page. I can see the URL flashing like http://localhost:8000/#/posts/1 and attempt to change, but, it goes back to http://localhost:8000/#/home.
Plunker: http://plnkr.co/edit/KV6lwzKUHrIZgVWVdrzt
What I am missing here?
Note 1: I already read UI Router documentation and I think I'm not missing anything.
Note 2: I'm following this tutorial (thinkster).
Note 3: I'm using SimpleHTTPServer python -m SimpleHTTPServer 8000 command to serve this project.
This is my app.js:
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl'
})
.state('posts', {
url: 'posts/:id',
templateUrl: '/posts.html',
controller: 'PostsCtrl'
});
$urlRouterProvider.otherwise('home');
}]);
...
app.controller("PostsCtrl", ["$scope", "$stateParams", "postsFactory", function($scope, $stateParams, postsFactory){
// grab the right post from postsFactory posts array
$scope.post = postsFactory.posts[$stateParams.id];
console.log($scope.post);
}]);
And this is my index.html:
<ui-view></ui-view>
...
<script type="text/ng-template" id="/posts.html">
<div class="page-header">
<h3>
<a ng-show="post.link" href="{{post.link}}">
{{post.title}}
</a>
<span ng-hide="post.link">
{{post.title}}
</span>
</h3>
</div>
</script>
Issue in your code is in your router config for posts state. It should be like below. URL should be /posts/:id instead of posts/:id.
$stateProvider
.state('posts', {
url: '/posts/:id',
templateUrl: '/posts.html',
controller: 'PostsCtrl'
});
You are basically missing / slash at the start of your posts state URL, because of / is missing it was redirecting to .otherwise rule of $urlRouterProvider
Code
.state('posts', {
url: '/posts/:id',
templateUrl: '/posts.html',
controller: 'PostsCtrl'
});
You also need to add $locationProvider.html5Mode(true); in the app.config block and <'base href="/"'> in the head section of your HTML file.
Otherwise, the #/posts/:id routing won't work.

Ionic framework ionicView.entered event fired twice

I'm playing with this ionicView events that are fired whenever the views become active, and I'm using the side menu template that you can re-use when creating the project. It seems that if I put a listener for the $ionicView.entered event in the AppCtrl (the one used by the side menu template, which belongs to an abstract state in the ui-router configuration) it gets called twice in a row for any of the subviews (like, when using app.someotherview as a state).
I don't know if this is the expected behavior because from the documentation I would expect it to only fire once, no matter if I change the subview (the menuContent view).
I'd like to know if this is expected behavior and if so, how do I get to have an event fired only once every time it gets to show the side menu template.
This is what I've got written:
This is from the application module:
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('app.overview', {
url: "/overview",
views: {
'menuContent': {
templateUrl: "templates/overview.html",
controller: 'OverviewCtrl'
}
}
})
.state('login', {
url: "/login",
templateUrl: "templates/identificationscreen.html",
controller: "IdentificationCtrl"
})
.state('app.agenda', {
url: "/agenda",
views: {
'menuContent': {
templateUrl: "templates/agenda.html",
controller: 'AgendaCtrl'
}
}
});
$httpProvider.interceptors.push('credentialsInjector');
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/login');
Then the AppCtrl is this:
angular.module('dashboard.controllers.app', [])
.controller('AppCtrl', function($scope, $ionicModal, $timeout, $ionicSideMenuDelegate, authService, $state) {
$scope.logout = function() {
authService.logout();
$state.go('login');
};
$scope.$on('$ionicView.enter', function(){ //This is fired twice in a row
console.log("App view (menu) entered.");
console.log(arguments);
});
$scope.$on('$ionicView.leave', function(){ //This just one when leaving, which happens when I logout
console.log("App view (menu) leaved.");
console.log(arguments);
});
});
The menu template:
<ion-side-menus enable-menu-with-back-views="false">
<ion-side-menu-content edge-drag-threshold="true">
<ion-nav-bar class="bar-stable">
<ion-nav-back-button>
</ion-nav-back-button>
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" menu-toggle="left">
</button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-nav-view name="menuContent"></ion-nav-view>
</ion-side-menu-content>
<ion-side-menu side="left">
<ion-header-bar class="bar-stable">
<h1 class="title">APPoint!</h1>
</ion-header-bar>
<ion-content>
<ion-list>
<ion-item nav-clear menu-close href="#/app/overview">
Overview
</ion-item>
<ion-item nav-clear menu-close href="#/app/agenda">
Agenda
</ion-item>
<ion-item nav-clear menu-close ng-click="logout()">
Logout
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
</ion-side-menus>
A simple workaround is:
$scope.$on('$ionicView.enter', function(ev) {
if(ev.targetScope !== $scope)
return;
// Your code which should only run once
});
The best option that I found to simulate onEnter event was to use in view controller :
$scope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
if (toState.name == "stateName")
doSomething();
}
You can globally disable the caching mechanism used by ionic by doing:
$ionicConfigProvider.views.maxCache(0);
I haven't tried that on my own though.
Else, the best way that worked for me was doing
$scope.$on("$ionicView.afterLeave", function () {
$ionicHistory.clearCache();
});
This is to clear the cache before leaving the view to re-run controller every time you enter back again.
Try $ionicView.afterEnter. That fires only once for me.
URL
At first I used Florian's answer but noticed that in my case it's just a symptom of a root problem, because this doesn't happen to all controllers, just this one.
In my case, it happens because I was trying to incrementally migrate from Ionic/AngularJS v1 to Ionic/AngularJS v2.
I have added controllerAs in my template:
<ion-view view-title="Avatar ยป Instruments" ng-controller="AvatarInstrumentsCtrl as vm">
but I forgot to remove the controller reference in my app.js :
.state('app.avatar-instruments', {
url: "/avatar/instruments",
views: {
'menuContent': {
templateUrl: "templates/avatar/instruments.html",
controller: 'AvatarInstrumentsCtrl'
}
}
})
So in my case the solution is:
.state('app.avatar-instruments', {
url: "/avatar/instruments",
views: {
'menuContent': {
templateUrl: "templates/avatar/instruments.html"
}
}
})

How to set ng-show on a specific element through different views/controllers?

Plunker: http://plnkr.co/edit/0efF1Av4lhZFGamxKzaO?p=preview
Below is my header, there is an ng-show="cornerLogo" which I only want to be set true on the about, login and register views, but false the home view.
<body id="body_id"
ng-app="myApp"
ng-controller="HomeCtrl">
<header>
<section ng-show="cornerLogo">
<h1>Logo</h1>
</section>
<nav id="main_nav">
<ul>
<li><a ui-sref="about">About</a></li>
<li><a ui-sref="login">Sign In</a></li>
<li><a ui-sref="register">Create Account</a></li>
</ul>
</nav>
</header>
<ui-view></ui-view>
So it works in my HomeCtrl because that is the main controller on the page.
var app = angular.module('app-home', [])
.controller('HomeCtrl', ['$scope', function($scope) {
$scope.cornerLogo = false;
}]);
However when I switch to the about, login or register views I lose that $scope
Is there a way somehow to have a global var set somewhere in my stateProvider for ui-router? Otherwise, how would you go about this issue?
var app = angular.module('bitAge',
['ui.router',
'app-header',
'app-home',
'app-about',
'app-login',
'app-register'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: '_views/home.html',
controller: 'HomeCtrl',
})
.state('about', {
url: '/about',
templateUrl: '_views/about.html',
controller: 'AboutCtrl'
})
.state('login', {
url: '/login',
templateUrl: '_views/login.html',
controller: 'LoginCtrl'
})
.state('register', {
url: '/register',
templateUrl: '_views/register.html',
controller: 'RegisterCtrl'
});
// default view:
$urlRouterProvider.otherwise('/home');
}]);
Apart from my comments in the question, to fix your issue you can take this approach.
You have HomeCtrl specified as bound controller in the state registration of home partial. So instead create a main controller for your application. So that you keep the responsibilities separated out. Inject $state and expose a method called hideLogo and use $state.is to determine the logic to show/hide the logo.
i.e:
var app = angular.module('app-home')
.controller('MainCtrl', ['$scope', '$state', function($scope, $state) {
$scope.hideLogo = function(){
return $state.is('home');
}
}]);
In the index html use MainCtrl as your main controller for the app.
<body ng-app="myApp" ng-controller="MainCtrl">
<header>
<section
ng-hide="hideLogo()">
<h1>Corner Logo</h1>
</section>
Plnkr
If you want to use $state directly on the view you would need to inject it in MainCtrland set $state on the $scope object so that you can use it directly. Though i highly recommend not to use this technique, you should not expose state in the scope object and ultimately in the view. Just expose only what is needed in the viewmodel.
i.e in the MainCtrl :
var app = angular.module('app-home')
.controller('MainCtrl', ['$scope', '$state', function($scope, $state) {
$scope.$state= $state;
}]);
and just use it on the view as:
<section
ng-hide="$state.is('home')">
You can check your current state and depends on that, show or not your logo.
<section ng-show="$state.includes('home')">
<h1>Logo</h1>
</section>
Also, your anchor elements to navigate, should be like this <a ui-sref="about">About</a> and so on, because if you use normal href attribute, angular wont change state.
Also, you need to inject $state in your main module and then you can use $state module
var app = angular.module('myApp',
['ui.router',
'app-home',
'app-about']).run(function ($state,$rootScope) {
$rootScope.$state = $state;
})
UPDATE:
Here is the punklr with the answer

ionic how to add blank page as home page of the app?

I would like to add new page into default ionic app with tabbed menu and display this page as the default page of the app.
Here is how I tried to do that:
I added new state into app.js
.state('home', {
url: '/home',
views: {
'home': {
templateUrl: 'templates/home.html',
controller: 'HomeCtrl'
}
}
})
and set default router to home page
$urlRouterProvider.otherwise('/home');
And template file:
<ion-view title="Home">
<ion-content class="padding">
<h1>Home page</h1>
</ion-content>
</ion-view>
Now, if I am trying to go to http://myapp.loc/index.html#/home I got always black page without content.
What I'm doing wrong?
Thanks for any help.
EDIT:
In order to Error: [ng:areq] Argument 'HomeCtrl' is not a function, got undefined
I'm adding related code.
Added controllers:
angular.module('starter.controllers', [])
.controller('HomeCtrl', function($scope, $location) {
})
.controller('DashCtrl', function($scope) {
})
.controller('FriendsCtrl', function($scope, Friends) {
$scope.friends = Friends.all();
})
.controller('FriendDetailCtrl', function($scope, $stateParams, Friends) {
$scope.friend = Friends.get($stateParams.friendId);
});
Order in index.html is following
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/controllers/overall_stats.js"></script>
<script src="js/services.js"></script>
I would say, that solution here should be surprisingly simple:
Remove the view name 'home'. In fact change it to '' (empty string)
There is a working plunker with this change?
.state('home', {
url: '/home',
views: {
// instead of this
// 'home': {
// use this
'': {
templateUrl: 'tpl.home.html',
controller: 'HomeCtrl',
}
}
})
Why? Because most likely the index.html would look like this:
<body ng-app="myApp" >
...
<ion-nav-view></ion-nav-view>
</body>
And that means that the view name is "", almost like <ion-nav-view name=""></ion-nav-view>
Check that in action here
EXTEND based on the question extension
if we use ionic-like approach with separated file and module for controllers:
// inside of the index.html
<script src="js/controllers.js"></script>
// the controller.js
angular.module('starter.controllers', [])
We must be sure, that our app.js does contain that module as well:
angular.module('myApp', [
'ionic',
'starter.controllers' // this is a MUST
])

Defining ng-view="NAME" from RouteProvider

I'm really new to Angular and I have a little question about sending a template or URL into a ng-view. But the way I intend to do I may have to ng-view in my base template.
When my template base is like this:
<body>
<div ng-view></div>
</body>
And my JS looks like:
var app = angular.module('myApp',[])
.config(['$routeProvider', '$locationProvider', '$httpProvider', function($routeProvider, $locationProvider, $httpProvider) {
$routeProvider
.when('/home', {
templateUrl: '/contato'
})
(...)
Works fine loading URL inside ng-view when I have only ONE ng-view case, HOW ABOUT IF I need to have more then one ng-view to load ? (like: ng-view="area1" and ng-view="area2")
I've tried in each $routeProvider, but won't work:
$routeProvider
.when('/home', {
area1: {templateUrl: '/path1'},
area2: {templateUrl: '/path2'}
})
How would be the right way to set each ng-view separately?
Appreciate any help! Thanks.
Unfortunately, as you know now, you cannot have more than one ng-view on your page. You should have a look at UI-Router from AngularUI which does exactly what you are looking for (https://github.com/angular-ui/ui-router).
An example from their doc (https://github.com/angular-ui/ui-router#multiple--named-views):
setup
var myApp = angular.module('myApp', ['ui.router']);
html
<body>
<div ui-view="viewA"></div>
<div ui-view="viewB"></div>
<!-- Also a way to navigate -->
<a ui-sref="route1">Route 1</a>
<a ui-sref="route2">Route 2</a>
</body>
template 1
<h1>State 1</h1>
template 2
<h1>State 2</h1>
js
myApp.config(function($stateProvider, $routeProvider) {
$stateProvider
.state('index', {
url: "",
views: {
"viewA": { template: "index.viewA" },
"viewB": { template: "index.viewB" }
}
})
.state('route1', {
url: "/route1",
views: {
"viewA": { templateUrl: "route1.viewA.html" },
"viewB": { templateUrl: "route1.viewB.html" }
}
})
.state('route2', {
url: "/route2",
views: {
"viewA": { templateUrl: "route2.viewA.html" },
"viewB": { templateUrl: "route2.viewB.html" }
}
});
});
Here you could specify a controller at the state level, that would be effective for both views, or at the view level, in order to set two different controllers.
Edit: Live demo from ui-router docs (http://plnkr.co/edit/SDOcGS?p=preview)
Basically you can't have two ng-view. Have a look at this SO question:
You can have just one ng-view.
You can change its content in several ways: ng-include, ng-switch or mapping different controllers and templates through the routeProvider.

Categories