How to Forward $scope from Controller to Controller AngularJS? - angularjs

I am New to Angular Js.
Is It Possible to Forward Scope of the RestaurantController to MenuController Code as Follows
Example :-
angular.module('restaurants').controller('RestaurantController', ['$scope',
function($scope) {
$scope.restaurantid="435scvcxvbrcvbnvn";
}
]);
And i assigned restaurant id as New scope in Menu Controller as Follows
angular.module('menus').controller('MenuController', ['$scope',
function($scope) {
$scope.currentrestaurantid= $scope.restaurantid;
alert($scope.currentrestaurantid); // showing Null
}
]);
The Restaurant Id is not Persisted .. Honestly i feel that some thing is Missing .
How to Get the Id From Restaurant controller to Menu Controller ?

Try inheritance
angular.module('restaurants', []);
angular.module('menus', ['restaurants']);
angular.module('restaurants').controller('RestaurantController', function($scope) {
$scope.restaurantid="435scvcxvbrcvbnvn";
});
angular.module('menus').controller('MenuController', ['$scope','$controller',
function($scope, $controller) {
$controller('RestaurantController', {$scope: $scope});
$scope.currentrestaurantid= $scope.restaurantid;
}
]);
Working fiddle

I propose use the rootScope to do that.
Use the $broadcast to notify the other controller the change on the firsts controller scope.
$rootScope.$broadcast("restIDUpdated", {
restaurant: $scope.restaurantid
});
Use the $on to receive the notification in the second controller about the event that happend in the first controller.
$scope.$on("restIDUpdated", function (event, args) {
$scope.restaurant = args.restaurant;
});
There is an example of this here
Try something like this.
angular.module('restaurants').controller('RestaurantController', function($scope) {
$rootScope.$broadcast("restIDUpdated", {
restaurantid: 435scvcxvbrcvbnvn
});
});
angular.module('menus').controller('MenuController', ['$scope','$controller',
function($scope, $controller) {
$controller('RestaurantController', {$scope: $scope});
$scope.$on("restIDUpdated", function (event, args) {
$scope.currentrestaurantid= args.restaurantid;
});
}
]);
But to be honest I am not sure if this mechanism works with two different angular apps, I know using the same module it works but not sure what is going to happen using two different modules, but take a look at the API

A pattern I commonly use is to create an angular service and inject it into controllers I want to share data with. something like this...
angular.module('restaurants', []);
angular.module('menus', ['restaurants']);
angular.module('restaurants').service('RestaurantService', function() {
this.restaurantid = "435scvcxvbrcvbnvn";
});
angular.module('restaurants').controller('RestaurantController', function($scope, RestaurantService) {
$scope.restaurantid = RestaurantService.restaurantid;
});
angular.module('menus').controller('MenuController', function($scope, $controller, RestaurantService) {
$scope.currentrestaurantid = RestaurantService.restaurantid;
});

As said here, injectable service as generic way for your situation.
But I'd like to point more pretty way, using markup and controller as (docs) expression.
For example you have following controller:
.controller('RestaurantController', function($scope) {
$scope.restaurantid="435scvcxvbrcvbnvn";
})
and you can bind it to variable in the scope:
<div ng-controller="RestaurantController as restaurant">
<div ng-controller="MenuControllerOrAnyOther">
restaurant id = {{restaurant.restaurantid}}
</div>
</div>
Remarks
This approach would be nicer if you need restaurantid ONLY in markup, e.g.
<button ng-click="select(restaurant.restaurantid)">
This looks ugly, if you want to use restaurantid in js code (you should use injectable services then):
var restaurantid = $scope.$eval('restaurant.restaurantid');

Related

Sharing variable globally

In my main controller I have a user service that returns the current user.
// Curr user
userService.getCurrentUser().then(user => {
mainCtrl.currUser = user;
});
Is there any way to be able to use the currUser variable in other controllers without injecting my service and calling this method over and over again in maybe 50 controllers?
Ex.
// My other awesome controller
console.log(currUser.fullName);
You could go for a LocalStorage or $rootScope. , if you are sure about not using Providers/Services
Sample:
myApp.controller('DemoController1', ['$scope', '$rootScope', function DemoController($scope,$rootScope) {
$rootScope.currUser.fullName ="test";
}]);
Then access it as,
myApp.controller('DemoController2', ['$scope', '$rootScope', function DemoController($scope,$rootScope) {
var fullName = $rootScope.currUser.fullName;
}])
you can use $rootScope like bellow:
mainCtrl.$rootScope.currUser = user;
and the other controller, you can recover this as bellow:
console.log(yourContrl.$rootScope.currUser.fullName);
You don't want you use controller injections? Use injector its the same like injections but also different :D ... like in this runnable fiddle demo. In that way you still can use services, factories, or components.
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.name = angular.injector(['data']).get('user').currUser.username;
});
angular.module('data', []).factory('user', function () {
return {
currUser: {
username: 'Alfred'
}
}
});

