$scope.myVar updates but not in the view - angularjs

I have a template nested in an other template (menu is the parent template) which share the same controller. My Controller gets the value from a service called counterOperations. Althought $scope.total seems to be updating in console.log the view does not update the value.
Controller
.controller('ListController', function ($scope, $http, $state, counterOperations, userService) {
//Some code
$scope.add = function (index) {
$scope.total = counterOperations.getTopCounter();
console.log($scope.total);
};
})
Templates
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html',
controller: "ListController"
})
$stateProvider
.state('app.list', {
url: '/list',
views: {
'products': {
templateUrl: 'templates/product-list.html',
controller: "ListController"
}
}
})
with ng-click I trigger a function which calls some functions from a service
The view variable for menu
<span>{{total}}</span>
I have tried to update the value this way but nothing changes
setTimeout(function() {
$scope.total = counterOperations.getTopCounter();
}, 1000);
Any ideas ?

Here your 2 states "app" and "app.list" have same controller, when you change state from "app" to "app.list" regardless of same controller controller will reinstantiated which means value in $scope.total variable will lost and will reset in next state.
Solution -
Assign total to $rootScope if you want it in other state too
$scope.add = function (index) {
$rootScope.total = counterOperations.getTopCounter();
};

.controller('ListController', function ($scope, $http, $state, counterOperations, userService) {
//Some code
$scope.total = ''; //or $scope.total = 0;
$scope.add = function (index) {
$scope.total = counterOperations.getTopCounter();
console.log($scope.total);
};
})
Check if the above works for you?

Related

$stateChangeSuccess, always the same value of $state.params

I can't wrap my head around this, I'm really struggling with ui-routeand $stateChangeSuccess and $watch, got two issues. I'm trying to change a value of a parameter, when I change the state, click a url. I think it's important to know that I use sub-states. With my current setup I get that the $stateChangeSuccess triggers twice on load, meaning it sets the value right away. What I'm trying to do is to only have it set on change of url.
My route looks like this, I'm trying to set the value of a parameter
.state('medications', {
url: '/medications',
templateUrl: '/partials/home.html',
controller: 'mainController',
params: { showSection: false },
resolve: {
postPromise: ['medicationservice', function(medicationservice) {
return medicationservice.getAll();
}]
}
})
.state('medications.add', {
url: '/add',
templateUrl: '/partials/home.html',
controller: 'mainController',
params: { showSection: true },
})
in my controller I got the following, where I set the openSesame parameter to false explicitly on init, but as described it triggers and sets it to true.
mainModule.controller('mainController', [
'$scope',
'$state',
'$rootScope',
'medicationservice',
function($scope, $state, $rootScope, medicationservice) {
$scope.medication = {};
$scope.medications = medicationservice.posts;
$scope.openSesame = false;
$rootScope.$on("$stateChangeSuccess", function() {
$scope.openSesame = true;
console.log($scope.openSesame);
});
}]);
in my plunker the state change works once, that is if I use the $rootScope.
You can actually use $stateChangeSuccess without $injecting $rootScope. You can set a listener on the $scope object instead. Take a look at the following plunker.
And the revised code:
(function() {
var app = angular.module('myApp', ['ui.router']);
app.controller('mainController', [
'$scope',
'$state',
function($scope, $state) {
$scope.medication = {};
$scope.openSesame = false;
$scope.$on('$stateChangeSuccess',function(event, toState){
$scope.openSesame = toState.params.showSection;
});
$scope.triggerMe = function() {
alert('yes');
};
}
]);
app.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('medications', {
url: '/medications',
template: '<div>{{openSesame}}</div>',
controller: 'mainController',
params: {
showSection: false
}
})
.state('medications.add', {
url: '/add',
template: '<div>{{openSesame}}</div>',
controller: 'mainController',
params: {
showSection: true
},
});
$urlRouterProvider.otherwise('medications');
}
]);
}());
Click your buttons medication and medication add to see the value change.
The callback of $stateChangeSuccess, you have access toState, fromState. Or check current state name
$rootScope.$on("$stateChangeSuccess", function() {
if ($state.current.name == "medications.add") {
$scope.openSesame = true;
}
console.log($scope.openSesame);
});

Angular passing parameters from one controller to another

