Angular directive function from controller - angularjs

I want to create a directive of a sidebar.
I have function in the controller that toggles the sidebar.
How can I access this function from any other controller in my application?
Thanks!

If you want to manipulate with your SideBar from whole your application, it is probably better way to save the state of SideBar in the Service. So you will have access to this service whenever you inject it, and you will able to change state of SideBar by changing this related Service value (It is possible because Services are always have only one instance, like Singleton pattern).
So, I will attach image which will show basic communication pattern for current case.
If we will change the state of active variable in the State Service, the SideBar Directive will be affected, because all three controllers are linked to the same instance of service.
Here Controllers service communication is small example of communication between controllers via service.
Hope it will help you!

I have function in the controller that toggles the sidebar
ItsĀ“not recomended to use any UI logic inside Controllers. In the World of Angular these things can be achieved by using declerative HTML.
<a ng-click="isReplyFormOpen = !isReplyFormOpen">Reply</a>
<div ng-show="isReplyFormOpen" id="replyForm"></div>
To answer your question: You can define a service which contains your function.
(function(){
angular.module('YourApp')
.factory('ToggleBar', function(){
function anyFunc(){
//Logic here...
}
return{
toggle: anyFunc
};
});
)();
Than inject the service to any Controller you whant to.
(function(){
angular.module('YourApp')
.controller('ControllerName',['ToggleBar',function(ToggleBar){
//Call the service func...
ToggleBar.toggle();
}]);
)();

Related

AngularJS - Sharing data from a parent route controller to all child directives on the page

I have a page that has multiple components and I've created each component as a directive. When the page is first loaded, that's when I grab all the data that should be available on the page. So all of the data exists on the controller for that route, which we'll just call pageCtrl. And then what I've been doing is binding any required data to each directive through the attributes, which of course ends up creating an isolate scope for each of them.
I know there are a few ways to share data, so given this situation, is there a recommended way of doing it? Or has anyone had better success doing it one particular way? While it's working perfectly fine the way I'm doing it, I've run into a few caveats. If I need just even one bit of information from the pageCtrl, I need to add another attribute to the directive. So it ends up creating more code on the directive element itself.
I was thinking about just creating a service that would store all the data, which the pageCtrl could initialize, instead of setting it on itself. Any feedback would be appreciated.
good question :)
First solution is to create in parent controller object and pass this object (via ng-model) to all directives. This object will be passed by reference (not by value) so controller and all directives will have access to the same object.
```
// in controller
$scope.shared_data = {someItems: []};
// in html
<my-directive ng-model=shared_data></my-directive>
Second solution is to create some simple service to store all of those data.
// in this solution you have to inject additional service to directive controller
(extended idea of point 2) creating service/factory that will be responsible by collecting and returning data. This service could be injected into directive and use the same methods to collect data. To avoid making multiple calls to API (REST) it could have some cache for each sensitive method.
Communication via events.... (probably the worsts solution for your example)
The first two ideas are probably the best, I do not know full specification of your product so final solution picking belongs to You:).
My advice is to try/play with all of those methods to really understand what is going on and how and when to use each of them :)
You can directly call your parent controller from child directive controller by using $parent.
App.controller('aCtrl', ['$scope', function ($scope) {
$scope.refresh=function(){
.......... //Updated Data get from DB
};
...........
}]);
App.directive('bDirective',function(){
restrict: 'EC',
replace: true,
scope: {},
controller: function($scope) {
$scope.$parent.refresh();
}
...
});
HTML:
<div ng-controller="aCtrl">
<div class="bDirective"></div> //directive
</div>

MVC violation in Ionic(angular) framework with $ionicModal

When working with the $ionicModal in Ionic Framework, I noticed a lot of people instantiate the Modal inside the controller and pass the controller scope to the Modal.
Like so,
$ionicModal.fromTemplateUrl("views/call_options_view.html", function ($ionicModal) {
$scope.menu = $ionicModal;
}, {
scope: $scope,
animation: "slide-in-up"
});
Doing this allows the modal to invoke methods in the controller scope. Is there some way we can give a separate controller to the Modal?
Right now, using the controller scope, isn't there a MVC violation? The controller owns two views. Suppose I want the same modal available on another controller, then I would have to duplicate my functionality for the modal on both the controllers. MVC is supposed improve code reuse. So essentially, I want to re-enforce MVC by giving my modal a separate controller.
One way I thought of fixing this is by putting the modal in the Root Controller. Doing so, will make it accessible from all the child controllers and the functionality for the modal will only be available in the root controller. I still don't like this fix, cause i don't want to clutter my root controller with too much logic.
Any other suggestions?
I stumbled on your question while trying to come up with a solution similar to your concern.
Because I had a problem regarding navigation in my routes, I decided to use $ionicModal to show a view of another state in a modal view. I came up with a solution I crafted there (but I did not implement it for my working context yet) that should work in my case, while I'm not really satisfied with it.
To summarize, all my states are nested under tabs; when I am in the tabs.home state, I want to directly show the tabs.settings.sub state.
However, tabs.settings.sub relies on data populated by its parent state tabs.settings. Hence my problem with giving the scope of my current state (tabs.home) to tabs.settings.sub.
My modal uses a template that will include the template of my view:
<script id="templates/modal.html" type="text/ng-template">
<ion-modal-view>
<ng-include src="templateUrl" ng-controller="controller"></ng-include>
</ion-modal-view>
</script>
I can then reuse the view from the state. Regarding the scope, I used $scope.new(true) to isolate it, and populated it with data required by my modal template:
var subState = $state.get ('tabs.settings.sub');
var subScope = $scope.$new (true); // true: isolate
subScope.title = 'Sub';
subScope.templateUrl = subState.templateUrl;
subScope.controller = function () {
if (subState.controller)
return $controller (subState.controller, {$scope:subScope});
return null;
};
The modal is instantiated using this scope (that's one problem to my opinion: mixing the scope of the modal and the scope of the controller).
The controller has to be a function that returns the appropriate controller.
$ionicModal.fromTemplateUrl ('templates/modal.html', {
scope: subScope
}).then (function (modal) {
modal.show ();
});
The major problem with my solution is to transit data up to the controller of the view to show (in this case SubCtrl). But it is more narrowed to my specific context: my modal is not aware of the chain of inheritance of controllers adn states, because this is handled by UI router.
I don't know if it is possible to access the state associated to a controller (the usual pattern seems to be to use $state.parent, but this cannot be used here, as mentioned by the UI router wiki).
The workaround I use here (this is the part I am not satisfied with) is to federate data through the states:
.state ('tabs.settings', {
data: { status: 'valid' }
}
I have access to it when creating my modal:
subScope.status = subState.data.status;
And I have access to it from the parent controller:
$scope.status = $state.current.data.status;

how do i bind a directive to an injected service instead of a parent or isolated scope?

related to this question: How do I create a dynamic nav which gets allowed menu items passed to it?
Basically, I have a nav outside of any views. The nav needs to display menu items which a user has access to.
I create the nav markup with a directive like so:
<tree family="treeFamily"></tree>
Where treeFamily is the data which will be used to build the navigation menu.
However, since my nav is outside of any views, it doesn't have a controller, so there is no scope variable called treeFamily. Which means the directive doesn't get any data to create a navigation.
I originally thought I could just inject a service with the data for the menu items, but then there is no way that I can see to tell an angular directive to use data taken from an injected service for binding.
The only other way that seems to be possible is to have a $rootScope variable called treeFamily and have the directive generated markup bind to that instead.
I still think you want to have a look at angular-ui router, as mentioned I in your previous question
https://github.com/angular-ui/ui-router
However, the way I'd do this without angular-ui-router is to create the service, then just inject the service in to the directive when you declare that, and use the data in there as per http://docs.angularjs.org/guide/directive.
For example:
angular.module('yourModule').service('yourService', function() {
// define your service
});
angular.module('yourModule').directive('yourDirective', function(yourService) {
return {
link: function postLink(scope, element, attrs) {
// you can now define your directive and access your yourService service
}
};
});
If you don't want to use a $rootScope variable here is a slightly hacky solution but you could get the scope by the element.
Example.
Say your data is applied to a test controller so you have a element like this
<div id="test" ng-controller="test">
You could do this example using jQuery (not required)
$('#test').scope().treeFamily
There it is you have access to the scope that you need to get your data from, demo in progress.
Demo: http://jsfiddle.net/hq26h/
In the demo the random directive is accessing the treeFamily data from the test controller when the directive is outside the controller.
If you wan't your service data to be bindable, you can do this
app.directive('something', function( $someNavDataService ) {
return function( $scope ) {
$scope.navData = $someNavDataService;
};
});

Partial page updates with AngularJS

I am building an AngularJS application that has a sidebar and a main content area. Both are populated by separate invocations of the $http service to retrieve some JSON data. The stuff in the sidebar is basically a table of contents and the intent is that clicking on one of the sidebar items will cause the main area to be updated.
My initial stab at this involved putting the sidebar and main area into one partial and then associating it with a controller that does both retrievals. The application has a route that associates the controller to a URL, and the links in the sidebar access these URL with the appropriate parameter that will cause the desired content to appear in the main area. All this works, but it does cause the whole page to refresh. The "partial" really is a "total".
What I'd like to do is somehow cause a click on the sidebar links to trigger a refresh of the main content area only. One thought is to somehow split it into two controller/partial pairs and figure out a way to cause sidebar clicks to request an update. I'm not sure about the mechanics of doing this, though. Another approach is to keep the stuff in one controller and use some kind of shared state that would trigger this update. I attempted to do this by setting an ng-click directive on the links, but this did not update a scope variable, as I had hoped.
Is there a recommended way of structuring an application to achieve this kind of AJAX-driven partial page update? It seems like a fairly common case, but I haven't mastered enough of AngularJS to get a solution.
This is what I'm doing:
I have 2 services, 1 for the sidemenu and the other for the main content. They are both injected into the controller.
To handle cross service calls I use $broadcast to send events.
Works really well and is very clean code.
additional info on using services based on comment
For the sidemenu i have a shared menu service, that way all controllers can use the same menu.
The maincontent service doesnt have to be shared, but i use them because services don't loose their data when the controller goes out of scope. If I didn't the controller would have to use some other mechanism to repopulate its data. For me a service handles it without having to code anything
To show the different views i use the following main layout html
<div >
<!-- left menu -->
<div id="left" ng-include src="menu.view" ></div>
<!-- main content -->
<div id="main" ng-include src="maincontent.view"></div>
</div>
the controller
function myControllerCtrl($scope, $rootScope, menuService, maincontentService) {
$scope.menu = menuService;
$scope.maincontent = mainContentService
}
the menu service
app.factory('menuService', ['$rootScope', function ($rootScope) {
var service = {
view: 'leftmenuview.html',
MenuItemClicked: function (data) {
$rootScope.$broadcast('menuitemclicked', data);
}
};
return service;
}]);
the main content service
app.factory('maincontentService', ['$rootScope', function ($rootScope) {
var service = {
view: 'maincontentview.html',
MenuItemClicked: function(data){
//handle updating of model based on data here
}
};
$rootScope.$on('menuitemclicked', function (event, data) { service.MenuItemClicked(data) });
return service;
}]);

Angularjs: two way data bindings and controller reload

If use routing and controllers, then model not save his states between controller reload. Angular create controller instance and new scope on every route load.
For example, i type something in input that have ng-model="something", go to another route, then back to first route. All my typed text is lost.
I need simple two way data binding between routes changing. As simple as possible, like ko.observable in knockoutjs. Or implicitly like in angular within one controller. Maybe with singleton $scope for controller?
I found the way, when i create service for saving data between route changing, and inject it into controller. In controller's constructor i create model with value from service, and $scope.watch this model for changes, and on change i set model's value to service.
Is there any simpler way?
You are right - services is right way for doing this. You can use it like so:
app.js
app.factory('paginationService', function() {
return {
cur: 1,
total: 9,
pageSize: 8
};
});
app.controller('Page1Ctrl', function($scope, paginationService) {
$scope.pagination = paginationService;
});
Page1.html
<div ng-controller="Page1Ctrl">
<h2>Page1</h2>
<p>curPage: <input type="number" ng-model="pagination.cur" /></p>
</div>
See full example.
You could inject $rootScope into your controller and use it to store globally accessible variables, though you would still have the same issue watching for changes. You could create a directive which would inject your service into the current scope, and have it bind watch handlers in the scope as well.

Resources