error while passing data to directive in Angular - javascript

I have this simple directive:
angular.module('app')
.directive('ppBubble', function () {
function PpBubble($scope, $element, $attrs) {
var vm = this;
vm.message = $attrs.message;
vm.isSent = $attrs.sent;
}
return {
templateUrl: '../views/directives/ppBubble.html',
restrict: 'E',
controller: PpBubble,
controllerAs: 'vm',
bindToController: true
};
});
And here is how I use it:
<pp-bubble ng-repeat="msg in vm.messages" sent="{{msg.sent}}" message="{{msg.message}}"></pp-bubble>
The problem is that I see this "msg.message" instead of the string this object attribute hold:
Here is the messages array:
vm.messages = [
{
sent: true,
message: "aasdasd dsfsdfd"
}, ...
]
I am probably missing something stupid :(

The problem is that you're doing vm.message = $attrs.message; and you're taking the string literally, which means: {{msg.message}}, since that's the text that's written in that attribute:
message="{{msg.message}}".
You need to work with a scope to make Angular work its binding magic, and take the data from there:
function PpBubble($scope, $element, $attrs) {
var vm = this;
vm.message = $scope.message;
vm.isSent = $scope.sent;
}
return {
templateUrl: '../views/directives/ppBubble.html',
restrict: 'E',
scope: { message: "=", sent: "=" },
controller: PpBubble,
controllerAs: 'vm',
bindToController: true
};
Here is the messages array:
vm.messages = [
{
sent: true,
message: "aasdasd dsfsdfd"
}, ...
]
EDIT: Another thing is that now that you're binding from the directive, you need to bind to the variable itself, and not to its string literal, meaning to remove the {{}} from the directive HTML:
<pp-bubble ng-repeat="msg in messages" sent="msg.sent" message="msg.message"></pp-bubble>
Fiddle

Here is what worked for me:
function PpBubble($scope, $element, $attrs) {
var vm = this;
}
return {
templateUrl: '../views/directives/ppBubble.html',
restrict: 'E',
controller: PpBubble,
controllerAs: 'vm',
bindToController: true
};
});
Also i removed the {{}} from the html:
Thanks to #Omri Aharon

Related

AngularJS Custom directives - Issue accessing controllerAs variables/objects in directive link

