This is a dumb scoping issue and I'm bad at Javascript. Pretty sure I don't know how to return the value of a variable up to parent scope (e.g. look for the **** in AppCtrl)
I have 2 controllers. AppCtrl (parent) and MainCtrl (child)
<div ng-controller="AppCtrl">
<div ng-controller="MainCtrl">
</div>
</div>
I am fetching some random JSON data in the MainCtrl.
.controller('MainCtrl', function ($scope, $http) {
$scope.datas = [];
$http.jsonp('http://filltext.com/?rows=10&delay=1&fname={firstName}&callback=JSON_CALLBACK').
success(function(data){
$scope.datas[0] = data;
console.log($scope.datas);
$scope.$emit('load::1');
});
$http.jsonp('http://filltext.com/?rows=10&delay=2&fname={firstName}&callback=JSON_CALLBACK').
success(function(data){
$scope.datas[1] = data;
console.log($scope.datas);
$scope.$emit('load::2');
});
$http.jsonp('http://filltext.com/?rows=10&delay=4&fname={firstName}&callback=JSON_CALLBACK').
success(function(data){
$scope.datas[2] = data;
console.log($scope.datas);
$scope.$emit('load::3');
});
});
When that data is successful I will emit a message to AppCtrl.
On the AppCtrl I am listening for the emitted messages and setting the array var load = true.
.controller('AppCtrl', function ($scope, $http) {
//*****
var load = [];
$scope.$on('load::1', function(){
//how do i return this value to the var above?
load[0] = true;
});
$scope.$on('load::2', function(){
load[1] = true;
});
$scope.$on('load::3', function(){
load[2] = true;
});
if(load[0]=== true && load[1] === true && load[2] == true) {
console.log('loaded');
}
});
when all three are set to true I would like the console.log to log out 'loaded'.
If condition will run only once so you can call if condition with every $on listener or combine multiple listener and check if all content is loaded or not.both ways are same.
myapp.controller('AppCtrl', function ($scope, $http) {
//*****
var load = [];
angular.forEach(['load::1', 'load::2', 'load::3'], function (value) {
$scope.$on(value, function (event) {
load.push(true);
if (load[0] === true && load[1] === true && load[2] == true) {
alert('loaded');
}
});
});
});
Fiddle Demo
It will never print 'loaded' because this statement run only once
if(load[0]=== true && load[1] === true && load[2] == true) {
console.log('loaded');
}
2. To share data between controllers in Angular is better to use services https://docs.angularjs.org/guide/services.
Related
I've been doing a todo list with AngularJS and want to know if there's a way to focus on a input box after creating it by clicking on a button.
Actually, my save function inside the controller is defined like this:
$scope.save = function() {
$scope.objetivo.$save()
.then(function() {
$scope.message = {
text : 'Saved'
};
$scope.objective = new Objective();
})
.catch(function(err) {
console.log(err.data);
if(err.data.code === 11000) {
text = 'This objective is already registered'
}
$scope.message = {
text : text || "Error when saving"
};
});
};
I think there's might be a way by adding the input box and then focusing on it, but I don't know how to do it.
This fiddle shows you how to implement focus elements even on async logics.
View
<div ng-controller="MyCtrl">
<button ng-click="someAsyncFunction()">
Set focus on async functions
</button>
<input type="text"
set-focus="{{setFocus}}">
</div>
AngularJS Application
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope, $timeout) {
$scope.someAsyncFunction = function ()
//the following timeout indicates ansyc functions.
$timeout(function () {
$scope.setFocus = true;
}, 250);
}
});
myApp.directive('setFocus', function($timeout) {
return {
scope: { trigger: '#setFocus' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === "true") {
$timeout(function() {
element[0].focus();
},250);
}
});
}
};
});
I use this controller in ionic app to check new message or activity and add a badge if is there news:
.controller('checkNew', function($scope, getNewMsg, getNewAct, $localstorage, $cordovaLocalNotification, $http, $state) {
getNewMsg.getNew(function(data) {if(data!=0){$scope.addActivity(data);$scope.counterAct = data}else{$scope.cancelAllNotification}});
getNewAct.getNew(function(data) {if(data!=0){$scope.addNotification(data);$scope.counter = data}else{$scope.cancelAllNotification}});
and this is for example my .factory getNewMsg:
.factory('getNewMsg', function($http, $localstorage, $timeout){
return {
getNew: function(callback){
var user_id = $localstorage.get('user_id');
var timer;
function myLoop(){
timer = $timeout(function(){console.log( "Timeout executed")},5000);
timer.then(
function(){$http.post('http://www.digitalxp.it/appwork/include/check_msg.asp?id='+user_id).success(function(data){callback(data)}).error(function(){alert("Errore di comunicazione!")});myLoop()},
function() {console.log("Timer rejected!")}
);
}
myLoop();
}}
})
my problem is that I've to call the controller each time to add the badge in the header, but I would check only one time and view the badge on all ion-view of the app! (also the side menu near message item!)
I think is it possible with directive, but I dont know how to start!
I've updated this to use broadcas as you've requested, now your controller will be registered to the event newMessageReceived and will update every 5 seconds. Also, this acts more as a service, so I've updated the module to be a service and not a factory.
Please note that I changed timer.then(setNew, handleError); to timer.then(setNew(), handleError);
.controller('checkNew', function($scope, messageService, getNewAct, $localstorage, $cordovaLocalNotification, $http, $state) {
//register to broadcast and update when the messages are updated
messageService.newMessageReceived($scope, updateMessage)
function updateMessage(message) {
if(message){
$scope.addActivity(message);
$scope.counterAct = message;
} else {
$scope.cancelAllNotification
}
}
});
.service('messageService', function($rootScope, $http, $localstorage, $timeout){
/* Initialization */
//setup timer to run every 5 seconds
var timer = $timeout(timeoutFunction, 5000);
//set the actions for resolve and reject
timer.then(setNew(), handleError);
function timeoutFunction() {
return;
}
function handleError() {
console.log('Timer rejected');
}
//every time the newMessageRecieved event is broadcasted, run the callback and register the scope
function newMessageReceived(scope, callback) {
callback(getNew());
scope.$on('newMessageReceived', function() {
callback(getNew());
});
}
function getNew() {
return $localstorage && $localstorage.message || undefined;
}
function setNew() {
var user_id = $localstorage.get('user_id');
return $http.post('http://www.digitalxp.it/appwork/include/check_msg.asp?id='+user_id)
.success(function(data){
$localStorage.message = data;
$rootScope.$broadcast('newMessageReceived');
})
.error(function(){alert("Errore di comunicazione!")})
}
return {
newMessageReceived: newMessageReceived
};
});
i am working with angular and Parse.com and I have a problem:
I have a "mainController" that its in all the html document and a routeProvider with some pages and controllers.
function mainController($scope)
{
$scope.logeado = (Parse.User.current()!=null)
if (Parse.User.current()==null)
{
$scope.showuser=""
}
else
{
$scope.showuser=Parse.User.current().get('username')
}
$scope.logout = function()
{
Parse.User.logOut();
$scope.logeado = (Parse.User.current()!=null);
$scope.$apply();
}
};
One of this controller is "loginController". In this controller make the login, but my problem is:
If i logout, the function is in the maincontroller and this ng-show="logeado" change because the variable is inside, but if i login since logincontroller, i can change "Parse.User.current()" but $scope.logeado isn't update, and i need update all the page to see the changes.
How can i do to update de variable?
One of the way to solve problem is to implement pattern observer. Let
Parse.User.current notify about his state.
function mainController($scope){
Parse.User.current.onChange(function () {
$scope.logeado = (Parse.User.current()!=null);
if (Parse.User.current() == null) {
$scope.showuser=""
} else {
$scope.showuser=Parse.User.current().get('username')
}
});
$scope.logout = function() {
Parse.User.logOut();
};
};
Finally, I have make a function in mainController($scope):
$scope.login2= function(){
$scope.logeado = (Parse.User.current()!=null);
if (Parse.User.current() == null)
{
$scope.showuser=""
}
else
{
$scope.showuser=Parse.User.current().get('username')
}
};
and call it since loginController($scope) because $scope.login2 is inherited in this.
I'm trying to pass in a $scope that I want to change the value on.
These are two switch button on the same page that.
View
<div class="input">
<switch state="false" toggle="user.emailNotification">
<flip></flip>
</switch>
</div>
<div ng-click="saveNotifySettings()" class="button fill primary">Vista</div>
Controller
app.controller('notifySettingCtrl', function($scope, Users){
Users.get().$promise.then(function(data) {
$scope.user = data;
console.log($scope.user.emailNotification);
});
$scope.saveNotifySettings = function() {
console.log("PUT the new value for the switch");
};
});
When I press the button the state changes from false to true
First I want to init the state with the existing value from $scope.user.emailNotification and then change it and pass the changes to the controller.
The beginning of the my directive.
switch directive
(function() {
define(['angular', 'app', 'underscore'], function(angular, app, _) {
app.directive('switch', function() {
return {
restrict: 'E',
scope: {
value: '&toggle',
},
link: function(scope, element) {
var x = scope.value();
element.bind('mouseenter', function() {
console.log(x);
});
var $element = angular.element(element),
uniqueId = _.uniqueId('switch-'),
state = $element.attr('state');
state = typeof state === 'undefined' ? false : state;
$element.data('uniqueId',uniqueId)
.attr('state',state)
.attr('alive',true);
$element.on('click.'+uniqueId,function(){
if (state === true) {
$(this).attr('state',false);
state = false;
} else {
$(this).attr('state',true);
state = true;
}
});
}
};
});
});
}).call(this);
I'm very new at creating directives, so any help is well appreciated.
I have spend way to much time at this and it getting frustrating :/
Update:
I have created http://jsfiddle.net/HuLn4/. I have taken the pointers from you and refactored.
TLDR: I'm trying to create directive by sending in the model that I want to change in this case user.emailNotification and when the element is clicked on it changes the value from true/false and false/true and stores it back to the controllers $scope.user so it can be PUT to server and the state attribute is only to tell the look on the switch and for the initialize on the button ( on / off ).
I created a service that shares a confirm modal method, and allow to broadcast methods between controillers.
services.SharedService = function($rootScope, $modal) {
var sharedService = {
controller : '',
method : '',
args : []
};
sharedService.prepare = function(controller, method){
this.controller = controller;
this.method = method;
this.broadCast();
};
sharedService.broadCast = function(){
$rootScope.$broadcast('menuBroadcast');
};
return sharedService;
});
Then I have three controllers :
controllers.ctrl1 = function($scope, $rootScope, SharedService) {
$rootScope.$on('menuBroadcast', function() {
if (SharedService.controller == 'ctrl1') {
$scope[SharedService.method]();
}
});
$scope.delete = function(){
var c = window.confirm("Are you sure you want to delete it?");
if(c === true){
//delete
}
};
};
and
controllers.ctrl2 = function($scope, $rootScope, SharedService) {
$rootScope.$on('menuBroadcast', function() {
if (SharedService.controller == 'ctrl1') {
$scope[SharedService.method]();
}
});
$scope.delete = function(){
var c = window.confirm("Are you sure you want to delete it?");
if(c === true){
//delete
}
};
};
};
controllers.menu = function($scope, SharedService) {
$scope.delete1 = function() {
console.debug("Calling delete 1");
SharedService.prepare('ctrl1', 'delete');
};
$scope.delete2 = function() {
console.debug("Calling delete 2");
SharedService.prepare('ctrl2', 'delete');
};
}
The first time I open the confirm from ctrl1, clicking on the ok button works as expected. I can open this modal as many times, it will work.
Then, switching to ctrl2, I open the confirm , I have to click two times on the ok button to be able to close the confirm box.
The console debugs shows that the "calling delete1" and "calling delete2" are triggered only once. But the console.debug from on("menuBroadcast") is triggered sometimes up to 3 times. Any idea why the service triggers the event more than one time? When injected, is it instantiated more than once?
Duplicate of
How can I unregister a broadcast event to rootscope in AngularJS?
The controller is instantiated more than once . I have to un-register the $rootScope.$on listener on when the $destroy is invoked.
var cleanUpFunc = $rootScope.$on('menuBroadcast', function {
if (SharedService.controller == 'ctrl1') {
$scope[SharedService.method]();
}
});
$scope.$on('$destroy', function() {
cleanUpFunc();
});