Trying to call a function inside the directive:
angular.module('abc').directive('autocomp', function () {
return {
replace: true,
scope: true,
controller: 'Controller',
controllerAs: 'ctrl',
srchtxt: '#',
scope: {
src: '&'
},
templateUrl: '/dom/abc/auto.html',
link: function (scope, element, attrs) {
scope.$watch('srchtxt', function () {
scope.src(scope.srchtxt});
});
}
}
});
==========================================================================
Method inside the controller:
function search(srchtxt) {
console.log('srchText:', srchText);
}
===========================================================================
HTML:
<abc src="rc.search()"></abc>
I am writing a directive for the md-autocomplete control to which method name can be passed as attributes from the HTML pagee, and then try to execute the method inside the directive.
There's a watch on the textchange in the control in which I call the method to be executed (scope.src(scope.srchText}))
When I type in something into the autocomplete control I get the following error:
TypeError: Cannot use 'in' operator to search for 'rc' in g
Could any one please suggest on what am I missing or doing wrong ?
When you use & to bind controller method into directive, you have to declare exact parameter in your html like
<sml-auto-complete datasrc="cr.query(searchText)"></sml-auto-complete>
// ^
Then when you call this method from directive you need to pass the parameter as object like
scope.$watch('searchText', function () {
scope.datasrc({searchText: scope.searchText}); //not simply scope.datasrc(scope.searchText)
});
Check this demo
Related
We can pass scope parameter to a directive
app.directive('appInfo', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'js/directives/appInfo.html'
};
});
and use it as follows in a view:
<app-info info="app"></app-info>
A component can be used as a directive too:
<component-info></component-info>
But can we pass to it a scope parameter same as info="app" ?
Yes, for a component you'll use bindings instead of scope. So your component definition will look somewhat like this:
app.component('componentInfo', {
bindings: {
info: '='
},
// ... and so on
});
My directive's controller is not getting updated with the scope that was set using the '=' two-way-binding.
Here is my directive:
.directive('navigation', function() {
return {
restrict: 'E',
scope: {
selection: '=?selectedItem',
goforward: '&onForward'
},
controller: function() {
var vm = this;
vm.hideForward = !vm.selection
},
controllerAs: 'vm',
bindToController: true,
template: '<button ng-hide="vm.hideForward" ng-click="vm.goforward()">Continue</button>'
};
});
Here is my html file where I use the directive:
<div class='product' ng-click='ctrl.selectedItem = true'>item</div>
<navigation on-forward="ctrl.goForward()" selected-item='ctrl.selectedItem'></navigation>
note: the ctrl.goForward() works just fine.
The vm.selectedItem in the html's controller is only set to true once the product div is clicked.
I expected the ctrl.selectedItem to get passed into my directive's controller and modify the vm.hideForward value, except this is not happening.
I want to be able to change whether the navigation directive is visible and/or active depending on variables that are passed into it from whatever controller's scope I used my directive in.
If I place a <div>{{vm.selectedItem}}</div> inside my directive's template, that does print out properly depending on how ctrl.selectedItem that value changes. My issue is getting the directive's controller to change as well.
How am I setting up this scope binding improperly? I am using angular 1.5.3
You dont need the double brackets for binding a function to ng-click, use ng-click="vm.goforward()"
Pass the function to the directive as on-forward="ctrl.goForward", if you use parenthesis you will be passing the result of the function call instead.
Also for, ng-click='ctrl.selectedItem === true' you should use ng-click='ctrl.selectedItem = true' to set the value, as === is a comparison operator.
ctrl.selectedItem seems to be a variable from the present controller. So while passing it as attribute, you need to pass it as '{{ctrl.selectedItem}}" .
Try using:
**<navigation on-forward="ctrl.goForward()" selected-item='{{ctrl.selectedItem}}'></navigation>**
Try this
.directive('navigation', function() {
return {
restrict: 'E',
scope: {
selection: '=selectedItem',
goforward: '&onForward'
},
controller: function(scope) {
var vm = this;
vm.hideForward = !scope.selection
},
controllerAs: 'vm',
bindToController: true,
template: '<button ng-hide="vm.hideForward" ng-click="vm.goforward()">Continue</button>'
};
});
in my main controller I have a function to set messages on the page:
function setMessageState(showSuccess, showError, message) {
vm.showSuccessMessage = showSuccess;
vm.showErrorMessage = showError;
vm.message = message;
}
Within my main controller and service I relay messages to the user by calling this if the service succeeds or fails in retrieving data.
I have moved one of my repeaters into a directive which handles iteration of tabular data but part of this relies on calling the setMessageState function and feeding back the parameters into the main VM.
Can anyone assist in how I can call my setMessageState function from the directive with the included params?
I did try including setMessageState: "&" but while dupressing the issue of setMessageState is not defined it didn't call the function
My Directive is:
(function () {
'use strict';
//Define the Controller
angular
.module("tpAccStatus")
.directive("tpAccStatusTransactionRow", tpAccStatusTransactionRow);
function tpAccStatusTransactionRow() {
return {
scope: {
trans: "=tpAccStatusTransactionRow",
accountNumber: "="
},
restrict: "A",
replace: true,
templateUrl: "tpAccStatusTransactionRow.directive.html",
bindToController: true,
controller: "tpAccStatusTransactionRowCtrl",
controllerAs: "transCtrl"
};
}
})();
And my Directive Controller has a function which calls (needs to call) upon that parent controller function:
function requestStatementErrored(error) {
vm.busy = false;
vm.setMessageState(false, true, "Problem obtaining details");
}
Thanks
The directive wont know anything about vm in your parent, instead, you can add a scope property to your directive that is then bound to a reference to the function in your parent
So:
scope: {
trans: "=tpAccStatusTransactionRow",
accountNumber: "=",
messageStateFn: "="
}
Then when using your directive, you can set like so:
<tr tpAccStatusTransactionRow messageStateFn="vm.setMessageState"></tr>
And in your directive controller:
function requestStatementErrored(error) {
scope.setMessageState(false, true, "Problem obtaining details");
}
You may do like this from your directive,
scope: {
trans: "=tpAccStatusTransactionRow",
accountNumber: "=",
setMessageState: '&'
},
function requestStatementErrored(error) {
vm.setMessageState({showSuccess: false, showError: true, message: "Problem obtaining details");
},
and in HTML,
<tr tpAccStatusTransactionRow set-message-state="vm.setMessageState"></tr>
I have a (simplified) directive
angular.module('myApp')
.directive('myButton', function () {
return {
restrict: 'E',
scope: {
callbackFn: '&'
},
template: '<button ng-click=ca;;backFn($evenb)'
}
});
Now, in some parent controller I have defined a callback function:
this.myCallback = function ($event) {
this.doIt($event);
}
and the HTML:
<my-button callback-fn="page.myCallback()"></my-button>
(I'm using things like bindToController and controllerAs)
The issue is that the $event is never passed to myCallback, which most likely has to do with how I bind this function (&). But on the other hand, inside myCallback I would like to use this.
Is there some way to fix this ? without doing things like
var self = this;
this.myCallback = function ($event) {
self.doIt($event);
}
You haven't completely set up your bindings correctly. You can pass back arguments from the directive to the parent controller via a key-value map. According to the angular docs (emphasis mine):
& or &attr - provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given <widget my-attr="count = count + value"> and widget definition of scope: { localFn:'&myAttr'}, then isolate scope property localFn will point to a function wrapper for the count = count + value expression. Often it's desirable to pass data from the isolated scope via an expression to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).
So that means in your consuming HTML you need to add parameters:
<my-button callback-fn="page.myCallback(parentEvent)"></my-button>
And then in the directive:
......
restrict: 'E',
scope: {
callbackFn: '&'
},
template: '<button ng-click="ctrl.callbackFn({parentEvent: $event})">Callback</button>'
,
According to me you should do it this way :
In your HTML page :
<my-button callback-fn="page.myCallback(event)"></my-button>
In your directive :
angular.module('myApp')
.directive('myButton', function () {
return {
restrict: 'E',
controller: 'Controller',
bindToController: true,
scope: {
callbackFn: '&'
},
template: '<button ng-click=foo($event)'
}
});
function Controller() {
this.foo = function (event) {
this.callbackFn({event: event});
}
}
But I'm not sur what's the point of your question.
I am trying to create a directive that uses a template with a input field that makes a callback to the parent controller when the input field loses focus so that I can update other values in the parent model. The directive is implementing jQuery autocomplete. I have tried both calling the parent callback function directly as well as calling a function on the local scope that then in turn calls the callback function.
Here is the directive code and the element the directive is applied to. What am I missing?
HTML:
<div iptm-ext-lookup extension="button.destination" siteid="vm.buttonInfo.primaryExtSiteID" buttonid="button.identifier" extensionselected="vm.extensionSelected" numberlostfocus="vm.numberLostFocus(buttonid)"></div>
JS:
angular.module("iptmApp").directive("iptmExtLookup", function () {
function Controller($scope, $element, $attrs, extensionMgmtService) {
$scope.getData = function (extension, siteid) {
return extensionMgmtService.getExtensionInfo(extension, siteid);
}
}
// I bind the $scope to the DOM behaviors.
function link(scope, element, attributes, controllers) {
// Setup jquery UI
element.autocomplete({
minLength: 3,
source: [],
select: function (event, ui) {
scope.extensionselected(scope.buttonid, ui.item.value, ui.item.extId, ui.item.extLabel);
}
});
//Watcher to update autocomplete list when the input data changes
scope.$watch('extension', function (value) {
if (value != null && value.length > 2) {
scope.getData(value, scope.siteid).success(function (data) {
element.autocomplete("option", "source", data);
}).error(function (data, status, headers, config) {
alert(status);
});
}
});
}
// Return the directive confirugation.
return ({
controller: Controller,
link: link,
restrict: "EA",
replace: true,
template: '<input type="text" ng-model="extension" ng-blur="lostFocusCallback({ buttonid: $scope.buttonid })" style="width:100px; height:14px;">',
scope: {
extension: '=',
buttonid: '=',
extensionselected: '=',
lostFocusCallback: '&numberlostfocus',
siteid: '='
}
});
});
Have a closer read of the documentation...
Often it's desirable to pass data from the isolated scope via an expression to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn
You'll need the following
<div iptm-ext-lookup ... numberlostfocus="vm.numberlostfocus(buttonid)"></div>
<!-- note the argument name ^ -->
and in your directive
template: '<input ... ng-blur="lostFocusCallback({ buttonid: buttonid })" ...'
You didn't need $scope.buttonid as
The template is already bound to the directive's scope, and
There is no $scope variable defined in the template