I guess it is possible to have many angular-modules attached to different regions within one shellpage.
But can modules in AngularJS "talk" to each other?
If yes, how?
There are various ways module can interact or share information
A module can be injected into another module, in which case the container module has access to all elements of the injected module. If you look at angular seed project, modules are created for directive, controllers, filters etc, something like this
angular.module("myApp", ["myApp.filters", "myApp.services", "myApp.directives", "myApp.controllers"])
This is more of a re usability mechanism rather than communication mechanism.
The second option is as explained by #Eduard would be to use services. Since services are singleton and can be injected into any controller, they can act as a communication mechanism.
As #Eduard again pointed out the third option is to use parent controller using $scope object as it is available to all child controllers.
You can also inject $rootScope into controllers that need to interact and use the $broadcast and $on methods to create a service bus pattern where controllers interact using pub\sub mechanism.
I would lean towards 4th option. See some more details here too What's the correct way to communicate between controllers in AngularJS?
Using the service mechanism to communicate among module's controllers.
(function () {
'use strict';
//adding moduleB as dependency to moduleA
angular.module('Myapp.moduleA', ['Myapp.moduleB'])
.controller('FCtrl', FCtrl)
.service('sharedData', SharedData);
//adding the dependency shareData to FCtrl
FCtrl.$inject = ['sharedData'];
function FCtrl(sharedData) {
var vm = this;
vm.data = sharedData.data;
}
//shared data service
function SharedData() {
this.data = {
value: 'my shared data'
}
}
//second module
angular.module('Myapp.moduleB', [])
.controller('SCtrl', SCtrl);
SCtrl.$inject = ['sharedData'];
function SCtrl(sharedData) {
var vm = this;
vm.data = sharedData.data;
}
})();
And the HTML as follows:
<html ng-app="firstModule">
<body>
<div ng-controller="FCtrl as xyz">
<input type=text ng-model="xyz.data.value" />
</div>
<div ng-controller="SCtrl as abc">
<input type=text ng-model="abc.data.value" />
</div>
</body>
</html>
You can use services and controllers inheritance (explained here http://docs.angularjs.org/guide/dev_guide.mvc.understanding_controller)
in any case, you shuold consider not having your controllers tighlty coupled.
Related
I have an angular app where we have 4 different pages.Each page has its own controller. There is an home page which has a controller which routes to each page and its controller using
when('/a',{
templateUrl: './components/a.html',
controller:'aCtrl'
}).
when('/b',{
templateUrl: './components/b.html',
controller:'bCtrl'
}).
when('/c',{
templateUrl: './components/c.html',
controller:'cCtrl'
}).
when('/d',{
templateUrl: './components/d.html',
controller:'dCtrl'
}).
when('/home',{
templateUrl: './components/Home.html',
controller:homeCtrl'
}).
Now I want to share some data or some common functions between these controllers/pages. How can we do this? I googled it they say to use SERVICE. But I don't know in which controller I need to write the service. Can anybody give a good example for this.
A service in AngularJS is not written within a controller. It is bound to your app directly and can be used anywhere within your application. This is why Services are the recommended means of communication between controllers in AngularJS.
What you need to do is write a service like so:
angular.module('yourApp').service('serviceName', function () {....});
Within the service, you can:
Fetch data from an API end point (You can use the $http provider for this)
Define constant data (You can use Angular's constant provider for this)
Define some code that takes in some data and manipulates it and returns new data
Pretty much anything else you want to do with your data
Now, include the service in your controller as a dependency like so:
angular.module('yourApp').controller('yourController', function (serviceName) {
console.log(serviceName.getData());
// Do something with your data
});
Now within this controller, you have access to the data that the service has returned. Of course, the same service can be injected into multiple controllers, thereby making it possible to share data across controllers.
There are many ways you can share data.
event
services
$rootScope
Services provide an easy way for us to share data and functionality throughout our app. The services we create are singletons that can be injected into controllers and other services, making them the ideal place for writing reusable code.
var app = angular.module('app', []);
app.controller('leftCtrl', function ($scope,userService) {
left.btnClicked = function (object) {
userService.saveData(object);
}
});
app.controller('rightCtrl', function ($scope, userService) {
$scope.getData = userService.getData();
});
app.service('userService', function () {
var data = {};
this.saveData = function(object){
data = object;
}
this.getData = function(){
return data;
}
});
Dustin has the right approach. However there are times when you could use a different approach and that is to wrap the application in an AppController.
Everything that is in AppController can now be accessed. You could use this approach to put functions or constants that you want the child controllers of the application to have access to and don't have to inject services everywhere.
<body ng-controller="AppController">
<div ng-view></div>
</body>
I have a page.html with a directive in it. I also have a service which I want to use as a manager of events between the directive and the page.html controller.
I'm new to Angular so I don't know if I'm approaching this the right way. I want the service to update the directive if say my controller has a button clicked. Do I just put the directive as a dependency for my service?
No. You have to inject your service in your directive.
The service should handle communication with your server or strictly business logic, best you must separate into two services if you have communication and business logic.
Directive is only for the visualization part.
Events should be placed into directive "link" function and visualization functionality into "controller" but remember. Angular is not jQuery so many events like "click" are obsolete in angular by that i mean they handled automatically by a property in the controller.
This sounds like a fine approach and is often necessary, since services are singletons and controllers and directives are not. You will inject your service into both your directive AND the page controller. This way both can act on the service and the service can act as a bridge between the two.
app.service("myService", function() {
//define your service
});
app.directive('myDirective', function (myMervice) {
return {
template: '...',
restrict: '...',
scope:true,
controller: function($scope) {
//you can use the service here
}
};
});
app.controller("myController", function($scope, myMervice) {
//you can use the service here
});
I am migrating an AngularJS multiple-page app to a single-page app, and I am having some trouble to replicate the following behaviour:
Each HTML file has a different base controller and a ng-view. For example, file1.html looks like this:
<body ng-controller="BaseCtrl1">
<!-- Routes to /view11, /view12, etc. with their corresponding controllers -->
<div ng-view></div>
</body>
<script src="file1.js"></script>
file2.html uses BaseCtrl2, routing to /views21, /view22 and so on. Each of this controllers initialize the scope, and the corresponding subset of views share this part of the model:
file1.js:
module.controller('BaseCtrl1', function($scope, ServiceA, ServiceB, ServiceC) {
// Populate $scope with ServiceN.get() calls
ServiceA.get(function(response) {
$scope.foo = response.results;
});
});
// ...
file2.js:
module.controller('BaseCtrl2', function($scope, ServiceX, ServiceY) {
// Populate $scope with ServiceN.get() calls
});
// ...
However, with a single-page app I cannot use a fixed parent controller (declared in the body element) for each different group of views. I have tried using the $controller service like in the answer of this question, but I need to inject all the dependencies of the parent in the child controller, and does not look like a neat solution at all:
module.controller('View11Ctrl', function($scope, ServiceA, ServiceB, ServiceC) {
$controller('BaseCtrl1', {/* Pass al the dependencies */});
});
module.controller('View12Ctrl', function($scope, ServiceA, ServiceB, ServiceC) {
$controller('BaseCtrl1', {/* Pass al the dependencies */});
});
I would like to know if there is a way to replicate the original behaviour by initializing a "common" part of the scope of a group of views, and maintain it when changing the route.
You can use $injector.invoke() service method to achieve this.
module.controller('View11Ctrl', function($scope, ServiceA, ServiceB, ServiceC) {
$injector.invoke(BaseCtrl1, this, { $scope: $scope });
}
The third argument is defined as:
If preset then any argument names are read from this object first,
before the $injector is consulted.
This way you only need to pass the locals to your base controller that is specific to your child controller, and all other base controller dependencies will be resolved using the normal $injector DI.
I am making first steps in Angular.JS and faced the problem when variable available in scope is no longer available in router view. Setup is as follows:
var vApp = angular.module('appG', ['ngRoute', 'appG.directives'])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/', {templateUrl: '/partials/form.html', controller: 'ctrlMain'})
.when('/welcome', {templateUrl: '/partials/welcome.html', controller: 'ctrlMain'})
.otherwise({redirectTo: '/'});
});
validationApp.controller('ctrlMain', function($scope, $http, $location) {
$scope.user = {};
$scope.submitForm = function () {
$http.post('/signup', $scope.user).
success(function(data) {
$location.url('/welcome');
});
};
$scope.submitData = function () {
if ($scope.signupForm.$valid) {
$scope.submitForm();
} else {
$scope.signupForm.submitted = true;
}
};
});
HTML code:
<html ng-app="appG">
<div ng-controller="ctrlMain">
<ng-view></ng-view>
</div>
</html>
form.htm (just part of it):
<form name="signupForm" id="signupForm" ng-submit="submitForm()" novalidate>
<input type="text" class="text" ng-class="{'submit-error' : signupForm.submitted}" name="uFirst" placeholder="First name" ng-model="user.first" required/>
<i class="fa fa-sign-in pull-left"></i>Submit
</form>
welcome.html:
<p>{{user.first}}, thank you! </p>
Partial form.html contains form code with basic validation and works ok, partial welcome.html contain simple with texts containing {{user.first}} (user has binding from form and name is a property of the user object. The problem is that {{user.first}} is always empty in welcome.html partial. I would appreciate any help to figure out how it is possible to pass value of the $scope.user to the second partial. Thank you!
New scopes are created whenever you define a route/controller. Your two controllers have completely seperate $scope objects. In this case they're called "sibling" scopes. A variable defined in ctrlMain won't be available in welcome even though you use the same controller reference. The router is actually creating a fresh instance of ctrlMain.
The quick easy fix is to inject $rootScope and set $rootScope.user instead of $scope.user. $rootScope is a special scope that is the parent of all scopes. The values will be "inherited" by all child scopes in your entire app.
Obviously that's not that efficient and usually frowned upon, so the proper way is usually to use a service. You'll have to read up on creating a service, but you'd then inject the service and set your user object there. In fact, your HTTP stuff would go into the service as well.
An option that I often use, which follows the same pattern as most server side MVC frameworks (like Rails) is to create a controller called 'ApplicatonController' , and set that on your body tag, with ng-controller="ApplicationController"
Your MainCtrl will load inside the body tag, so it will inherit anything found in the parent controller.
It's called controller nesting.
You create a controller called "ApplicationController" as you have your MainCtrl, except now you can access properties and methods from the application controller from within the MainCtrl without the need to inject anything additional like $rootScope.
The hierarchy now goes
$rootScope -> ApplicationController -> MainCtrl
I find this approach is easier to wrap your head around if you're familiar with frameworks like Rails that follow the same structure.
I also like to avoid over confusing things by creating a service for everything, because you end up with huge dependency injection strings which become a hassle to manage.
Controller nesting is probably not going to be the most "Angular" way of doing it, but it solves your problem with minimal effort.
I'm finding a way to call a service
injecting in the parent from within a children.
I ended up with
<div ng-controller="ParentController">
<div ng-controller="ChildController">{{my}}</div>
</div>
<script>
var app = angular.module('myApp', []);
app.factory('Data',function(){
return {show:function(msg){return msg;}};
});
app.controller('ParentController',function($scope,Data){
$scope.shareService = Data;
});
app.controller('ChildController',function($scope){
$scope.my = $scope.$parent.shareService.show('Hey');
});
</script>
I'm wondering if could be a good practice (may be it's not very handy) or not
or if there is a better way.
If the service is independent of the parent controller, you can inject the service directly in the child controller like you did in parent controller. Why create a dependency on the parent scope.
Services by definition are meant to be shared across controllers, directive and other services.