How To Call A Controller Method From Isolate Scope Directive? - javascript

What I'm doing wrong?
After of the click on "Click me" text in status block should appear a new string. (actually 4 new lines, but there are only 2)
(JSFiddle)
<div ng-app="myApp">
<div ng-controller="myController">
<button my-directive status="status" clickHandler="handler(message)">Click me</button>
<pre>{{ status }}</pre>
</div>
</div>
Script:
var app = angular.module('myApp', []);
app.controller('myController', ['$scope', '$timeout',
function myController ($scope, $timeout) {
$scope.status = 'Start controller.\n'; // update status
$scope.someData = 'Hello from controller !!!\n';
$scope.handler = function handler (message) {
$scope.status += $scope.someData; // must update status, but doesn't do it
$scope.status += 'Message from directive: ' + message; // must too
}
}]);
app.directive('myDirective', function myDirective () {
return {
scope: { clickHandler: '&', status: '=' },
link: function postLink (scope, element, attr) {
scope.status += 'Start link in myDirective.\n'; // update status
element.on('click', function onClick (event) {
scope.status += 'Click is fired.\n'; // update status
scope.$apply(function scopeApply () {
scope.status += 'In apply context.\n'; // update status
scope.clickHandler('"I\'m from directive."\n');
});
});
}
}
});

HTML
The attribute name should be dash-separated:
<button my-directive status="status" click-handler="handler(message)">
Click me
</button>
JS
The method should be called by passing in an object:
scope.clickHandler({message:'"I\'m from directive."\n'});
Updated Fiddle

Related

AngularJS ng-click not firing after compile attempt

