Automatically update id variable in two controllers using $broadcast in AngularJS - angularjs

$ctrl.clicker = function(id)
{
$rootScope.$broadcast('idBull', id);
}
When I mouseenter an image the above function gets called. I want to share the id in another controller and broadcast whatever changes where made to this id.
$scope.$on('idBull', function (event, data) {
console.log(data); // 'Data to send'
});
In the other controller I used the code to do a console loge of my id but got no results.

http://jsfiddle.net/87rLob9x/
Check this fiddle hope it helps
html
<html ng-app="myApp">
<div ng-controller='ControllerA'>
<button ng-click='add()'>Add</button
</div>
<div ng-controller='ControllerB'>
{{ increment }}
</div>
</html>
js:
var app = angular.module('myApp', [])
.controller('ControllerA', function($scope) {
$scope.increment = 0;
$scope.add = function() {
$scope.$broadcast('hasIncremented');
}
}).
controller('ControllerB', function($scope) {
$scope.$on('hasIncremented', function(event) {
$scope.increment++;
});
})

Not sure why you are not getting your code to work, maybe the controller with $scope.$on is not created/loaded when the $rootScope.$broadcast is executed?
Another solution is to use a service that you inject into both controllers and use that for communication instead. Example of broadcast solution:
var app = angular.module("app", [])
.controller("ControllerA", function($scope, $rootScope)
{
$scope.clicker = function(id)
{
$rootScope.$broadcast("id changed", id);
}
})
.controller("ControllerB", function($scope)
{
$scope.$on("id changed", function(event, id)
{
// Do whatever you need to do with id
});
});
Example of solution with custom service:
var app = angular.module("app", [])
.factory("customService", function()
{
var callbacks = [];
return {
onIdChange: function(callback)
{
callbacks.push(callback);
},
idChanged: function(id)
{
callbacks.forEach(function(callback)
{
callback(id);
});
}
};
})
.controller("ControllerA", function($scope, customService)
{
$scope.clicker = function(id)
{
customService.idChanged(id);
}
})
.controller("ControllerB", function(customService)
{
customService.onIdChange(function(id)
{
// Do whatever you need to do with id
});
});

Related

How to pass data from one controller to another controller in ONSEN UI angular?

