$state.go() calls the controller of other state several times - angularjs

Here are my states:
var states = angular.module("states", []);
states.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/landingPage");
$stateProvider
.state("landingPage", {
url: "/landingPage",
templateUrl: "../html/landing-page.html",
controller: "landingCtrl"
})
.state("units",{
url: "/units",
templateUrl: "../html/units.html",
controller: "unitsCtrl",
})
});
Here are my controllers:
var controllers = angular.module("controllers", []);
controllers.controller("landingCtrl", function($scope, $cordovaDialogs,$state) {
$scope.getStarted = function () {
$state.go("units");
};
});
controllers.controller("unitsCtrl", function($scope){
$scope.createUnits = function(){
console.log("Hello");
}();
});
here is the associated html bit:
<button class="button button-assertive btn newbtn" ng-click="getStarted()">Let's Get Started</button>
Basically, the console logs "hello", specifically 4 times, when i get transferred to "units" state. Can anybody steer me into some general directions as why this happens? I haven't the foggiest.

Related

AngularJS router - parent controller loads when child is clicked

I am having strange results working with AngualarJS states. Here is app code:
/* myApp module */
var myApp = angular.module('myApp', ['ui.router'])
.config(function ($stateProvider) {
$stateProvider.state('home', {
url: "home",
template: '<div ui-view><h3>Home</h3><a ui-sref="home.child({reportID:1})">Child</a></div>',
params: { reportID: null },
controller: function ($scope) {
$scope.homeCtrlVar = "home";
console.log("Home controller loaded");
}
}).state('home.child', {
template: '<div><h3>Child</h3><a ui-sref="home">Back</a></div>',
controller: function ($scope) {
$scope.childCtrlVar = "child";
console.log("Child controller loaded");
}
});
})
.controller('MainCtrl', function ($scope, $state) {
console.log("MainCtrl initialized!");
$state.go("home");
});
And main page:
<div ng-app="myApp" ng-controller="MainCtrl">
<h2>My app</h2>
<div ui-view></div>
What's happening is that as long as there parameters for the home state and reportID value doesn't match between a parameter being sent and the state default the home controller is loaded when I click on Child. Can someone please explain why that's happening?
Fiddle
Here is updated code which works as you expect it to:
var myApp = angular.module('myApp', ['ui.router'])
.config(function ($stateProvider) {
$stateProvider.state('home', {
url: "home",
template: '<div ui-view><h3>Home</h3><a ui-sref="home.child({reportID:1})">Child</a></div>',
controller: function ($scope) {
$scope.homeCtrlVar = "home";
console.log("Home controller loaded");
}
}).state('home.child', {
url: "/:reportID",
params: { reportID: null },
template: '<div><h3>Child</h3><a ui-sref="home">Back</a></div>',
controller: function ($scope) {
$scope.childCtrlVar = "child";
console.log("Child controller loaded");
}
});
})
Problem with your approach:
specifying params reportID in home state instead of home.child state.
When user clicks on home.child({ reportId: 1}) it should load home.child, which is fine, and was working with old approach.
However, If you take notice, as you click on home.child({ reportId: 1}), you are sending new parameter reportID(old value was null). reportID belongs to home state, hence its controller is also loaded.
Note that url: "/:reportID" in state home.child is optional.

Closing Modal Dialog does not return to original html page

