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
});
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>
According to the below image:
I want to improve components communication method....I think this way is not efficient.
When clicking tabsetComponent to emit event, then parent controller catch this event, changing rootScope variable. Using $watch rootScope variable in tableComponent to trigger http fetch data function...
Could anyone has better and efficient way to communicate sibling component?
The accepted AngularJS method for communication between components is using component attributes for communication.
<div ng-controller="rootCtrl as vm">
<tab-set-component tsc-click="vm.fn($event, data)">
</tab-set-component>
<table-component="vm.tableData">
</table-component>
</div>
For more information on defining component attributes, see AngularJS Comprehensive Directive API -- isolate scope
Best practices
Only use .$broadcast(), .$emit() and .$on() for atomic events
Events that are relevant globally across the entire app (such as a user authenticating or the app closing). If you want events specific to modules, services or widgets you should consider Services, Directive Controllers, or 3rd Party Libs
$scope.$watch() should replace the need for events
Injecting services and calling methods directly is also useful for direct communication
Directives are able to directly communicate with each other through directive-controllers
-- AngularJS Wiki Best Practices
Controller Example
In your html, you use vm.fn that came from root controller right? So your advice is it should call the click method defined root controller, the click method will trigger http request function defined on the rootScope, then get table component datas, then bind the datas on table component attribute.
As example:
angular.module("myApp", []);
angular.module("myApp").controller("rootCtrl", function($http) {
var vm = this;
vm.tableData = { /* initial data */ };
//click handler
vm.fn = function(event, url) {
$http.get(url).then (function onFulfilled(response) {
vm.tableData = response.data;
}).catch (function onRejected(response) {
console.log(response.status);
});
};
});
The above example avoids cluttering $rootScope. All the business logic and data is contained in the controller.
The controller sets the initial data for the table-component, receives click events from the tab-set-component, makes HTTP requests, handles errors, and updates the data to the table-component.
UPDATE -- Using Expression Binding
Another approach is using expression binding to communicate events:
<header-component view="root.view" on-view-change="root.view = $event.view">
</header-component>
<main-component view="root.view"></main-component>
For more information, see SO: How to pass data between sibling components in angular, not using $scope
With version 1.5.3, AngularJS added the $onChanges life-cycle hook to the $compile service.
app.component("mainComponent", {
template: "<p>{{$ctrl.count}}",
bindings: {view: '<'},
controller: function() {
this.count = 0;
this.$onChanges = function(changesObj) {
if (changesObj.view) {
this.count++;
console.log(changesObj.view.currentValue);
console.log(changesObj.view.previousValue);
console.log(changes)bj.view.isFirstChanged());
};
};
}
});
For more information, see AngularJS Comprehensive Directive API Reference -- Life-cycle hooks
See also SO: AngularJs 1.5 - Component does not support Watchers, what is the work around?
I have a bit of a weird situation which requires me to pass my controllers to my directives via a directive scope variable. This works great as long as there are only one controller in use per route, which is declared in my $routeProvider.
But now I have to have 2 controllers in use in the same template, which causes problems because I can't declare my controllers using ng-controller because that will throw a routeProvider error since I'm trying to access data from my route resolve. (You can only access route resolve data if you declare the controller in the same route as the resolve, which then makes using ng-controller in the template and controller in the directive useless to me).
So this is what I want to do:
// Declare one controller in the routing
.when('/someroute', {
controller: 'MyCtrl'
}
// But pass a different controller to my directive that hasn't been declared
// in either the route, template or directive
<my-directive ctrl="MyOtherCtrl"></my-directive>
But my question is, is it possible to access a controller and its functions without declaring the controller as ng-controller, controller in directive or in route? My far-fetched idea is that there's a service or something that you can inject which holds all of the controllers, but so far I've come up with none.
You could inject th controller by name into your directives controller using $controller.
var someOtherController = $controller('SomeOtherController ',{$scope: $scope});
It's methods would now be available on $scope. Be careful with this though, things can get hairy quickly.
There is a service to get an instance of any controller, use the $controller
in your directive, inject the $controller service then use it in your link function:
myApp.directive("myDirective", function($controller){
return {
scope: {
ctrl: "#"
},
link: function(scope, element, attrs){
var myNeededCtrl = $controller(scope.ctrl, {$scope: scope, otherDepenciesThatTheControllerNeed: ...});
myNeededCtrl.doSomething();
}
};
});
I am trying to build a chat with Pusher and AngularJs.
<div id="chats" ng-controller="ChatCtrl">
<chat chat-id="1" chat-name="Max"></chat>
<chat chat-id="2" chat-name="John"></chat>
<chat chat-id="3" chat-name="Susanne"></chat>
</div>
I have the following directive:
.directive('chat', function() {
return {
restrict: 'E',
templateUrl: '/tpl/chat-box.html'
}
}]);
and the following controller:
.controller('ChatCtrl', ['$scope', '$pusher',
function($scope, $pusher) {
$scope.pusher = $pusher(client);
}])
Where should I handle ajax requests to receive old messages and Pusher channel binding to receive new messages for a chat-box? In the controller or in the directive?
Probably in a service, which would encapsulate other business logic as well. The controller would then just the service's API to send/receive data and make this data available to the view, while the directive should generally be only concerned with DOM manipulation, and it, too, should not contain business logic.
Your question is a bit too general though, and the title does not really reflect your question (your directive doesn't even have an isolate scope).
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.