Why variable is not available in controller? - angularjs

I have HTML code:
<div ng-controller="ProfileLeftMenu">
<li ng-class="{'active':selectedTab == 'personal'}" ng-click="selectedTab = 'personal'" class="">Personal
</li>
</div>
And controller:
$scope.selectedTab = 'first';
if ($routeParams.page) {
ajax.get(page, function (CbData) {
$scope.selectedTab = page;
});
}
So, if do:
{{selectedTab}}
in template HTML get always: first

You need to update your $scope variable with the new $routeParams just after the change in route. For that you can listen for the$routeChangeSuccess event. Try this:
DEMO
app.js
var app = angular.module('plunker', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/test/:page', {
templateUrl: function(params) {
return 'pidat.html';
},
controller: 'MainCtrl'
});
}
]);
app.controller('MainCtrl', ['$scope', '$http', '$routeParams', function($scope, $http, $routeParams) {
// when controller is loaded params are empty
console.log('on controller load $routeParams', $routeParams);
$scope.name = 'World';
// only after you have transitioned to the new
// route will your $routeParams change so we
// need to listen for $routeChangeSuccess
$scope.$on('$routeChangeSuccess', function(){
console.log('on $routeChangeSuccess load $routeParams', $routeParams);
if ($routeParams.page) {
$scope.name = $routeParams.page;
}
});
}]);
So for your original example you would probably have to do something like this:
$scope.selectedTab = 'first';
$scope.$on('$routeChangeSuccess', function(){
if ($routeParams.page) {
ajax.get(page, function (CbData) {
$scope.selectedTab = page;
});
}
});

Use the angular $http service ($http.get()), not ajax.get(). Otherwise, Angular isn't aware of the change you make to the scope once the HTTP response comes and the callback is executed, unless you call $scope.$apply().

Related

Scope variable are not available in the template