I've just started refactoring my code to do DOM manipulation and functions in directives instead of inside controllers as I had previously been doing, but I'm having issues accessing variables/objects defined using controllerAs 'this' syntax within the controller from which I need them to be inherited.
I've tried using bindToController as below, where I've added the different objects that are used in the directive function, but when I try to access these withink the 'link', they're all returning as undefined in the console.
Example here. 'this.test' defined in controller, tried accessing this in the directive in a console log message.
Controller:
app.controller('notificationsController', function($scope, $state, $http, $document, $mdDialog, $filter, $timeout, $mdToast) {
this.test = 'TEST';
Directive:
app.directive('clearNotifications', function($mdDialog, $mdToast, $timeout) {
return {
controller: 'notificationsController',
controllerAs: 'notifications',
scope: {},
bindToController: {
notifications: '=',
filters: '=',
test: '#'
},
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('click', function() {
console.log('notifications.test string test: ' + notifications.test);
this in controller is different with controllerAs in directive , in directive you should use ctrl or model to binding.
var app = angular.module("app", []);
app.controller("notificationsController", function($scope) {
this.test = "foo!";
})
app.directive("clearNotifications", function() {
return {
controller: 'notificationsController',
controllerAs: 'notifications',
scope: {},
bindToController: {
notifications: '=',
filters: '=',
test: '#'
},
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
element.bind('click', function() {
console.log('notifications.test string test: ' + ctrl.test);
})
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<button clear-notifications>clearNotifications</button>
</div>

angular scope.$watch not working

I have a directive where i bind the tabInfo to tabdata which is an array.
angular.module('widgets')
.directive('tabs', function() {
return {
restrict: 'E',
require: 'info',
transclude: true,
scope: {},
controllerAs: 'tabs',
bindToController: {
tabInfo: '=tabdata'
},
templateUrl: 'Template.html',
link: function(scope, element, attrs, tabs) {
scope.$watch('tabs.tabInfo', function() {
tabs.populateDataProvider();
}, true);
},
controller: ['$filter', '$state', function($filter, $state) {
}]
};
});
I need to watch on the "tabInfo" for any change and populate some data. However , my watch is not called at all. Is there something wrong here , in the way that I am refering to tabs.tabInfo?
A better solution to this would be to have an ng-if along with the directive so as to make sure that the directive is rendered only when there is data to populate . This avoids the need of having a watcher inside the directive .
So you could use ,
<tabs ng-if="$ctrl.tableData.length" tab-info="$ctrl.tableData"></tabs>
For your question above , you need to bind this to a variable to tabs
to get it work .
var tabs = this;
EDIT:
There is also a correction on your code . It should be ,
angular.module('widgets')
.directive('tabs', function() {
return {
restrict: 'E',
require: 'info',
transclude: true,
bindToController: true,
controllerAs: 'tabs',
scope: {
tabInfo: '=tabdata'
},
templateUrl: 'Template.html',
link: function(scope, element, attrs, tabs) {
scope.$watch('tabs.tabInfo', function() {
tabs.populateDataProvider();
}, true);
},
controller: ['$filter', '$state', function($filter, $state) {
}]
};
});

Customer Directive with Angular Material and controllerAs

I'm testing out Angular Material and I'm trying to use with a custom directive but it seems to not want to respect my binding of vm = this. Here's an example of what I'm trying.
HTML
<md-button ng-click="vm.test()">vm.Test</md-button>
JavaScript
(function() {
'use strict';
angular
.module('sidenav.directive', ['ngMaterial'])
.directive('sidenav', sidenav);
function sidenav() {
return {
templateUrl: 'app/components/sidenav/sidenav.directive.html',
restrict: 'EA',
controller: SidenavController,
controllerAs: 'vm',
bindToController: true
};
}
SidenavController.$inject = ['album', '$scope', 'authenticated', '$location', '$mdSidenav'];
function SidenavController(albumFactory, $scope, authenticated, $location, $mdSidenav) {
var vm = this;
vm.test = function () {
console.log('vm fired');
};
}
})();
The click event won't fire. However if I replace the vm in the controller with $scope and use just ng-click="test()" the button works. Any help on how to fix this would be appreciated.
Needed to add a scope to the directive, updated:
function sidenav() {
return {
templateUrl: 'app/components/sidenav/sidenav.directive.html',
restrict: 'EA',
controller: SidenavController,
controllerAs: 'vm',
bindToController: true,
scope: {
vm: '='
}
};
}

share data between sibling directives

Currently, I am facing one issue related to angularjs directive. I want to send outlet object from directive1 to directive2. Both directives having same controller scope. I tried with emitting event from directive1 to controller, broadcasting that event from controller to directive2 and listening to that event on directive2. but that is not working.
Directive1:
angular.module('moduleName')
.directive('directive1', function() {
return {
restrict: 'E',
templateUrl: 'directive1.html',
scope: false,
link: function(scope) {
scope.selectOutlet = function(outlet) {
scope.order.entityId = outlet.id;
scope.navigation.currentTab = 'right';
};
}
};
Here, in directive1, scope.selectOutlet() setting outletId to scope.order.entityId. I want to move/set that line to directive2 save function.
Directive2:
angular.module('moduleName')
.directive('directive2', function(config, $rootScope, $state) {
return {
restrict: 'E',
templateUrl: 'directive2.html',
scope: false,
link: function(scope) {
scope.save = function() {
// Save functionality
// scope.order.entityId = outlet.id; This is what i want to do
};
}
};
});
});
Any help.
you can use a factory or a service. Inject that factory into your directive. Now when you are trying set the data in function written into factory. `app.factory('shared',function(){
var obj ={};
obj.setData = function(){
// call this function from directive 1.
}
return obj;
})`
So if you include this factory into your directives you will get the data in 2 directives.
I will try to make some jsfiddle or plunker. If it is not clear.
Do the following in first directive
angular.module('moduleName')
.directive('directive1', function($rootScope) {
return {
restrict: 'E',
templateUrl: 'directive1.html',
scope: false,
link: function(scope) {
scope.selectOutlet = function(outlet) {
$rootScope.$broadcast('save:outlet',outlet);
//scope.order.entityId = outlet.id;
//scope.navigation.currentTab = 'right';
};
}
};
and in second
angular.module('moduleName')
.directive('directive2', function(config, $rootScope, $state) {
return {
restrict: 'E',
templateUrl: 'directive2.html',
scope: false,
link: function(scope) {
$rootScope.$on('save:outlet',function(event,data){
// do staff here
});
}
};
});

How to make two way binding between angular directives without using "dots"

Suppose, I have controller:
angular.module('tf').controller('Ctrl', function($scope){
$scope.params = {
orderBy: null
};});
And a directive "common":
angular.module('tf').directive("common", function() {
return {
restrict: 'E',
replace: true,
template: '<div><outer order-by="orderBy"><inner order-by-field="name1"></inner><inner order-by-field="name2"></inner></outer></div>',
controller: function ($scope) {
},
scope: {
orderBy: '='
},
link: function (scope, element, attrs) {
}
}});
Controller is using directive within it's template:
<div ng-app="tf">
<div ng-controller="Ctrl">
<common order-by="params.orderBy"></common>
<div style="color:red">{{params.orderBy}}</div>
</div>
This directive is using directive "outer":
angular.module('tf').directive("outer", function() {
return {
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-transclude></div>',
controller: function ($scope) {
this.order = function (by) {
$scope.orderBy = by
};
},
scope: {
orderBy: '=',
}
}});
Which is parent for the directive "inner":
angular.module('tf').directive("inner", function() {
return {
require: '^outer',
restrict: 'E',
transclude: true,
replace: true,
template: '<div ng-click="onClicked()">{{orderByField}}</div>',
controller: function ($scope) {
$scope.onClicked = function () {
$scope.outer.order($scope.orderByField);
}
},
scope: {
orderByField: '#'
},
link: function (scope, element, attrs, outer) {
scope.outer = outer;
}
}});
The directive "outer" shares "order" method with directive "inner" by it's controller. The directive "inner" is accessing it by using "require" mechanism.
For some reason, this is not working as expected (Property of the controller isn't updated each time it's changed by directive). If I place "orderBy" into object (e.g. {"order": {"by": null }} ) and use object instead of string value, everything is working as expected ( controller scope is properly updated by the directive). I know about "always use dots" best practices principle, but I don't wanna use it here, because it would make my directive's API less intuitive.
Here is jsfiddle:
http://jsfiddle.net/A8Vgk/1254/
Thanks

Categories