i have a html like below
<a class="event_detail_btn_a" ng-click="foo()"></a>
following is my controller
onsenApp.controller("EventDetailController", function ($scope) {
$scope.foo = function(){
alert("foo!");
var options = {
data: {
title: 'Another Page'
}
};
};
});
i want to pass "options" variable to another controller from this controller.
how can i do it ?
Use $broadcast to pass data between controllers.
In the example add() is defined in Controller1 which is passed to Controller2 after 2 secs.
angular
.module("app", [])
.controller("Controller1", function($scope, $rootScope, $timeout){
var add = function(a, b) {
return a + b;
}
$timeout(function(){
$rootScope.$broadcast("Add function", add);
}, 2000);
})
.controller("Controller2", function($scope, $rootScope, $timeout){
$scope.$on("Add function", function(event, add) {
$scope.sum = add(3, 4);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="Controller1">
<span>Add function defined in Controller1</span><br /><br />
</div>
<div ng-controller="Controller2">
<span ng-show="sum">3 + 4 = {{ sum }}</span>
<span ng-show="!sum">Controller2 waiting for add function...</span><br />
</div>
</div>
Best way to create a service OR a factory and inject in both controllers like below:
var myApp = angular.module('myApp', []);
myApp.factory('MyService', function() {
var options = {
data: {
title: 'Another Page'
}
};
return {
options
};
});
myApp.controller('FirstCtrl', function($scope, MyService) {
$scope.options = MyService.options;
});
myApp.controller('SecondCtrl', function($scope, MyService) {
$scope.options = MyService.options;
});
You want to pass data to another controller, the way i am getting it is :
onsenApp.controller("EventDetailController", function ($scope) {
$scope.foo = function(){
alert("foo!");
var options = {
data: {
title: 'Another Page'
}
};
this is one way you can achieve this using event push-page with it you send data.
myNavigator.pushPage('xyz.html',
{myData: options} );
}
};
});
onsenApp.controller("SecondController", function ($scope) {
here you can get your desired data
$scope.options = myNavigator.getCurrentPage().options.myData;
});

How to call a method from a controller to another controller in angular js

I have a view for SidebarController like below -
<a ng-click="reachMe($event);$event.preventDefault()" ng-href="#/app/hello">
Before going to the link I want to call reachMe() to check some changes on page and need to show an alert if any changes made
function SidebarController($rootScope, $scope, $state, $location, SidebarLoader){
$scope.reachMe = function(event){
//here I want to call function isPageChanged() from StaticPageController
//something like this
// if StaticPageController.isPageChanged() return true
// then show alert
// else
// $location.url($href)
}
}
Update 1 :
Not sure about this, But give it a try.
<div ng-app="testApp" ng-controller="ControllerOne">
<button ng-click="methodA();"> Call Another Controller</button>
</div>
<script>
var app = angular.module('testApp', []);
app.controller('ControllerOne', function($scope, $rootScope) {
$scope.reachMe = function() {
var arrayData = [1,2,3];
$rootScope.$emit('callEvent', arrayData);
if($rootScope.isChanged){
// Show Alert
}else{
//Go to route
}
}
});
app.controller('ControllerTwo', function($scope, $rootScope,$state) {
$scope.checkSomethingChanged = function() {
alert("Hello");
$rootScope.isChanged = true;
}
$rootScope.$on('callEvent', function(event, data) {
console.log(data);
$scope.checkSomethingChanged();
});
});
Following method worked for me perfectly :
<div ng-app="testApp" ng-controller="ControllerOne">
<button ng-click="methodA();"> Call Another Controller</button>
</div>
<script>
var app = angular.module('testApp', []);
app.controller('ControllerOne', function($scope, $rootScope) {
$scope.methodA = function() {
var arrayData = [1,2,3];
$rootScope.$emit('callEvent', arrayData);
}
});
app.controller('ControllerTwo', function($scope, $rootScope) {
$scope.reachMe = function() {
alert("Hello");
}
$rootScope.$on('callEvent', function(event, data) {
console.log(data);
$scope.reachMe();
});
});
</script>
A controller is not the right concept for sharing functionality. Use a Factory or Service for that.
var logicFactory = function () {
return {
methodA: function () {
},
methodB: function()
{
}
};
}
You can then inject that factory into each controller where it is needed like:
var ControllerA = function ($scope,logicFactory) {
$scope.logic = logicFactory;
}
ControllerA.$inject = ['$scope', 'logicFactory'];
Another option is to use the broadcast/emit Patern. But I would use that only where really necessary:
Usage of $broadcast(), $emit() And $on() in AngularJS

Issues updating angular view from another controller

In the below, I have a sidenav and a main content section. I am trying to call the sidenav function from the main controller so the number updtes in the sidenav. What am I doing wrong here?
This is my view.
<div>Teams {{ teams.length }} </div>
This is my sidebar controller:
angular.module('myApp').controller('leftNav', function($scope, myFactory) {
myFactory.getTeams().then(function(res) {
$scope.teams = res.data;
})
This is my factory
angular.module('myApp').factory('myFactory', function($http) {
return {
getTeams : function() {
return $http.get('/teams').then(function(res) {
return res;
});
}
};
});
This is a complete different controller:
angular.module('myApp').controller('main', function($scope,$http, myFactory) {
$http.post(...blablabl..).then(function() {
// What can i call here to update the sidenav above?
});
});
You can utilise $controller service to call your leftNav controller from your main controller like below
angular.module('myApp').controller('main', function($scope,$http, myFactory,$controller) {
$http.post(...blablabl..).then(function() {
var leftCTRL= $controller('leftNav', { $scope: $scope.$new() });
});
});
you can try like so :
angular.module('myApp').controller('leftNav', function($scope, myFactory) {
myFactory.getTeams().then(function(res) {
this.teams = res.data;
})
angular.module('myApp').controller('main', function($scope,$http, $controller) {
var leftNav= $controller('leftNav');
$http.post(...blablabl..).then(function(res) {
leftNav.teams = res.data;
});
});

Pass value in-between angular JS controller and services

I am trying to get the user query from html using ng-click. I want to make a https call using the value which I fetch from ng-click. I can see the data in Alert--1 but in Alert--2 i get undefined. on internet I read that passing values using services is the best practice.Please correct me if I am wrong.
My controller
mainApp.controller('searchController',function($scope,searchString){
$scope.getQuery = function(userq) // its ng-click="getQuery(userq)" on Search button
{
$scope.userq=userq;
alert('Alert--1'+userq); // its working fine
searchString.setSearchString(userq);
};
});
//====================
mainApp.controller('fetchQueryResultController',function($scope,searchString){
var searchStr = searchString.getSearchString();
alert('Alert--2--'+searchStr); // Undefined
// Later I'll use this value to fetch data from Watson search(Django) using $https call
});
My service:
mainApp.factory('searchString', function () {
var qVal ;
return {
setSearchString:function (query) {
qVal = query;
},
getSearchString:function () {
return qVal;
}
};
});
Routing:
.when('/search', {
templateUrl: "../static/views/seachResult.html",
controller: "fetchQueryResultController"
})
Is there any simpler way?
Using a service is OK. Take a look at this, is quite clear for begginers:
https://egghead.io/lessons/angularjs-sharing-data-between-controllers
alert('Alert--2'+searchStr); is showing undefined because it is being executed before $scope.getQuery obviously. Controller's initialization is done before ng-init evaluates the expression.
In your case I believe it is better to fire an event when the data is set, so the second controller gets notified. This is being done with $on and $emit.
Here is a plunker with your example: http://plnkr.co/edit/97mVwbWmoOH3F7m8wbN0?p=preview
var app = angular.module('plunker', []);
app.controller('searchController',function($scope,searchString){
$scope.searchText;
$scope.getQuery = function(userq) // its ng-click="getQuery(userq)" on Search button
{
$scope.userq=userq;
alert('Alert--1'+userq); // its working fine
searchString.setSearchString(userq, $scope);
};
});
//====================
app.controller('fetchQueryResultController',function($scope, $rootScope, searchString){
var searchStr = searchString.getSearchString;
$scope.getData = function(){
searchStr = searchString.getSearchString();
alert('Alert--2--'+ searchStr);
}
$rootScope.$on('dataModified', function(){
$scope.getData();
});
});
//====================
app.factory('searchString', function ($rootScope) {
var qVal ;
return {
setSearchString:function (query) {
qVal = query;
$rootScope.$emit('dataModified');
},
getSearchString:function () {
return qVal;
}
};
});
this alert undefined
var searchStr = searchString.getSearchString();
alert('Alert--2'+searchStr);
becuase qVal hasn't set yet
qVal set when getQuery get called but that time alert2 already executed
A simple solution is to have your factory return an object and let your controllers work with a reference to the same object:
JS:
// declare the app with no dependencies
var myApp = angular.module('myApp', []);
myApp.factory('searchString', function() {
var qVal;
return {
setSearchString: function(query) {
qVal = query;
},
getSearchString: function() {
return qVal;
}
};
});
myApp.controller('setSearchController', function($scope, searchString) {
$scope.setQuery = function(userq) {
$scope.userq = userq;
searchString.setSearchString(userq);
};
});
myApp.controller('fetchQueryResultController', function($scope, searchString) {
$scope.getQuery = function(user) {
alert(searchString.getSearchString());
};
});
HTML:
<div ng-app="myApp">
<div ng-controller="setSearchController">
<button ng-click="setQuery('Shohel')">Set</button>
</div>
<hr />
<div ng-controller="fetchQueryResultController">
<button ng-click="getQuery()">Get</button>
</div>
</div>
Here is similar fiddle

Angular, show loading when any resource is in pending

I already write a code to display a loader div, when any resources is in pending, no matter it's getting via $http.get or routing \ ng-view.
I wan't only information if i'm going bad...
flowHandler service:
app.service('flowHandler', function(){
var count = 0;
this.init = function() { count++ };
this.end = function() { count-- };
this.take = function() { return count };
});
The MainCTRL append into <body ng-controller="MainCTRL">
app.controller("MainCTRL", function($scope, flowHandler){
var _this = this;
$scope.pageTitle = "MainCTRL";
$scope.menu = [];
$scope.loader = flowHandler.take();
$scope.$on("$routeChangeStart", function (event, next, current) {
flowHandler.init();
});
$scope.$on("$routeChangeSuccess", function (event, next, current) {
flowHandler.end();
});
updateLoader = function () {
$scope.$apply(function(){
$scope.loader = flowHandler.take();
});
};
setInterval(updateLoader, 100);
});
And some test controller when getting a data via $http.get:
app.controller("BodyCTRL", function($scope, $routeParams, $http, flowHandler){
var _this = this;
$scope.test = "git";
flowHandler.init();
$http.get('api/menu.php').then(function(data) {
flowHandler.end();
$scope.$parent.menu = data.data;
},function(error){flowHandler.end();});
});
now, I already inject flowHandler service to any controller, and init or end a flow.
It's good idea or its so freak bad ?
Any advice ? How you do it ?
You could easily implement something neat using e.g. any of Bootstrap's progressbars.
Let's say all your services returns promises.
// userService ($q)
app.factory('userService', function ($q) {
var user = {};
user.getUser = function () {
return $q.when("meh");
};
return user;
});
// roleService ($resource)
// not really a promise but you can access it using $promise, close-enough :)
app.factory('roleService', function ($resource) {
return $resource('role.json', {}, {
query: { method: 'GET' }
});
});
// ipService ($http)
app.factory('ipService', function ($http) {
return {
get: function () {
return $http.get('http://www.telize.com/jsonip');
}
};
});
Then you could apply $scope variable (let's say "loading") in your controller, that is changed when all your chained promises are resolved.
app.controller('MainCtrl', function ($scope, userService, roleService, ipService) {
_.extend($scope, {
loading: false,
data: { user: null, role: null, ip: null}
});
// Initiliaze scope data
function initialize() {
// signal we are retrieving data
$scope.loading = true;
// get user
userService.getUser().then(function (data) {
$scope.data.user = data;
// then apply role
}).then(roleService.query().$promise.then(function (data) {
$scope.data.role = data.role;
// and get user's ip
}).then(ipService.get).then(function (response) {
$scope.data.ip = response.data.ip;
// signal load complete
}).finally(function () {
$scope.loading = false;
}));
}
initialize();
$scope.refresh = function () {
initialize();
};
});
Then your template could look like.
<body ng-controller="MainCtrl">
<h3>Loading indicator example, using promises</h3>
<div ng-show="loading" class="progress">
<div class="progress-bar progress-bar-striped active" style="width: 100%">
Loading, please wait...
</div>
</div>
<div ng-show="!loading">
<div>User: {{ data.user }}, {{ data.role }}</div>
<div>IP: {{ data.ip }}</div>
<br>
<button class="button" ng-click="refresh();">Refresh</button>
</div>
This gives you two "states", one for loading...
...and other for all-complete.
Of course this is not a "real world example" but maybe something to consider. You could also refactor this "loading bar" into it's own directive, which you could then use easily in templates, e.g.
//Usage: <loading-indicator is-loading="{{ loading }}"></loading-indicator>
/* loading indicator */
app.directive('loadingIndicator', function () {
return {
restrict: 'E',
scope: {
isLoading: '#'
},
link: function (scope) {
scope.$watch('isLoading', function (val) {
scope.isLoading = val;
});
},
template: '<div ng-show="isLoading" class="progress">' +
' <div class="progress-bar progress-bar-striped active" style="width: 100%">' +
' Loading, please wait...' +
' </div>' +
'</div>'
};
});
Related plunker here http://plnkr.co/edit/yMswXU
I suggest you to take a look at $http's pendingRequest propertie
https://docs.angularjs.org/api/ng/service/$http
As the name says, its an array of requests still pending. So you can iterate this array watching for an specific URL and return true if it is still pending.
Then you could have a div showing a loading bar with a ng-show attribute that watches this function
I would also encapsulate this requests in a Factory or Service so my code would look like this:
//Service that handles requests
angular.module('myApp')
.factory('MyService', ['$http', function($http){
var Service = {};
Service.requestingSomeURL = function(){
for (var i = http.pendingRequests.length - 1; i >= 0; i--) {
if($http.pendingRequests[i].url === ('/someURL')) return true;
}
return false;
}
return Service;
}]);
//Controller
angular.module('myApp')
.controller('MainCtrl', ['$scope', 'MyService', function($scope, MyService){
$scope.pendingRequests = function(){
return MyService.requestingSomeURL();
}
}]);
And the HTML would be like
<div ng-show="pendingRequests()">
<div ng-include="'views/includes/loading.html'"></div>
</div>
I'd check out this project:
http://chieffancypants.github.io/angular-loading-bar/
It auto injects itself to watch $http calls and will display whenever they are happening. If you don't want to use it, you can at least look at its code to see how it works.
Its very simple and very useful :)
I used a base controller approach and it seems most simple from what i saw so far. Create a base controller:
angular.module('app')
.controller('BaseGenericCtrl', function ($http, $scope) {
$scope.$watch(function () {
return $http.pendingRequests.length;
}, function () {
var requestLength = $http.pendingRequests.length;
if (requestLength > 0)
$scope.loading = true;
else
$scope.loading = false;
});
});
Inject it into a controller
angular.extend(vm, $controller('BaseGenericCtrl', { $scope: $scope }));
I am actually also using error handling and adding authorization header using intercepting $httpProvider similar to this, and in this case you can use loading on rootScope
I used a simpler approach:
var controllers = angular.module('Controllers', []);
controllers.controller('ProjectListCtrl', [ '$scope', 'Project',
function($scope, Project) {
$scope.projects_loading = true;
$scope.projects = Project.query(function() {
$scope.projects_loading = false;
});
}]);
Where Project is a resource:
var Services = angular.module('Services', [ 'ngResource' ]);
Services.factory('Project', [ '$resource', function($resource) {
return $resource('../service/projects/:projectId.json', {}, {
query : {
method : 'GET',
params : {
projectId : '#id'
},
isArray : true
}
});
} ]);
And on the page I just included:
<a ng-show="projects_loading">Loading...</a>
<a ng-show="!projects_loading" ng-repeat="project in projects">
{{project.name}}
</a>
I guess, this way, there is no need to override the $promise of the resource

Resources