I started a project with angular-seed. I mixed it with another project in which has jQuery. I can't reach the scope variables in my template.
JS:
'use strict';
angular.module('myApp.view1', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}])
.controller('View1Ctrl', ['$scope', function ($scope) {
$scope.showView = true;
$scope.proBlock = false;
$scope.modelBlock = false;
$.when(dbReadyDeferred).then(function() {
$scope.proBlock = true;
console.log('dbReadyDeferred.state()',dbReadyDeferred.state());
});
...
HTML:
<div ng-show="showView">
{{proBlock}}
</div>
In the browser, it shows: false. Is there something wrong with my code?
Thanks
As your using $.when, which is out of Angular world, you have to explicitly use $timeout or $scope.$digest.
You can use Angular's promise library which is $q.when method, as AngularJS will attach the watch to it and changes accordingly.
$q.when(dbReadyDeferred).then(function() {
$scope.proBlock = true;
console.log('dbReadyDeferred.state()',dbReadyDeferred.state());
});
If you want to use jQuery.when method, then used $timeout as below:
$.when(dbReadyDeferred).then(function() {
$timeout(function() {
$scope.proBlock = true;
console.log('dbReadyDeferred.state()',dbReadyDeferred.state());
},0,false) // false it doesnt invoke digest again which helps in performance
});
If you are sure that your function $.when(dbReadyDeferred).then(function() {}) is being called then change your controller code like this:
.controller('View1Ctrl', ['$scope', '$timeout', function ($scope, $timeout) {
$scope.showView = true;
$scope.proBlock = false;
$scope.modelBlock = false;
$.when(dbReadyDeferred).then(function() {
$timeout(function() {
$scope.proBlock = true;
console.log('dbReadyDeferred.state()',dbReadyDeferred.state());
});
});
}]);
Since, you are changing the $scope.proBlock value using jQuery so the Angular is unaware of this change and we need to explicitly tell Angular to run the digest cycle.
We can use $scope.$apply() as well but wrapping the call into $timeout function is a cleaner approach.
Read more: AngularJS with Ajax Form submission needing to click twice
Update:
You can modify your resolve variable like this:
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'.
resolve: {
dbState: ['$rootScope', '$q', function($rootScope, $q) {
var promise = $q.when(dbReadyDeferred)
promise.then(function() {
$rootScope.$broadcast("dbStateReady");
});
return promise;
}]
}
});
}])
.controller('View1Ctrl', ['$scope', function ($scope) {
$scope.showView = true;
$scope.proBlock = false;
$scope.modelBlock = false;
var deregisterFunction = $scope.$on("dbStateReady", function() {
$scope.proBlock = true;
console.log('dbReadyDeferred.state()',dbReadyDeferred.state());
deregisterFunction(); // Remove this watch for $on listener
});
});
Basically, we moved the $q.when (like #shushanthp mentioned) to the resolve and using $broadcast to know about when the db state is ready using $on.

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.

Angular Routing Params issue

I am using a module where I add in my first controller which is taking a service as a dependency. All the service is doing is bringing in some data.
Then I have a show function which I am adding on an anchor element in my view in order to be able to click on the the first name and then get the user's details.
My second controller takes the data from the first controller and then using $routeParams I am trying to show the data on the user view.
Is there something I am doing wrong here?
(function() {
var app = angular.module('test');
app.controller('testCtrl', ['$scope', 'testFactory', '$location', function($scope, testFactory, $location) {
testFactory.getContact().then(function(data) {
$scope.contacts = data.data;
});
$scope.show = function(firstname) {
$location.path('main/' + firstname);
};
}]);
app.controller('userCtrl', ['$scope', '$routeParams', function($scope, $routeParams) {
$scope.user = $scope.contacts[$routeParams.firstname];
}]);
}());
These are the routes
(function() {
var app = angular.module('test', ["ngRoute"]);
app.config(function($routeProvider, $locationProvider) {
$routeProvider
.when("/main", {
templateUrl: "main.html",
controller: "testCtrl"
})
.when("/main/:firstname", {
templateUrl: "contact.html",
controller: "userCtrl"
})
.otherwise({redirectTo:"/main"});
$locationProvider.html5Mode(true);
});
}());
This is the error I am getting in my console:
TypeError: Cannot read property 'any1' of undefined
where any1 is the is the first name.

Pass variable between controllers using $broadcast - AngularJS

I have a modal which I call from a function, when I call the modal I want to pass a URL to it like so:
ng-click="showModal('abc','testUrl')"
I want to share testUrl between controllers so I'm trying this:
hmwsApp.controller('mainController', ['$scope', '$http', '$rootScope', 'ModalService', function($scope, $http, $rootScope, ModalService) {
$scope.showModal = function(modalUrl, imageUrl) {
$rootScope.$broadcast('imageUrl', imageUrl); // <--- set global scope var
ModalService.showModal({
templateUrl: '/views/modals/' + modalUrl + '.html',
controller: "ModalController"
})
.then(function(modal) {
modal.element.modal();
modal.close.then(function(result) {
$scope.message = "Image show " + result;
});
});
};
}]);
Then in modal controller:
hmwsApp.controller('ModalController', function($scope, close) {
$scope.$on('imageUrl', function(response) {
$scope.imageUrl = response; // <--- read global scope var
})
$scope.close = function(result) {
close(result, 500); // close, but give 500ms for bootstrap to animate
};
});
The reason I'm doing this is I want to reference the URL in the modal like so:
<div class="modal-content">
<img src="{{ imageUrl }}">
</div>
But it simply doesn't work, the img src is empty, so I guess I'm missing something but not sure what? Any ideas?
UPDATE
Also tried the following but still nothing:
$scope.$on('imageUrl', function(events, args){
console.log(args); // <--- empty
$scope.imageUrl = args;
});
From what I can see, your ModalController isn't instantiated when you do the $rootScope.$broadcast call. You should do it AFTER you've shown the modal, otherwise it won't be "in existence" to intercept it with $scope.$on

Why are watchers not firing when ui-router state data is changed on parent?

I am unable to get a $scope.$watch or $scope.$watchCollection to trigger when updating $state.current.data from a parent view.
I've created a plunker to demonstrate: http://plnkr.co/edit/d4hblq9QvnMOLQKxO9jc?p=preview
To use, navigate to the /main/1 path and click the 'change' button. You will see the $state.data get updated, yet the watchers never get a notification.
Here is the script.js source:
var app = angular
.module('MyApp', [
'ui.router'
])
.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/main');
$stateProvider
// States
.state("main", {
data: {hello:'hello'},
controller:'mainController',
url:"/main",
templateUrl: "main_init.html"
})
.state("main.1", {
controller:'childController',
parent: 'main',
url:"/1",
templateUrl: 'form_1.html'
})
}])
.controller('mainController', function ($scope, $state, $timeout) {
$scope.Model = $scope.Model || {Name : "xxx"};
$scope.changeHello = function changeHello(){
console.log('about to change...');
$timeout(function(){
$state.current.data.hello = 'hi';
console.log('changed.');
},3500);
}
})
.controller('childController', function($scope, $state, $timeout){
$scope.changeStatus = 'nothing yet';
$scope.$watch($state.$current.data.hello, function(newObj){
if(newObj){
console.log('changed');
$scope.changeStatus = 'changed via watch';
}
});
$scope.$watchCollection($state.$current.data, function(newObj){
if(newObj){
console.log('changed');
$scope.changeStatus = 'changed via watchcollection';
}
});
$scope.$watchCollection($state.$current, function(newObj){
if(newObj){
console.log('changed');
$scope.changeStatus = 'changed via watchcollection $current';
}
});
$scope.$watchCollection($state.current, function(newObj){
if(newObj){
console.log('changed');
$scope.changeStatus = 'changed via watchcollection current';
}
});
})
app.run(
['$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}
])
$watch is looking for an event that is the value of your variable, $state.current.data.hello. This is what it's doing:
$scope.$watch('hello', function(newObj)...
Put your variable in quotes and the $watch will work as expected:
$scope.$watch('$state.current.data.hello', function(newObj)...

Resources