How do I use services in Angular to store variables?

How do I use services to store variables so that I can access it in previous controller on pressing back button?
For example:
.controller('myCtrl', function($scope, myservice) {
console.log(myservice.myvar.myval)
// output: undefined pressing back button
});
.controller('myCtrl2', function($scope, myservice) {
myservice.myvar.myval = "This I want in controller myCtrl on pressing back button"
console.log(myservice.myvar.myval)
// outputs the value
});
If services are not the best approach and I should use $rootScopethen will it do the job.
P.S angular.module and myservice are defined.
You need to use getter-setter for this.
For Example:
angular.module('MyModule', [])
.service('myservice', function () {
this.myval = "value";
this.getValue = function () { return this.myval }; //getter
this.setValue = function (val) { this.myval = val }; //setter
})
.controller('myCtrl', function($scope, myservice) {
console.log(myservice.getValue());
// get the value here
});
.controller('myCtrl2', function($scope, myservice) {
myservice.setValue("This I want in controller myCtrl on pressing back button");
// Set the value here
});
This is for the reference only. Please don't copy-paste and run code.
Angular services are singletons and can be freely shared among components, which have access to dependency injection. You can define a closure with a module pattern of getters and setters or directly attach properties to the object you intend to return.
I see 4 options, you can store it in a service, in $rootScope, in localStorage for persistent storage, and in sessionStorage to store datas only during the application lifetime. There's btw a nice module that wrap localStorage and sessionStorage, and make its use simpler :
http://ngmodules.org/modules/ngStorage

controller function not called from ng-click

I have a controller that ive added a function to but cant get the function to be called from ng-click on an anchor. Ive looked at similar posts but cant really see what I could be missing. Its as though the controller function cant be seen?
The module and controller:
var commonModule = angular.module('common', ['ngRoute', 'ng.ckeditor']);
var mainModule = angular.module('main', ['common']);
//mainModule.config(function ($routeProvider, $locationProvider) {
// $locationProvider.html5Mode(true);
//});
commonModule.factory('viewModelHelper', function ($http, $q, $window, $location) {
return MyApp.viewModelHelper($http, $q, $window, $location);
});
commonModule.factory('validator', function () {
return valJs.validator();
});
mainModule.controller("rootViewModel",
function ($scope, $http, $q, $routeParams, $window, $location, viewModelHelper, $rootElement) {
//test
console.log("creating controller");
var self = this;
viewModelHelper.apiGet('api/PageContent/1', null,
function (result) {
$scope.htmlEditor = result.data;
});
$scope.ToggleEditor2 = function () {
//test
console.log("hello");
if ($scope.editorVisible == true) {
$scope.editorVisible = false;
}
else {
$scope.editorVisible = true;
}
}
});
The controller is referenced at the root level of the page:
Which in this case since im using ASP.Net MVC, is in my _layout.cshtml
<body data-ng-app="main" data-ng-controller="rootViewModel">
In a (mvc) view that gets loaded, I have a button with ng-click that calls the ToggleEditor2 function, but its never called. Cant get a breakpoint to hit in the chrome dev console and I dont see anything written to the log either.
<input type="button" ng-click="ToggleEditor2()" value="test me" />
Update:
If I wrap that anchor with a div and specify the "rootviewModel" controller there, the log message gets written. Hmmm - something tells me its related to scope?
<div data-ng-controller="rootViewModel">
<input type="button" ng-click="ToggleEditor2()" value="test me" />
</div>
Problem
A common problem with $scope is that when using any sort of nested controllers or modules, your $scope's can step on each other and cause issues like you have experienced.
Solution
Using Angular's "Controller As" syntax is the recommended solution for this problem. It allows you to create multiple instances of the same controller, while defining and maintaining a unique scope for each instance.
This Article is a great resource I used to understand and implement this new syntax.

Call a method of a controller from another controller using 'scope' in AngularJS