I have a template for my directive, which contains a scope variable called content:
<div class="directive-view">
<span class="directive-header">My Directive</span>
<span ng-bind-html="content"></span>
</div>
I have the following directive, which watches for changes to content and then compiles the element's contents:
(function () {
"use strict";
angular
.module('myApp.myDirective', [])
.directive("myDirective", myDirective);
function myDirective($compile, $sce) {
return {
restrict: 'E',
scope: {
},
templateUrl:'../partials/directives/my-directive.html',
controller: function($scope) {
$scope.testAlert = function(msg) { alert(msg) };
},
link: function (scope, element, attrs, ctrl) {
scope.content = $sce.trustAsHtml('Initial value');
scope.$watch(attrs.content, function(value) {
element.html(value);
console.log("Content changed -- recompiling");
$compile(element.contents())(scope);
});
scope.content = $sce.trustAsHtml('<button ng-click="testAlert(\'clicked!\')">Click</button>');
}
};
}
})();
The directive renders the button element. However, the controller function testAlert() does not get called when the button element is clicked on.
Also, the $watch callback is called only once, after content is set to Initial value. The callback is not triggered after content is set to the button. (I would have thought that the callback is triggered when the value of attrs.content is changed.)
If I manually recompile the element:
$compile(element.contents())(scope);
the button element still does not trigger the testAlert function when clicked.
How do I correctly recompile the element contents, so that the correct bindings are made?
You do not need use $sce in this case. Use just $compile.
Live example on jsfiddle.
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
$scope.name = 'Superhero';
$scope.changeTEmplate = function(){
$scope.cont = '<div><b>i\'m changed</b></div>';
}
})
.directive("myDirective", function myDirective($compile) {
return {
restrict: 'E',
scope: {content:"="},
template: `<div class="directive-view">
<span class="directive-header">My Directive</span>
<span></span>
</div>`,
controller: function($scope) {
$scope.testAlert = function(msg) {
alert(msg)
};
},
link: function(scope, element, attrs, ctrl) {
scope.$watch('content', function(value) {
var span = angular.element(element.find('span')[1]);
span.html(value);
console.log("Content changed -- recompiling",value);
$compile(span)(scope);
});
scope.content = '<button ng-click="testAlert(\'clicked!\')">Click</button>';
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
Hello, {{name}}!
<button ng-click="changeTEmplate()"> change Content</button>
<my-directive content="cont"></my-directive>
</div>
</div>

How pass variables to directive from controller?

HTML:
<div ng-repeat="item in productArr">
{{ item.title }}
</div>
<div category-page-navigation current-page='currentPage' category-products-count='productsCount'></div>
JS:
.controller('categoryController', ['$scope', '$location', '$http', '$q', '$window', '$stateParams', function($scope, $location, $http, $q, $window, $stateParams) {
$scope.currentPage = 1;
$scope.productsCount = 0;
var GET = {
getProductData: function() {
var defer = $q.defer();
$http.post('services/loadProduct.php', {
'id' :1,
}).then(function(response) {
defer.resolve(response);
}, function(response) {
defer.resolve([]);
});
return defer.promise;
}
};
var getData = {
getProduct: function() {
var productData = GET.getProductData();
$q.all([productData]).then(
function(response) {
$scope.productArr = response[0].data.products;
$scope.productsCount = response[0].data.products.length;
});
}
};
getData.getProduct();
}])
.directive('categoryPageNavigation', function($compile, $parse) {
return {
scope: {
currentPage: '=currentPage',
categoryProductsCount: '=categoryProductsCount'
},
link: function (scope, element, attrs) {
debugger;
// Here scope.categoryProductsCount = undefined
// ...
$scope.$watch(scope.currentPage, function(value) {
// ...
});
}
};
});
I try to form new HTML for navigation to manipulate with HTML I get from ng-repeat.
In directive I need currentPage(from start =1) and total count of items from ng-repeat(length of array) witch I get from service. How I can pass variables to directive? First I need to get variables from service(ajax request or something else) then pass variables(some ather data) to directive.
If I understood correctly what you mean. Here is a code pen example on how to shared data between you controller and your directive.
A good read to understand the code below:https://docs.angularjs.org/guide/providers
http://codepen.io/chocobowings/full/Xmzxmo/
var app = angular.module('app', []);
//-------------------------------------------------------//
app.factory('Shared', function() {
return {
sharedValue: {
value: '',
}
};
});
//-------------------------------------------------------//
app.controller('ctrl', function($scope, Shared) {
$scope.model = Shared.sharedValue;
});
//-------------------------------------------------------//
app.directive('directive', ['Shared',
function(Shared) {
return {
restrict: 'E',
link: function(scope) {
scope.model = Shared.sharedValue;
},
template: '<div><input type="text" ng-model="model.value"/></div>'
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
Ctrl:
<div ng-controller="ctrl">
<input type="text" ng-model="model.value" />
<br/>
</div>
Directive:
<directive value="model.value"></directive>
</div>

how to send an object to angular directive on a check-box

i am trying to consume iCheck check box using a directive. my directive is setup like this.
.module('app').directive('bootstrapCheck', ['$timeout', '$parse', function ($timeout, $parse) {
return {
compile: function (element, $attrs) {
var icheckOptions = {
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal'
};
var modelAccessor = $parse($attrs['ngModel']);
return function ($scope, element, $attrs, controller) {
var modelChanged = function (event) {
$scope.$apply(function () {
modelAccessor.assign($scope, event.target.checked);
});
};
$scope.$watch(modelAccessor, function (val) {
var action = val ? 'check' : 'uncheck';
element.iCheck(icheckOptions, action).on('ifChanged', modelChanged);
});
};
}
};
}]);
my check-boxes are in ng-repeat. i want to send the current object to function to process it further. my html is setup like this.
<input type="checkbox" ng-model="item.isSelected" ng-change="onChangeEvent(item)" bootstrap-check>
modelChanged gets triggered each time i change any check-box. but i am trying to access item inside modelChanged function to process it further. please guide.
You can pass the item to directive scope and access it inside the link function. Along with item you can also pass the onChangeEvent handler to the directive. Try this.
JS
angular.module('app').directive('bootstrapCheck', ['$timeout', '$parse', function ($timeout, $parse) {
return {
scope: {
item: '=',
onChangeEvent: '&'
},
compile: function (element, $attrs) {
var icheckOptions = {
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal'
};
var modelAccessor = $parse($attrs['ngModel']);
return function ($scope, element, $attrs, controller) {
var modelChanged = function (event) {
//Here $scope.item will give you the item
//This will trigger the parent controller's onChangeEvent set in the directive markup
$scope.onChangeEvent({ item: $scope.item });
$scope.$apply(function () {
modelAccessor.assign($scope, event.target.checked);
});
};
$scope.$watch(modelAccessor, function (val) {
var action = val ? 'check' : 'uncheck';
element.iCheck(icheckOptions, action).on('ifChanged', modelChanged);
});
};
}
};
}]);
HTML
<input type="checkbox"
ng-model="item.isSelected"
item="item"
on-change-event="onChangeEvent(item)" bootstrap-check>

Bind a service variable to a directive?

I have a controller which contains a function that gets some data from the server. I store that data in a service variable. This service is then injected into a directive. I want the directive to be auto updated, whenever this function is called and the data is renewed.
My controller:
angular
.module('myApp')
.controller('myCtrl', ['$scope', 'SomeService', function($scope, SomeService) {
$scope.update = function() {
SomeService.myValue = 100;
}
}]);
The directive:
angular.module('myApp')
.directive('myDirective', ['SomeService', function(SomeService) {
return {
templateUrl : 'views/myDirective.html',
restrict : 'E',
scope : false,
controller : function($scope) {
this.myValue = SomeService.myValue;
}
};
}]);
The template:
<div>
{{ myValue }}
</div>
The update function is called when a button is clicked and it updates myValue to a new value. I want it to be automatically reflected in the directive.
Plunk: http://plnkr.co/edit/OUPzT4MFS32OenRIO9q4?p=preview
Please see working demo below
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, SomeService) {
$scope.name = SomeService.data;
$scope.update = function() {
$scope.name.myValue += 1;
}
});
app.factory('SomeService', function() {
var data = {
myValue: 0
};
return {
data: data
}
});
app.directive('myDirective', ['SomeService',
function(SomeService) {
return {
templateUrl: 'myDirective.html',
restrict: 'EA',
scope: false,
link: function(scope, elem, attr) {
scope.data = SomeService.data
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MainCtrl">
<p>My Value: {{name.myValue}}</p>
<button ng-click="update()">Click</button>
<hr/>
<div my-directive></div>
<script type="text/ng-template" id="myDirective.html">
<h3>My Directive</h3>
<p>Value: {{data.myValue}}</p>
</script>
</div>
</div>
You can try by adding the reference of the service to the directive itself..
The directive:
angular.module('myApp')
.directive('myDirective', ['SomeService', function(SomeService) {
return {
templateUrl : 'views/myDirective.html',
restrict : 'E',
scope : false,
controller : function($scope) {
this.SomeService = SomeService;
}
};
}]);
The template:
<div>
{{ SomeService.myValue }}
</div>
Edit : I went through your plunker, and have finally got it working.
You can check the updated code here
#RutwickGangurde and others who were having issues, if you're trying to set a scope variable that is not an object it won't work. I'm guessing that's what you're currently doing in your service:
...
this.myVar = true;
...
and then trying to set it in the directive/controller:
...
scope.myVar = myService.myVar;
...
That will NOT work for getting the updated variable in the service when it changes.
Try this instead in your service:
...
this.myObj = {};
this.myObj.myVar = true;
...
and in your directive/controller:
...
scope.myValue = myService.myObj;
...
and in your html:
...
{{ myValue.myVar }}
...
I would have made this as a comment, but I don't have sufficient privileges yet so decided to post as a response with a very brief example.

scope change doesn't execute code in directive

Here is what my directive looks like
//noinspection JSCheckFunctionSignatures
angular.module('notificationDirective', []).directive('notification', function ($timeout) {
//noinspection JSUnusedLocalSymbols
return {
restrict: 'E',
replace: true,
scope: {
ngModel: '#'
},
template: '<div class="alert fade" bs-alert="ngModel"></div>',
link: function (scope, element, attrs) {
scope.$watch('message', function () {
console.log('message now: ' + JSON.stringify(scope.message));
// element.show();
// //noinspection JSUnresolvedFunction
// $timeout(function () {
// //element.empty();
// element.fadeOut("slow");
// }, 2000);
});
}
}
});
Here is my controller
function NotificationController($scope) {
$scope.message = {};
console.log('notification activated');
$scope.invite = function() {
console.log("submitting invite for " + $scope.email);
$scope.message.title = 'hello';
// console.log('message now is ' + JSON.stringify($scope.message, null, 2));
}
}
Here is how I use it
<form class="pure-form" data-ng-controller="NotificationController">
<input type="text" class="pure-input-1-2" placeholder="Email"
data-ng-model="email">
<button type="submit"
class="pure-button notice pure-button-xlarge"
data-ng-click="invite()">Invite me
</button>
</form>
<notification data-ng-model="message"></notification>
What I want
- Whenever value if scope.message changes in NotificationController, the directive has the new value so that I can alert that on web page
What I do not understand
It seems I am not understanding how $scope.$watch is working
Please help
You made several mistakes :
Your notification tag must be inside controller (in html) since it must have access to 'message' variable.
Your binding in your directive is wrong : you must use '=' instead of '#' (as Thalis said).
Variable 'message' does not exist in your directive, you must use scope.ngModel (which is binded to your message variable).
Callback given in your watcher will be executed each time your variable will be updated. Since you use an object, watcher will be executed when your variable reference change. You must set 'true' to your third parameter of your watcher to check for object equality).
Here is your sample :
HTML :
<body>
<div id="my-app" data-ng-app="myApp">
<form class="pure-form" data-ng-controller="NotificationController">
<input type="text" class="pure-input-1-2" placeholder="Email" data-ng-model="email" />
<button type="submit"
class="pure-button notice pure-button-xlarge"
data-ng-click="invite()">Invite me
</button>
<notification data-ng-model="message"></notification>
</form>
</div>
</body>
Javascript :
var myApp = angular.module('myApp', []);
myApp.directive('notification', function() {
return {
restrict: 'E',
replace: true,
scope: {
ngModel: '='
},
template: '<div class="alert fade" bs-alert="ngModel"></div>',
link: function (scope, element, attrs) {
scope.$watch('ngModel', function () {
console.log('message now: ' + JSON.stringify(scope.ngModel));
// element.show();
// //noinspection JSUnresolvedFunction
// $timeout(function () {
// //element.empty();
// element.fadeOut("slow");
// }, 2000);
}, true);
}
}
});
myApp.controller('NotificationController', ['$scope', function($scope) {
$scope.message = {};
console.log('notification activated');
$scope.invite = function() {
console.log("submitting invite for " + $scope.email);
$scope.message.title = 'hello';
// console.log('message now is ' + JSON.stringify($scope.message, null, 2));
};
}]);
See this fiddle : http://jsfiddle.net/77Uca/15/
I believe that in your directive definition you need:
ngModel: '='
instead of:
ngModel: '#'
If you want to use the '#', your view should be:
<notification data-ng-model="{{message}}"></notification>
Also in your $watch you should be watching for ngModel and not message.

Categories

Resources