I have a very simply Modal Dialog that is displayed from the main header bar.
When I close the modal dialog, it does not go back to the original page. The url displayed in the address bar is the modal that I closed.
How do I go back to the original page that was displayed when I clicked the button.
This is my code:
This is in app.js which is how the Modal, reportSettingModal is displayed.
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'lib/views/login.html',
controller: 'LoginCtrl'
})
.state('home', {
url: '/home',
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
})
.state('renderReport',{
url: '/renderReport/{guid}',
templateUrl: 'views/renderReport.html',
controller: 'RenderReportCtrl'
})
.state('vAnalyze',{
url: '/vAnalyze',
templateUrl: 'views/vAnalyze.html',
controller: 'VAnalyzeCtrl'
})
.state('reportSetting',{
url: '/reportSettingModal',
onEnter: function($modal){
$modal.open({
templateUrl: 'views/reportSettingModal.html',
controller: 'ReportSettingModalCtrl'
});
}
});
This is the HTML:
<div class="modal-header" id="reportSettingModal" style="background-color: #A33441; color: white;">
<h4 class="modal-title">{{title}}</h4>
</div>
<div class="modal-body">
<button type="button" class="btn btn-danger" style="margin-right: 5px; margin-left: 5px;" ng-click="updateWarehouse()">Update Warehouse</button>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-vow" id="closeButton" ng-click="close()">Close</button>
</div>
This is the controller:
angular.module('vAnalyzeApp.controllers')
.controller('ReportSettingModalCtrl', [
'$scope',
'$uibModal',
'Session',
'$uibModalInstance',
function($scope, $uibModal, session, $uibModalInstance){
$scope.username = session.user;
$scope.title = 'Report Settings';
$scope.close = function() {
$uibModalInstance.close();
};
} //end main function
]);
So, if I am on the VAnalyze page and I open the modal, when I close the modal, i want to be on teh VAnalyze page.
UPDATE
.run(['AuthService', 'configSettings', '$rootScope',
function (AuthService, configSettings, $rootScope) {
// Apply Product Code
configSettings.productCode = 'VANALYZE';
AuthService.configProduct(configSettings.productCode);
},
function ($rootScope) {
$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
$rootScope.$previousState = from;
});
}
]);
UPDATE
I added the state.go function to the modal close function but is still not returning the previous url when the modal was opened. This is my current close function:
$scope.close = function() {
$uibModalInstance.close();
$state.go($rootScope.$previousState.name, {
url: $rootScope.$previousState.url,
templateUrl: $rootScope.$previousState.templateUrl,
controller: $rootScope.$previousState.controller
});
};
Stepping thru the code the url, name templateUrl and controller are all properly set.
Option 1:
Inside your close function, you should specify to which state you want to go. Once the user close the modal.
$scope.close = function() {
$uibModalInstance.close();
$state.go('state-name');
};
Option 2:
If the URL is not relevant, you could call the Modal service from the VAnalyze state Controller, which is VAnalyzeCtrl:
$modal.open({
templateUrl: 'views/reportSettingModal.html',
controller: 'ReportSettingModalCtrl'
});
By doing this, you wont have to Redirect the user to the previous URL.
For going back to the previous state you need to store it so that you can re-route to it on modal close.
You need to add this function in your app.js file same way as you have defined config,
.run(['AuthService', 'configSettings', '$rootScope', function (AuthService, configSettings, $rootScope)
{ // Apply Product Code
configSettings.productCode = 'VANALYZE';
AuthService.configProduct(configSettings.productCode);
$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
$rootScope.$previousState = from;
});
} ]);
Inside your ReportSettingModalCtrl,
angular.module('vAnalyzeApp.controllers')
.controller('ReportSettingModalCtrl', [
'$scope',
'$uibModal',
'Session',
'$uibModalInstance',
'$state',
'$rootScope'
function($scope, $uibModal, session, $uibModalInstance, $state, $rootScope){
$scope.username = session.user;
$scope.title = 'Report Settings';
$scope.close = function() {
$uibModalInstance.close();
$state.go($rootScope.$previousState.name);
};
} //end main function
]);

$http and $stateParams in resolve of $stateProvider, Unknown provider error

