Calling functions between directives - angularjs

I am currently building my very first APP using angular and I find myself a bit lost, will appreciate any help regarding the following topic.
I have a single page app with two panels. The left side panel displays a list of items (let's say news), and the right side panel displays the details of any of the available news. I want to be able to click on any of the items listed on the left panel and read the details on the right panel.
A sample HTML/JS code for this model will be as followed (This code will not work as it is):
HTML:
<div id="leftPanel">
<news-list></news-list>
</div>
<div id="rightPanel">
<news-detail></news-detail>
</div>
Javascript/Angular:
var app = angular.module('app', []);
app.directive('newsList', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-list.html',
controller: function ($show) {
$scope.clickAction(item){
data = ....some JSON http request...
showDetail(data);
}
},
controllerAs: 'listing'
};
});
app.directive('newsDetail', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-detail.html',
controller: function ($scope) {
$scope.showDetail(data){
....
}
},
controllerAs: 'detail'
};
});
As you can see, the showDetail() function from newsDetail has to be called from the newsList directive controller above. The click action event is called from the different elements within the news list template.
The question is: ¿How can I call a function within a controller from another controller when inside a directive?
Thanks

It would probably be a better idea for you to declare a controller in a parent element of both newsList and newsDetail . Here's my suggestion:
var app = angular.module('app', []);
app.directive('newsList', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-list.html',
controller: function ($show) {
$scope.clickAction(item){
data = ....some JSON http request...
$scope.showDetail(data);
}
},
controllerAs: 'listing'
};
});
app.directive('newsDetail', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-detail.html',
controllerAs: 'detail'
};
});
app.controller('newsCtrl', function($scope){
$scope.showDetail = function(data){
....
};
});
And nest your directives in the parent element on which you use the ngController directive:
<div ng-controller="newsCtrl">
<div id="leftPanel">
<news-list></news-list>
</div>
<div id="rightPanel">
<news-detail></news-detail>
</div>
</div>

How about trying to add the directive you wanna associate with inside your directive as follows:
var app = angular.module('app', []);
app.directive('newsList', function () {
return{
required: 'newsDetail',
restrict: 'E',
templateUrl: 'templates/news-list.html',
controller: function ($show) {
$scope.clickAction(item){
data = ....some JSON http request...
detail.showDetail(data);
}
},
controllerAs: 'listing'
};
});
app.directive('newsDetail', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-detail.html',
controller: function ($scope) {
$scope.showDetail(data){
....
}
},
controllerAs: 'detail'
};
});

Related

Angualrjs- redirect from one controller to another

I have two controllers. I would like to use the other controller based on a dependancy value. I am not able to set this at the $route level. How can I essentially do this on in the main controller: Is this even possible?
angular.module('app').controller('Controller1', ['importantService', function(importantService) {
if (importantService.getValue) {
// Use this other controller Controller2 instead
}
});
You could put your 2 controllers in directives and have an ng-if in the template switch between the two, something like:
app.controller('MainCtrl', function($scope, importantService) {
$scope.importantValue = importantService.getValue;
});
app.directive('myFirstComponent', function() {
return {
restrict: 'E',
templateUrl: '/same/url/for/both',
controller: function() {/*...your controller 1*/}
};
});
app.directive('mySecondComponent', function() {
return {
restrict: 'E',
templateUrl: '/same/url/for/both',
controller: function() {/*...your controller 2*/}
};
});
and in your template
<div ng-if="importantValue === 1"><my-first-component></my-first-component></div>
<div ng-if="importantValue === 0"><my-second-component></my-second-component></div>
probably not the most orthodox solution but it will do what you're asking.

directive unable to retrieve data from a service

I have a little SPA using angular. The concept is simple, after login, $routeProvider redirects to a home page where I have a homeController specified.
this is from my home view that is rendered by ng-view while navigating to "/home" :
<my-directive datas=getData()></my-directive>
<ul>
<li ng-repeat="data in datas"> {{data.title}} {{data.content}} </li>
</ul>
my directive is written as:
angular.module('app').directive('myDirective', ['myService', function (myService) {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
controller: function ($scope) {
$scope.getDatas = function()
{
myService.retData();
}
}
};
}]);
the home controller is:
angular.module('app').controller('homeController', homeController);
homeController.$inject = ['myService', '$scope'];
function homeController(myService, $scope) {
var vm = this;
vm.data = [];
initController();
function initController() {
vm.data = myService.retData();
}
}
and finally my service is
angular.module('app').service('myService', myService);
function myService () {
var data = [
{ id: 1, title: 'Albert Einstein', content: 'Random Content' }
];
return {
retData: function () {
return data;
},
addData: function (title, content) {
var currentIndex = data.length + 1;
data.push({
id: currentIndex,
title: title,
content: content
});
}
};
}
now that i mentioned everything, here comes the problem. the directive is not able to retrieve data from the service. Actually when i run the project in VS2013, myDirective.js is not even loaded. I included all services, directives, controllers etc in the main HTML page.
What is causing this problem?
Does it have something to do with the scope being isolated in the directive?
What is a better approach to sharing data between a controller, directive and service?
I may have made some silly mistakes while rewriting all the code. Please do point them out, however keep in mind my actual issue and what error may be causing that.
Better to use isolated scope to pass data controller to directive.
Html:
<my-directive datas="getData()" data="data"></my-directive>
Directive:
angular.module('app').directive('myDirective', [function () {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
link: function (scope) {
//Here you got the isolated scope data
var details = scope.data;
}
};
}]);
OR
app.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'partials/my-directive.html',
scope: {
date: '=',
},
controller : ['$scope', 'myService', function($scope, myService) {
myService.retData();
}],
link: function(scope, elem, attr) {
//
}
};
});