Im a newbie in angular, trying to learn the language.
Got the following code: http://plnkr.co/edit/fuVb0mzhmDCKr1xKp7Rn?p=preview
Have a tab:
app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider.when
('/jobs', {templateUrl: 'jobs-partial.html', controller: JobsCtrl }).
when
('/invoices', {templateUrl: 'invoices-partial.html', controller: InvoicesCtrl }).
when
('/payments', {templateUrl: 'payments-partial.html', controller: PaymentsCtrl }).
otherwise({redirectTo: '/jobs'});
// make this demo work in plunker
$locationProvider.html5Mode(false);
}]);
I would like to be able to access the selected tab from one the panel. How can I send parameters to the tab controllers?
Create a service that will set a value and return it:
.service('shared', function() {
var myValue;
return {
setValue: function(value) {
myValue = value;
},
getValue: function() {
return myValue;
}
}
});
Then inject it into both your controllers:
.controller('Ctrl1', ['shared', function(shared)......
.controller('Ctrl2', ['shared', function(shared)......
And then set the value from Ctrl1:
shared.setValue('somevalue');
And in Ctrl2 you can just retrieve the value:
var mySharedValue = shared.getValue();
You can create a Service or Factory, inject that in to your TabsCtrl, save the currentTab state in that service in ng-click. Inject the same service in your Page controllers like JobsCtrl
app.factory('MyService',function(){
var currentTab ;
return {
setCurrentTab : function(tab){
currentTab = tab;
},
getCurrentTab : function(tab){
return currentTab;
}
};
});
Update your TabsCtrl like below
function TabsCtrl($scope, $location, MyService) {
// removing other code for brevity
$scope.selectedTab = $scope.tabs[0];
// saving the default tab state
MyService.setCurrentTab($scope.tabs[0]);
$scope.setSelectedTab = function(tab) {
$scope.selectedTab = tab;
// saving currentTab state on every click
MyService.setCurrentTab(tab);
}
}
In your JobsCtrl, inject the same MyService and retrieve the cached tab state like below
function JobsCtrl($scope, MyService) {
var currentTab = MyService.getCurrentTab();
alert(currentTab.label);
}
Here's an updated Plunker with the above changes.

How to call one controller array in another controller

I have created countryApp module. I have created "names" array in aboutCtrl.
I want to access "names" array in contactCtrl in insertContact function.
var countryApp = angular.module('countryApp', ['ngRoute']);
countryApp.config(function($routeProvider) {
$routeProvider.
when('/', {
template: '<h1>Home</h1>',
controller: 'homeCtrl'
}).
when('/aboutus', {
templateUrl: 'aboutus.html',
controller: 'aboutCtrl'
}).
when('/contact', {
templateUrl: 'cotacts.html',
controller: 'contactCtrl'
}).
otherwise({
redirectTo: '/'
});
});
countryApp.controller('homeCtrl', function($scope) {
});
countryApp.controller('aboutCtrl', function($scope) {
$scope.names = [{name:'venu',number:'22222',sex:'male'},{name:'Aishu',number:'1111',sex:'female'},{name:'Milky',number:'2222',sex:'female'}]
});
countryApp.controller('contactCtrl', function($scope) {
$scope.greeting = 'Hello, World!';
$scope.insertContact = function () {
alert(names);
}
$scope.resetContact = function () {
}
});
To expand on Claies' solution with actual code:
countryApp.controller('aboutCtrl', function($scope, dataService) {
/* If you're binding the values into your HTML,
you need to $watch the service variable 'names' */
$scope.$watch(function() { return dataService.names }, function() {
$scope.names = dataService.names;
});
dataService.names = [{name:'venu',number:'22222',sex:'male'},{name:'Aishu',number:'1111',sex:'female'},{name:'Milky',number:'2222',sex:'female'}]
});
countryApp.controller('contactCtrl', function($scope, dataService) {
/* You can place the same watch as in aboutCtrl here if
you're displaying stuff in HTML that's related to this controller */
$scope.greeting = 'Hello, World!';
$scope.insertContact = function () {
alert(dataService.names);
}
$scope.resetContact = function () {
}
});
countryApp.service('dataService', function() {
var dataObj = {};
dataobj.names = [];
return dataObj;
});
Do notice the comments about $watching the service variable; if you want to display the values in the view through a $scope variable, you'll need to $watch the corresponding service variable or the $scope variable won't be updated when the value of the service variable changes.
If you want to communicate between two controllers, then please take reference of the following fiddle:
Fiddle : http://jsfiddle.net/VaibhavP17/nky45/
It shows how to communicate between the two controllers and also the nuances of events. I hope this helps :)

stateParams vs $stateParams with ui-router?

I am confused. For a long time now I have been using stateParams as a means of find out the stateParams inside a templateUrl.
Now I tried to do the same in a resolve and it does not work. In fact nothing happens when I use stateParams.
However by chance I found that I can use $stateParams in the resolve and it works.
Can someone tell me what is the difference and why do I need to use stateParams in the templateUrl and $stateParams in the resolve?
var auth = {
name: 'auth',
url: '/Auth/:content',
templateUrl: function (stateParams) {
var page = 'app/auth/partials/' + stateParams.content + '.html';
return page;
},
controller: function ($scope, authService) {
$scope.aus = authService;
},
resolve:
{
init: function ($stateParams) {
var x = 99;
return true;
}
}
};
I've created working example here, showing that $statePrams are accessible in the resolve
// States
$stateProvider
.state('auth', {
url: "/auth/:content",
templateUrl: 'tpl.html',
controller: 'AuthCtrl',
resolve : {
init : ['$stateParams' , function($stateParams){
return { resolved: true, content: $stateParams.content };
}]
}
})
Controller
.controller('AuthCtrl', ['$scope', 'init', function ($scope, init) {
$scope.init = init;
}])
and this could be the calls
auth/8
auth/xyz
Check it here

unable to open a modal with angular and ui.routes

I am trying to follow this example to show a bootstrap modal on a certain state. It works fine without a modal (so the state config should be ok). All needed dependencies (ie angular bootstrap) should be available.
when I do a console.debug($stateParams) before $modal.open I get the correct data, within the $modal.open-method however the stateParams from the last state are returned (the state I am coming from)
Any hints?
EDIT
the relevant state cfg:
.state('publications.view', {
parent: 'publications.productSelection',
url: '/{productSlug:[a-zA-Z0-9-]+}/{docID:[0-9]+}_{slug:[a-zA-Z0-9-]+}',
onEnter: ['restFactory', '$state', '$stateParams', '$modal',
function(restFactory, $state, $stateParams, $modal) {
console.debug($stateParams.docID);
$modal.open({
templateUrl: 'partials/publication.html',
resolve: {
publication: ['restFactory', '$stateParams',
function(restFactory, $stateParams) {
console.debug($state.params);
console.debug($stateParams);
return restFactory.view($stateParams.language, $stateParams.productSlug, $stateParams.docID);
}
]
},
controller: ['$scope', '$sce', 'publication', '$rootScope',
function($scope, $sce, publication, $rootScope) {
$rootScope.pageTitle = publication.data.data.publication.Publication.title;
$scope.publication = $sce.trustAsHtml(publication.data.data.publication.Publication.content);
}
]
});
}
]
});
You can get around this issue by injecting the current $stateParams into the onEnter function, save them as state in some service, and inject that service instead into your modal resolves.
I am adapting the code from here: Using ui-router with Bootstrap-ui modal
.provider('modalState', function($stateProvider) {
var modalState = {
stateParams: {},
};
this.$get = function() {
return modalState;
};
this.state = function(stateName, options) {
var modalInstance;
$stateProvider.state(stateName, {
url: options.url,
onEnter: function($modal, $state, $stateParams) {
modalState.stateParams = $stateParams;
modalInstance = $modal.open(options);
modalInstance.result['finally'](function() {
modalInstance = null;
if ($state.$current.name === stateName) {
$state.go('^');
}
});
},
onExit: function() {
if (modalInstance) {
modalInstance.close();
}
}
});
};
})
Then in your app config section
.config(function($stateProvider, $urlRouterProvider, modalStateProvider) {
modalStateProvider.state('parent.child', {
url: '/{id:[0-9]+}',
templateUrl: 'views/child.html',
controller: 'ChildCtrl',
resolve: {
role: function(Resource, modalState) {
return Resource.get({id: modalState.stateParams.id}).$promise.then(function(data) {
return data;
});
}
}
});
}

Resources