The goal here is to send an http request with the same parameter of the state parameter. This will then display the food types associated with the cuisine type that has been clicked. Is this even theoretically possible?
"Error: [$injector:unpr] Unknown provider: getFoodsProvider <- getFoods <- AppCtrl"
js
var myApp = angular.module('myApp', ['ui.router']);
myApp.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url:'/',
templateUrl: 'partials/home.html',
controller: 'AppCtrl'
})
.state('food', {
url: '/food/:cuisine',
templateUrl: 'partials/food.html',
controller: 'AppCtrl',
resolve: {
getFoods: ['$http', '$stateParams', function($http, $stateParams) {
var url = '/getfoods/' + $stateParams.cuisine;
return $http.get(url).success(function(response) {
return response.data;
})
}]
}
});
$urlRouterProvider.otherwise('/');
});
myApp.controller('AppCtrl', ['$scope', 'getFoods', function ($scope, getFoods) {
$scope.foods= getFoods;
}]);
home
<md-list>
<md-list-item ng-repeat="cuisine in cuisines">
<a ui-sref="food({cuisine:cuisine})">{{cuisine}}</a>
</md-list-item>
</md-list>
food
<md-list>
<md-list-item ng-repeat="food in foods">
<div>{{food}}</div>
</md-list-item>
</md-list>
Your logic seems perfect and it should work. But I think as you're sending ajax request in the resolve and it works asynchronously you need a resolve there. And no need to use the resolve value in controller. Just set the data of the http response in a factory and use the same factory to get the data in the controller.
So try this:
resolve: {
getFoods: ['$http', '$stateParams','$q','foodData' function($http, $stateParams, $q,foodData) {
var url = '/getfoods/' + $stateParams.cuisine,
deferred = $q.defer(),
$http.get(url).success(function(response) {
foodData.setData(response.data);
deferred.resolve();
}).error(function(error){
deferred.reject();
$state.go(some other state);
})
return deferred.promise;
}]
}
On the off-chance that someone needs a solution to the same problem, you should know it was resolved by creating a separate controller for the state with the service (see comment below). The main controller was trying to load the 'getFoods' service when its associated state hadn't been activated yet. No promises necessary. Also, I added .data after the service in the controller.
new controller
var myApp= angular.module('myApp');
myApp.controller('foodCtrl', ['$scope', 'getFoods', function ($scope, getFoods) {
$scope.foods = getFoods.data; //added .data after service
}]);
main js
var myApp = angular.module('myApp', ['ui.router']);
myApp.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url:'/',
templateUrl: 'partials/home.html',
controller: 'AppCtrl'
})
.state('food', {
url: '/food/:cuisine',
templateUrl: 'partials/food.html',
controller: 'foodCtrl', //specify different controller
resolve: {
getFoods: ['$http', '$stateParams', function($http, $stateParams) {
var url = '/getfoods/' + $stateParams.cuisine;
return $http.get(url).success(function(response) {
return response.data;
})
}]
}
});
$urlRouterProvider.otherwise('/');
});

Navigation and states inside ionic framework

i'm trying to make an app where the first time you use it I save some information locally. My purpose is that when the next times a person open the app it will be shown another page.
The problem is that I don't know how to load a simple page (not a part like a template) from my controller function.
Here my html
<body ng-app="starter" ng-controller="MainCtrl">
<ion-content class="center" [....]
<button class="button button-calm button-full button-clear button-large" ng-click="saveAll(name, job)">
<span style="font-size: 1.4em;">start</span>
</button>
</ion-content>
</body>
app.js
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider.state('home', {
url: "/home",
controller: "HomeCtrl",
template: "<h1>Home page</h1>"
})
$urlRouterProvider.otherwise('/first')
})
.controller('MainCtrl', function($scope, $localstorage, $location) {
$scope.setName = function(name) {
if (name) {
$localstorage.set('name', name.toLowerCase());
console.log(name);
}
}
$scope.getName = function() {
alert($localstorage.get('name'));
}
$scope.setJob = function(job) {
if (name) {
$localstorage.set('job', job.toLowerCase());
console.log(job);
}
}
$scope.getJob = function() {
alert($localstorage.get('job'));
}
$scope.saveAll = function(name, job) {
if (name) {
$localstorage.set('name', name.toLowerCase());
console.log(name);
}
if (job) {
$localstorage.set('job', job.toLowerCase());
console.log(job);
}
if (name && job) {
$location.path("home");
}
}
})
You can navigate routes using:
$location.path('/home');
or:
$state.go('home');
So.. in your MainCtrl you could put something like this:
$scope.getName = function() {
return $localstorage.get('name');
};
$scope.getJob = function() {
return $localstorage.get('job');
};
var init = function(){
if($scope.getName() && $scope.getJob()){
$location.path('/home');
}
};
init();
UPDATE:
Ionic has ui-router installed by default. ui-router documentation.
Each route accept a templateUrl property. I don't know which is you folder structure so you need to change them to work correctly:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
controller: 'HomeCtrl',
templateUrl: 'home/home.html'
})
.state('first', {
url: '/first',
controller: 'FirstCtrl',
templateUrl: 'first/first.html'
})
.state('second', {
url: "/second",
controller: "SecondCtrl",
templateUrl: 'second/second.html'
});
$urlRouterProvider.otherwise('/first');
})