controllerAs in directive, functions in controller can't see some variables

I have bellow definition of directive/controller. If you look, there's an onClick function defined. When function is being called, it can see this variable, with ftConditionButton bound to it as described by directive. The thing is, onClick doesn't see conditionButtonController which is against my understanding of JavaScript. Can someone explain to me what I am missing? Right now it looks to me like a new "Class" was created and was given all the methods of original controller.
angular
.module('app')
.directive('ftConditionButton', ftConditionButton);
function ftConditionButton() {
var directive = {
restrict: 'A',
scope: {},
require: ['ftConditionButton'],
templateUrl: 'conditionButton.html',
controller: ConditionButtonController,
controllerAs: 'conditionButtonController',
bindToController: {
ftConditionButton: '&'
}
};
return directive;
}
function ConditionButtonController() {
var conditionButtonController = this;
conditionButtonController.onClick = onClick;
////////////////
function onClick() {
this.ftConditionButton; // this works
conditionButtonController; // conditionButtonController is undefined
}
}
Not sure what you're doing wrong. Seems okay to me.
Few things I can recommend:
1) Make sure you're using angularjs-1.4.
2) Always wrap everything into local function, such that you don't expose anything globally.
(function() {
angular.module('experiment', [])
.controller('MyController', function($scope){
$scope.test = function() {
alert("Test!");
};
});
angular
.module('experiment')
.directive('ftConditionButton', ftConditionButton);
function ftConditionButton() {
var directive = {
template: '<button ng-click="conditionButtonController.onClick()">Hello </button>',
restrict: 'A',
scope: {},
bindToController: {
ftConditionButton: '&'
},
controller: ConditionButtonController,
controllerAs: 'conditionButtonController',
};
return directive;
}
function ConditionButtonController($scope) {
var conditionButtonController = this;
conditionButtonController.onClick = onClick;
function onClick() {
conditionButtonController.ftConditionButton();
}
}
})();
and view:
<div ng-app="experiment">
<div ng-controller="MyController">
<div ft-condition-button="test()" />
</div>
</div>
PS, use JSFiddle next time, to demonstrate your problem.

How to deal with more than one scope in an Angular directive?