I am trying to call a method of second controller in first controller by using scope variable. This is a method in my first controller:
$scope.initRestId = function(){
var catapp = document.getElementById('SecondApp');
var catscope = angular.element(catapp).scope();
catscope.rest_id = $scope.user.username;
catscope.getMainCategories();
};
I am able to set the value of rest_id but I cannot call getMainCategories for some reason. The console shows this error:
TypeError: Object # has no method 'getMainCategories'
Is there a way to call the above method?
Edit:
I used the following approach to load two apps at the same time;
angular.bootstrap(document.getElementById('firstAppID'), ['firstApp']);
angular.bootstrap(document.getElementById('secondAppID'), ['secondApp']);
I could definitely use a service here, but I wanted to know if there are any other options to do the same!
The best approach for you to communicate between the two controllers is to use events.
Scope Documentation
In this check out $on, $broadcast and $emit.
In general use case the usage of angular.element(catapp).scope() was designed for use outside the angular controllers, like within jquery events.
Ideally in your usage you would write an event in controller 1 as:
$scope.$on("myEvent", function (event, args) {
$scope.rest_id = args.username;
$scope.getMainCategories();
});
And in the second controller you'd just do
$scope.initRestId = function(){
$scope.$broadcast("myEvent", {username: $scope.user.username });
};
Edit: Realised it was communication between two modules
Can you try including the firstApp module as a dependency to the secondApp where you declare the angular.module. That way you can communicate to the other app.
Here is good Demo in Fiddle how to use shared service in directive and other controllers through $scope.$on
HTML
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">BROADCAST</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
<my-component ng-model="message"></my-component>
JS
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
By the same way we can use shared service in directive. We can implement controller section into directive and use $scope.$on
myModule.directive('myComponent', function(mySharedService) {
return {
restrict: 'E',
controller: function($scope, $attrs, mySharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'Directive: ' + mySharedService.message;
});
},
replace: true,
template: '<input>'
};
});
And here three our controllers where ControllerZero used as trigger to invoke prepForBroadcast
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
The ControllerOne and ControllerTwo listen message change by using $scope.$on handler.
Each controller has it's own scope(s) so that's causing your issue.
Having two controllers that want access to the same data is a classic sign that you want a service. The angular team recommends thin controllers that are just glue between views and services. And specifically- "services should hold shared state across controllers".
Happily, there's a nice 15-minute video describing exactly this (controller communication via services): video
One of the original author's of Angular, Misko Hevery, discusses this recommendation (of using services in this situation) in his talk entitled Angular Best Practices (skip to 28:08 for this topic, although I very highly recommended watching the whole talk).
You can use events, but they are designed just for communication between two parties that want to be decoupled. In the above video, Misko notes how they can make your app more fragile. "Most of the time injecting services and doing direct communication is preferred and more robust". (Check out the above link starting at 53:37 to hear him talk about this)

How to reuse one controller for 2 different views?

I have defined one controller, and apply it to 2 views with small differences.
Angular code:
app.controller('MyCtrl', function($scope) {
$scope.canSave = false;
$scope.demo = {
files : [{
filename: 'aaa.html',
source: '<div>aaa</div>'
}, {
filename: 'bbb.html',
source: '<div>bbb</div>'
}]
}
$scope.newFile = function(file) {
$scope.demo.files.push(file);
}
$scope.$watch("demo.files", function(val) {
$scope.canSave = true;
}, true);
});
View 1:
<div ng-controller="MyCtrl"></div>
View 2:
<div ng-controller="MyCtrl"></div>
The sample code is very simple, but there are a lot of code and logic in my real project.
The View 1 and 2 have almost the same features, only with a few differences, but I do need to write some code for each of them in the controller.
I don't want to create 2 different controllers for them, because they have most of same logic. I don't want to move the logic to a service to share it between the 2 controllers, because the logic is not that common to be a service.
Is there any other way to do it?
Under the given conditions I might be doing something like
function MyCommonCtrl(type){
return function($scope, $http) {
$scope.x = 5;
if(type = 't1'){
$scope.domore = function(){
}
}
....
....
}
}
angular.module('ng').controller('Type1Ctrl', ['$scope', '$http', MyCommonCtrl('t1')]);
angular.module('ng').controller('Type2Ctrl', ['$scope', '$http', MyCommonCtrl('t2')]);
Then
<div ng-controller="Type1Ctrl"></div>
and
<div ng-controller="Type2Ctrl"></div>
I don't know your specific set-up but your 2 controllers could inherit from a common ancestor.
Type1Ctrl.prototype = new MyCtrl();
Type1Ctrl.prototype.constructor = Type1Ctrl;
function Type1Ctrl() {
// constructor stuff goes here
}
Type1Ctrl.prototype.setScope = function() {
// setScope
};
Type2Ctrl.prototype = new MyCtrl();
Type2Ctrl.prototype.constructor = Type2Ctrl;
function Type2Ctrl() {
// constructor stuff goes here
}
Type2Ctrl.prototype.setScope = function() {
// setScope
};
I also faced similar problem and scope inheritance solved my problem.
I wanted to "reuse" a controller to inherit common state/model ($scope) and functionality (controller functions attached to $scope)
As described in the "Scope Inheritance Example" I attach parent controller to an outer DOM element and child controller to the inner. Scope and functions of parent controller "merge" seamlessly into the child one.
Here is another option. Slightly modified from this blog post
app.factory('ParentCtrl',function(){
$scope.parentVar = 'I am from the parent'
};
});
app.controller('ChildCtrl', function($scope, $injector, ParentCtrl) {
$injector.invoke(ParentCtrl, this, {$scope: $scope});
});
here is a plunker

Resources