Angular Binding to MathJax - UI Router Inconsistent?

I am using Angular UI Router with MathJax. When I route to a new state, the MathJax does not get processed. What burns my noodle is that if I have an update button with exactly the same code it works. Just not by itself.
PLUNKER
HTML
<p id="MathExample"><strong>Equation:</strong> {{data.equation}}</p>
<button class="btn btn-primary" ng-click="update()">Update</button>
JS
$stateProvider.state('route1', {
url: "/route1",
templateUrl: "route1.html",
controller: function($scope, $http){
$http({method: 'GET', url: 'data.json'})
.success(function(data) {
$scope.data = data;
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
//You would think that the value would be updated here!
})
$scope.update = function($scope){
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
//This works just fine, but is not automatic.
}
}
});
I'm trying to figure out why this is the case. Do I need to use $apply? One question hinted at the use of a directive. However, I don't need this function. I really just need the JSON data with the math expressions to reliably become typeset.
It looks like you just need to wait for the DOM to settle down before MathJax can process it. A simple setTimeout call seems to fix the issue:
$http({method: 'GET', url: 'data.json'})
.success(function(data) {
$scope.data = data;
setTimeout(function() {
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
});
});
See http://plnkr.co/edit/xIVvUHntVZ7lftGz9cDn?p=preview for an example.
You mentioned using a directive; you could use one to abstract away the need to call the MathJax function manually at all.
In this example, I've created a directive called math that automatically lays out whatever's passed in with MathJax:
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/");//Was defaulting to route 1. No more
$stateProvider.state('route1', {
url: "/route1",
templateUrl: "route1.html",
controller: function($scope, $http, $timeout){
$http({method: 'GET', url: 'data.json'})
.success(function(data) {
$scope.data = data;
});
}
});
});
myapp.directive('math', function() {
return {
restrict: 'EA',
scope: {
math: '#'
},
link: function(scope, elem, attrs) {
scope.$watch('math', function(value) {
if (!value) return;
elem.html(value);
MathJax.Hub.Queue(["Typeset", MathJax.Hub, elem[0]]);
});
}
};
});
<hr>
<h3>Route 1's data</h3>
<p>
<strong>Name:</strong>
{{data.name}}
</p>
<p>
<strong>Equation:</strong>
<span math="{{data.equation}}"></span>
</p>
See http://plnkr.co/edit/c9NBXXd9kF7Jne6saS3i?p=preview for a demonstration.
Going this route, absolutely anything passed to the math directive will be automatically rendered with MathJax whenever it changes. See this demo for an example that lets you edit the equation: http://plnkr.co/edit/dJSEGtBKSrKLjiwuLsl5?p=preview
Because you are trying to modify Math on the page before it is rendered. You can use the $timeout to wait until it is rendered:
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/");//Was defaulting to route 1. No more
$stateProvider.state('route1', {
url: "/route1",
templateUrl: "route1.html",
controller: function($scope, $http, $timeout){
$http({method: 'GET', url: 'data.json'})
.success(function(data) {
$scope.data = data;
$timeout(function() {
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
}, 0);
})
$scope.update = function($scope){
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
}
}
});
});

Resources