new to angularJS, after reading the Reference about Scope
If all controllers share a $rootScope in a ng-app, can we share a ng-model which is assigned to the $rootScope, so that controllers could communicate with each other?
Test with the following snippet, but gName failed to change when model in inputController changed, presume may be gName was made $scope.gName again when input changed. if that is true, is there any way to communicate the controller with each other? ie, the input could be displayed in other controllers?
var app = angular.module("myApp",[]);
app.controller("inputController", ["$rootScope", "$scope", function($rootScope, $scope){
$rootScope.gName = "Hello";
}])
.controller("displayController1", ["$rootScope", "$scope", function($rootScope, $scope){
}])
.controller("displayController2", ["$rootScope", "$scope", function($rootScope, $scope){
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<hr/>
<div ng-controller="inputController">
<input type="text" ng-model="gName"/> <br/>
<span>{{gName}}</span>
</div>
<hr/>
<div ng-controller="displayController1">
<span>{{gName}}</span>
</div>
<hr/>
<div ng-controller="displayController2">
<span>{{gName}}</span>
</div>
</body>
In AngularJS, $scope inherit from their parent scope (and in the end it will be rootScope)
In Angular, primitive types are overwritten when a child changes them. so in the input when changing gName it is keeping the $rootScope.gName and creating a new local $scope.gName that is only showing on that controller
to solve the problem you can add an object, and changing the property in it
var app = angular.module("myApp",[]);
app.controller("inputController", ["$rootScope", "$scope", function($rootScope, $scope){
$rootScope.prop = { gName: "Hello" };
}])
.controller("displayController1", ["$rootScope", "$scope", function($rootScope, $scope){
}])
.controller("displayController2", ["$rootScope", "$scope", function($rootScope, $scope){
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<hr/>
<div ng-controller="inputController">
<input type="text" ng-model="prop.gName"/> <br/>
<span>{{prop.gName}}</span>
</div>
<hr/>
<div ng-controller="displayController1">
<span>{{prop.gName}}</span>
</div>
<hr/>
<div ng-controller="displayController2">
<span>{{prop.gName}}</span>
</div>
</body>
Note I am only answering the question, this way is not recommended for making controllers communicate with each others, you should use services for that, you can check this guide
You do not want your controllers to communicate with each other, as the only purpose of a controller is to transform data from and for the view and to handle view-local logic (like hiding things with checkboxes, previewing calculated numbers and so on).
Your actual data and business logic should be in services, which also allows for multiple controllers to access it.
Share logics/data within your application and application controllers through services. The big advantage is that the digest cycle is not influenced by changes, and that your logics/data can easily be shared accross your application on a structured / seperated manner. Usage of service reduce pollution of your readability.
I try to only use the $rootScope for event broadcasting / listening and for data attachment on the main application frame (ex. data in the application outer frame).
AngularJS services are:
Lazily instantiated – AngularJS only instantiates a service when an application component depends on it.
Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.
A detailed example can be followed through a tutorial on https://thinkster.io/a-better-way-to-learn-angularjs/services
Related
I'm looking to sure up my understanding of dependency injection and make sure my understanding is correct. I'm following a course at the moment that takes 2 modules as examples, ngMessage vs. ngResource. Within my app module injection I include ['ngResource', 'ngMessage'].
Within the controller I inject the service $resource (for ngResource).
Then in my view html I can make use of the ngMessages directive $error.
My question is why doesn't ngMessage require any insertion in to the controller? Is it the difference between a service ($resource) vs directive ($error)?
I just want to make sure I'm clear on why ngMessage didn't require any form of injection in to the controller. If my thinking is right, all modules that are injected in to your app will have all directives immediately available in the view, but any services will need to be injected in to the controller.
app.js
var myApp = angular.module('myApp', ['ngResource']);
myApp.controller('mainController', function('$resource') {
console.log($resource)
});
<form name="myForm">
<input type = "text" ng-model="field" name="myField" required minlength="5"/>
<div ng-messages="myForm.myField.$error">
<div ng-message="required">You did not enter a field</div>
<div ng-message ="minlength">The value entered is too short</div>
</div>
</form>
Directive are available at the module level and can be used in any template in the module or module that import the module that declares it.
Services are injected as objects into your controllers for accessing data from them and sharing data between controllers.
I am developing single page application using partial views and angularJS.
I am having a scenario where I have to open same partial view multiple times with different data.
I tried to make controller name dynamic by adding app.directive and changing controller name both in js and html and then calls Angular-Complies method, but the issue is that it changes it for all previous tabs opened.
Then I tried to make multiple js files and changing controller name in partial view with Jquery but still does not helps. Here is my code.
HTML:
<div ng-app="myApp">
<!-- this is in view1.html -->
<div ng-controller="DashboardDesignerController">
<div ng-repeat="widget in workspace.widgets">stuff here
</div>
</div>
<!-- this is in view1.html -->
<div ng-controller="DashboardDesignerController">
<div ng-repeat="widget in workspace.widgets">stuff here
</div>
</div>
//Angular controller is in separate JS file.
app.controller("DashboardDesignerController" , function ($scope, $http,
$rootScope, $compile, $injector, $timeout) {
}
I expect to behave every tab separately according to its data but currently when I go to first opened tab, it's scope changed with the last opened tab.
Is there built in way of angularJS to doing it, Or some good approach will also appreciated.
Actually the issue was, I was calling chart drill down method in core JavaScript which was replacing $scope with last opened.
Resolved the issue by getting scope with JQuery
var scope= angular.element($('.k-state-active').find('.tempDiv')[0]).scope();
and called angular function with this scope variable.
Just see the code
var angApp = angular.module('angApp',[]);
angApp.controller('testController', ['$http', function ($http) {
var self = this;
self.name ='Hello';
self.btnClick=function(){
self.name ='Hello! Button Clicked';
}
}]);
<html>
<head>
</head>
<body data-ng-app="angApp" data-ng-controller="testController as model">
<div>
{{model.name}}
</br>
<input type="button" value="Click" data-ng-click="model.btnClick();"/>
</div>
</html>
Now, tell me if we avoid scope and declare controller like this way testController as model then what will be an advantage or is it only syntactic sugar?
Basically, $scope has been removed as of Angular 2. In addition to that, the angular documentation specifically recommends using this instead of $scope.
Take a look at this article for more information: https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/
And also check the accepted answer on this question: 'this' vs $scope in AngularJS controllers
Once advantage I can think about is, if you have nested controllers, for instance
<div ng-controller="myFirstController as ctrl1">
<div ng-controller="mySecondController as ctrl2">
{{ctrl1.someValue}}
</div>
</div>
It is easier and more clear when trying to reference a variable on the parent controller
Here's Plunker
I have an external template within in a controller with ng-include. It is shown and hidden based on click event of Button.It is working as required but with $parent in ng-include Template.Is there any other better way of doing this ?
Html
<body ng-controller="MainCtrl">
<div data-ng-include="'terms.html'" data-ng-show="otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{name}}!</p>
<button data-ng-click="mainPage=false; otherContent=true">Link to some Content</button>
</div>
</body>
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.mainPage=true;
});
External Template
<p>Some content here </p>
<button data-ng-click="$parent.mainPage=true; $parent.otherContent=false">Back</button>
Option1 - Set property on an object in the scope
In the main controller add an object on the scope.
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.page={mainPage:true};
});
and in the ng-click do:-
<div data-ng-include="'terms.html'" data-ng-show="page.otherContent"></div>
<div ng-show="page.mainPage">
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
<!-- -->
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
Demo - setting property on an object in the scope
Option2 - Use function
Instead of setting properties on the view (Which is anyways a good idea to abstract out too much logic from the view), Do your set operations in the controller exposed as a function that can be invoked from the view, which also gives extensibility when you need to add more logic for that particular action. And in your case you could even use the same function and call it from both the button clicks and flipped based on a boolean argument.
Demo - with function
Option3 - Use Controller Alias
Using an alias for the controller, which is nothing but instance of the controller is set as a property on the scope with the property name same as the alias provided. This will make sure you are enforce to use dot notation in your bindings and makes sure the properties you access on the child scopes with the controller alias are inherited as object reference from its parent and changes made are reflected both ways. With angular 1.3, it is also possibly to set the isolate scoped directive properties are bound to the controller instance automatically by setting bindToController property in the directive configuration.
Demo - With Controller alias
ControllerAs is the recommend way of avoiding this problem.
Using controller as makes it obvious which controller you are accessing in the template when multiple controllers apply to an element.
If you are writing your controllers as classes you have easier access to the properties and methods, which will appear on the scope, from inside the controller code.
Since there is always a . in the bindings, you don't have to worry about prototypal inheritance masking primitives.
<body ng-controller="MainCtrl as main">
<div data-ng-include="'terms.html'" data-ng-show="main.otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{main.name}}!</p>
<button data-ng-click="main.mainPage=false; main.otherContent=true">Link to some Content</button>
</div>
</body>
Here are some resources for controller as:
http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
http://toddmotto.com/digging-into-angulars-controller-as-syntax/
https://docs.angularjs.org/api/ng/directive/ngController#example
I want to share the data of one controller between two places in my page. For example:
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="x" /> {{x}}
</div>
</div>
<!-- these are in totally different places and I do not want, nor can't nest them -->
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="x" /> {{x}}
</div>
</div>
and of course, this:
var myApp = angular.module('myApp', []);
myApp.controller('myController', function($scope) {
$scope.x = 'test';
});
What can I do so that, no matter what I type first input text will be reflected in the second? And vice versa? Basically the same data being propagated to these two sections, while maintaining a single copy of the data.
JSFiddle here: http://jsfiddle.net/LETAd/
PS: I bootstrap it manually, if this is of any relevance.
Thanks!
To share data between controllers, normally a service is your best option. Put the shared data into the service, and inject the service into the controller:
function myController($scope, MyService) {
Each scope/controller instance will then be able to access the shared data.
Note that services are singletons, so there will only be one instance of your shared data around.
Here is a fiddle (I didn't write it) showing how two controllers can share data.
See also AngularJS: How can I pass variables between controllers? and Angularjs: two way data bindings and controller reload.
Ideally, you should have only one application running in a single page. Since you also need to communicate between the controllers, you should really run a single application. Possibly on body or html. Then you can create a main controller which would encapsulate all your controllers. (controller inheritance).
Here is what it should look like:
<html ng-app="myApp">
<head>...</head>
<body ng-controller="MainCtrl">
<div ng-controller="MyCtrl">
<input type="text" ng-model="mainView.x" /> {{x}}
</div>
<div ng-controller="MyCtrl">
<input type="text" ng-model="mainView.x" /> {{x}}
</div>
</body>
And JS:
function MainCtrl($scope) {
$scope.mainView = {};
}
function MyCtrl($scope) {
}
We created a mainView object on the MainController, and since MyController and its scope prototypally inherit from MainController we can reach that.
There is one caveat you should be aware of, when you use ngModel, it is almost always best to have a dot in somewhere (paraphrased from angularjs's authors).
Due to javascript's prototypal inheritance:
// In MainCtrl
$scope.mainView.x = "hello";
$scope.myX = "hello";
// In MyCtrl
$scope.mainView.x
>> "hello"
$scope.myX
>> "hello"
$scope.mainView.x = "welcome";
$scope.myX = "welcome";
// In MainCtrl
$scope.mainView.x
>> "welcome"
$scope.myX
>> "hello"
When you ask for a property in an object in javascript, it looks its properties to see if there is one, if not, it goes up in the prototype chain (parent), and looks for it there, it goes up until it finds any or goes at the end of the prototype chain.
So when we set $scope.myX, we don't actually change myX in the parent scope but we create a property called myX in the current scope; because of the hiearchy in the prototype. However, when we set $scope.mainView.x, we first ask for mainView then set x which then results in changing the value in parent scope.
I know it feels kind of unrelated to the original question but surely one would suffer from this when one goes into controller and scope inheritance.