Using directives I ended stuck when I needed to have more than one scope.
I'm building a data visualization app with Mongoose Node, Express and D3JS.
Here's the directive
angular.module('prodsChart', [])
.controller('businessCtrl', ['$scope','$http', 'BusinessSrv', 'Products', function($scope, $http, $location, BusinessSrv, Products) {
Products.get()
.success(function(data) {
$scope.products = data;
});
BusinessSrv.getTotal()
.success(function(data) {
$scope.businessSales = data;
});
}])
.directive( 'saleProd', [
function () {
return {
restrict: 'E',
link: function (scope, element) {
// Building the chart here
}
And the HTML :
<sale-prod></sale-prod>
Is it good to inject that way the Services in the Directive ?
Now I have 2 set of data in two $scope.
How do I use them in the directive ?
You can inject the $rootScope into your directive:
angular.module('prodsChart', [])
.directive( 'saleProd', ['$rootScope', function ($rootScope) {
// your code here
}]);
and then use it everywhere within the directive.
In 1st and 2nd example directive is in controller's scope and controller's datasets transferred to the directive as attributes. In 1st example directive can modify controller's data. In 2nd example directive use controllers data as strings and creates 2 objects 'productsObj' and 'salesObj' and can't modify parent's scope. It depends on how you handle attributes in the directive and how to transfer them into it. Just click on items in 'Product list (directive)' section and you'll see result.
1st: http://plnkr.co/edit/v46oEGHvUnxMNYsKAeaW?p=preview
var directive = function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'directive-template.html',
scope: {
products: '=',
sales: '='
}
};
};
html:
<div ng-controller="BusinessController as BusinessCtrl">
<sale-prod products="BusinessCtrl.products" sales="BusinessCtrl.sales"></sale-prod>
</div>
2nd: http://plnkr.co/edit/7CyIsqBNLbeZjyfbkGo9?p=preview
var directive = function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'directive-template.html',
scope: {
products: '#',
sales: '#'
},
link: function(scope, element, attrs) {
attrs.$observe('products', function(newVal) {
scope.productsObj = angular.fromJson(newVal);
});
attrs.$observe('sales', function(newVal) {
scope.salesObj = angular.fromJson(newVal);
});
}
};
};
html:
<div ng-controller="BusinessController as BusinessCtrl">
<sale-prod products="{{BusinessCtrl.products}}" sales="{{BusinessCtrl.sales}}"></sale-prod>
</div>
3rd example is just a piece of code that show's how to inject service in directive and controller. I add it because in you example i didn't see service injection in directive:
(function(undefined) {
var app = angular.module('prodsChart', []);
var controller = function($scope, Business, Products) {
// controller logic
};
var directive = function(Business) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// here you can use all Business service logic
}
};
};
var serviceBusiness = function() {
// business service logic
};
var serviceProducts = function() {
// products service logic
};
app.controller('BusinessController', ['$scope', 'Business', 'Products', controller])
.directive('saleProd', ['Business', directive])
.service('Business', serviceBusiness)
.service('Products', serviceProducts);
})();
html:
<div ng-controller="BusinessController as BusinessCtrl"></div>
<sale-prod></sale-prod>
You could inject a Service into a directive and use it to bring data across the application or use $emit.
A less elegant solution would be using a .value() and deal with it everywhere in your application.

Angular ng-click not executing function inside a custom directive

I have created a custom directive and added a controller to it and a function on the state hamburgerClick.Here is my code:
directivesModule.directive('headerDir', [function () {
var headerDir = {
restrict: 'E',
templateUrl: 'App/scripts/main/directives/header/HeaderDir.html',
replace: true
};
headerDir.controller = ['$state', function ($state) {
$state.hamburgerClick = function() {
var app = $('.application-wrap');
if (app.hasClass('menu-opened')) {
app.removeClass('menu-opened');
}
else {
app.addClass('menu-opened');
}
};
}];
return headerDir;
}]);
<div>
<span class="menu-overlay"></span>
<section class="menu-bar">
<article>
<div class="menu-button" ng-click="hamburgerClick()">
<span class="hamburger-icon"></span>
</div>
<h1 class="logo"></h1>
</article>
</section>
My problem is that for some reason the function does not get executed when I am trying to click on it.ANyone know what I am doing wrong?
Try this!
directivesModule.directive('headerDir', [function () {
return{
restrict: 'E',
templateUrl: 'App/scripts/main/directives/header/HeaderDir.html',
replace: true
controller: function($scope){
$scope.hamburgerClick = function() {
var app = $('.application-wrap');
$('.application-wrap').toggleClass('menu-opened');
};
}
}
}]);
There are several things doubtful in your code
1) You should replace $state with $scope
2) You do not use your directive inside your HTML code. Instead, you refer to a directive named 'article'
3) You use replace:true, which replaces the original content of the directive. Unless you planned on defining your $('.menu-button') as header-dir directive, the call to hamburgerClick will be removed.
Furthermore, you could replace
var app = $('.application-wrap');
if (app.hasClass('menu-opened')) {
app.removeClass('menu-opened');
}
else {
app.addClass('menu-opened');
}
with
$('.application-wrap').toggleClass('menu-opened');

Resources