I have a directive:
Here I am passing a data object:
<a-link options = "data"></a-link>
And here is my directive:
.directive('aLink', function() {
return {
restrict: 'AE',
replace: true,
template: '<a href = "{{href}}">{{text}}</div>',
link: function(scope, element, attrs) {
scope.$watch('options', function(newValue, oldValue) {
if (newValue)
scope.href = newValue.href;
scope.text = newValue.text;
});
}
});
This works fine. It gives me an anchor tag like
link
My question is:
If i get the following on json:
{
body : "Click <a-link text = 'here' href = '/home/login.html'></a-link>"
}
and my html:
<p ng-bind-html = "body"></p>
This doenot work.
How do I recompile this? Do I need to pass it through another directive and compile it there?
Use $compile service:
$compile(response.body)($scope)
Related
I have a directive which creates a modal.I am trying to pass an object through attributes to this directive.
<modal-dialog model="viewSummaryDialog" info="{{info}}"></modal-dialog>
And I'm retrieving it through attributes like this
return {
restrict: 'E',
scope: {
model: '=',
info:'#',
},
link: function(scope, element, attributes) {
scope.info=scope.$eval(attributes.info);
In my HTML info is an object which has ng-model of different text fields and dropdowns
My problem is that info is not being updated with whatever I enter in the text fields.I am getting only auto selected drop down values in my info object in the directive.I understand this is because link function is being called even before I enter anything in text fields.
Is there any way to make sure that my info object is passed only after I enter all the fields in the form? I am not very clear about how to pass an object to the directive.I tried using resolve function also in my directive but that didn't work.
Thanks in advance :-)
Passing object to directive
var myApp = angular.module('myApp',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { object: '=' },
template: '<div>Hello, {{object.prop}}!</div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.object = { prop: "world" };
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<pass-object object="object"></pass-object>
</div>
From what I can tell, an interpolated value string expands/resolves correctly if it's specified in the template, but not if it's added later. To demonstrate, I have the following code:
describe.only('directive tests - ', function() {
it('add dynamic interpolated attribute value', function() {
module(function($compileProvider) {
$compileProvider.directive('hello', function() {
return {
restrict: 'A',
replace: true,
template: '<a foo="{{1+1}}"></a>',
compile: function link(tElement, tAttrs) {
tElement.attr('bar', '{{2+2}}'); // add an interpolated attr.
}
};
});
});
inject(function($compile, $rootScope) {
var element = angular.element('<div hello/>');
$compile(element)($rootScope);
$rootScope.$apply();
console.log(' * ', element.prop('outerHTML'));
});
});
});
and console.log prints:
<a foo="2" hello="" bar="{{2+2}}" class="ng-scope"></a>'
and NOT:
<a foo="2" hello="" bar="4" class="ng-scope"></a>'
as I'd think. What gives?
tElement.attr('bar', $interpolate('{{2+2}}')());
Right, it is too late to do this in compile, more specifically to make changes to the directive element itself that have to be compiled (it needs to be recompiled in order to make the changes work).
But the following
// replace: true,
template: '<a foo="{{1+1}}">aa</a>',
compile: function link(tElement, tAttrs) {
tElement.find('a').attr('bar', '{{2+2}}');
}
would work as expected.
Attribute watching and interpolation can also be done in link (or controller):
link: function (scope, element, attrs) {
attrs.$observe('bar', function (interpolatedBar) {
scope.bar = interpolatedBar;
});
}
It will set up a watcher on bar attribute (while $interpolate(...)() is one-time assignment and doesn't interpolate any values from scope).
I am trying to output child element id in a directive, but it keeps printing non-interpolated values. I don't know how to achieve that....please help.
I am trying to learn angular...
//////////////////////
//Directive example
app.directive('simpleNumber', ['$http', '$compile', function($http, $compile) {
return {
restrict: 'A', /*Example: <span simple-number></span>*/
terminal: true,
transclude: true,
replace: true, /*Replace <simple-number-ctrl> tag with below template*/
template: "<div><div id=\"{{$id}}\"></div></div> ",
scope: { /*data-binding to parent scope*/
ctrlWidth: "#", /*one way binding*/
strNumber: "=", /*two way binding*/
onWidthChanged: "&" /*Event function fired*/
},
link: function($scope, elm, attrs) {
console.log(elm.children()[0].id); //This is printing {{$id}} !!! I AM CONFUSED
}
};
}]);
<span simple-number ctrl-width="100px" str-number="numberText" on-width-changed="onWidthChanged(width);"><font color=green>Transcluded text</font></span>
When the linking function is called, the {{ }} bindings are not evaluated yet, so you still have them as raw text. You will get the correct value once the $digest cycle is finished.
Use this in your link function if you want to better understand what is going on.
link: function($scope, elm, attrs) {
console.log("bindings not evaluated yet:",element.html());
var unwatch = scope.$watch(function(){
console.log("after $digest, bindings are evaluated",element.html());
unwatch();
});
}
I have solved this by using $timeout (see code below). I don't know why it's working this way. Can someone provide an explanation?
//////////////////////
//Directive example
app.directive('simpleNumber', ['$http', '$compile', function($http, $compile) {
return {
restrict: 'A', /*Example: <span simple-number></span>*/
transclude: true,
replace: true, /*Replace <simple-number-ctrl> tag with below template*/
template: "<div><div id=\"{{$id}}\"></div></div> ",
link: function($scope, elm, attrs) {
//Print children ID's
var __debugOutputChildrenInfo = function(children)
{
for(var i = 0; i < children.length; i++)
console.log(children[i].id);
}
var children = elm.children();
__debugOutputChildrenInfo(children); //Prints {{$id}} [Shouldn't binding have been resolved by now?]
//Don't understand why this is working...need explanation?
$timeout(function() {
__debugOutputChildrenInfo(children); //Prints 002 [THIS IS WHAT I WANTED..NEED EXPLANATION]
});
}
};
}]);
<span simple-number ctrl-width="100px" str-number="numberText" on-width-changed="onWidthChanged(width);"><font color=green>Transcluded text</font></span>
I want to create a directive that links to an attribute. The attribute specifies the function that should be called on the scope. But I also want to pass an argument to the function that is determined inside the link function.
<div my-method='theMethodToBeCalled'></div>
In the link function I bind to a jQuery event, which passes an argument I need to pass to the function:
app.directive("myMethod",function($parse) {
restrict:'A',
link:function(scope,element,attrs) {
var expressionHandler = $parse(attrs.myMethod);
$(element).on('theEvent',function( e, rowid ) {
id = // some function called to determine id based on rowid
scope.$apply(function() {expressionHandler(id);});
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}
Without passing the id I can get it working, but as soon as I try to pass an argument, the function is not called anymore
Marko's solution works well.
To contrast with recommended Angular way (as shown by treeface's plunkr) is to use a callback expression which does not require defining the expressionHandler. In marko's example change:
In template
<div my-method="theMethodToBeCalled(myParam)"></div>
In directive link function
$(element).click(function( e, rowid ) {
scope.method({myParam: id});
});
This does have one disadvantage compared to marko's solution - on first load theMethodToBeCalled function will be invoked with myParam === undefined.
A working exampe can be found at #treeface Plunker
Just to add some info to the other answers - using & is a good way if you need an isolated scope.
The main downside of marko's solution is that it forces you to create an isolated scope on an element, but you can only have one of those on an element (otherwise you'll run into an angular error: Multiple directives [directive1, directive2] asking for isolated scope)
This means you :
can't use it on an element hat has an isolated scope itself
can't use two directives with this solution on the same element
Since the original question uses a directive with restrict:'A' both situations might arise quite often in bigger applications, and using an isolated scope here is not a good practice and also unnecessary. In fact rekna had a good intuition in this case, and almost had it working, the only thing he was doing wrong was calling the $parsed function wrong (see what it returns here: https://docs.angularjs.org/api/ng/service/$parse ).
TL;DR; Fixed question code
<div my-method='theMethodToBeCalled(id)'></div>
and the code
app.directive("myMethod",function($parse) {
restrict:'A',
link:function(scope,element,attrs) {
// here you can parse any attribute (so this could as well be,
// myDirectiveCallback or multiple ones if you need them )
var expressionHandler = $parse(attrs.myMethod);
$(element).on('theEvent',function( e, rowid ) {
calculatedId = // some function called to determine id based on rowid
// HERE: call the parsed function correctly (with scope AND params object)
expressionHandler(scope, {id:calculatedId});
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}
Not knowing exactly what you want to do... but still here's a possible solution.
Create a scope with a '&'-property in the local scope.
It "provides a way to execute an expression in the context of the parent scope" (see the directive documentation for details).
I also noticed that you used a shorthand linking function and shoved in object attributes in there. You can't do that. It is more clear (imho) to just return the directive-definition object. See my code below.
Here's a code sample and a fiddle.
<div ng-app="myApp">
<div ng-controller="myController">
<div my-method='theMethodToBeCalled'>Click me</div>
</div>
</div>
<script>
var app = angular.module('myApp',[]);
app.directive("myMethod",function($parse) {
var directiveDefinitionObject = {
restrict: 'A',
scope: { method:'&myMethod' },
link: function(scope,element,attrs) {
var expressionHandler = scope.method();
var id = "123";
$(element).click(function( e, rowid ) {
expressionHandler(id);
});
}
};
return directiveDefinitionObject;
});
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) {
alert(id);
};
});
</script>
You can create a directive that executes a function call with params by using the attrName: "&" to reference the expression in the outer scope.
We want to replace the ng-click directive with ng-click-x:
<button ng-click-x="add(a,b)">Add</button>
If we had this scope:
$scope.a = 2;
$scope.b = 2;
$scope.add = function (a, b) {
$scope.result = parseFloat(a) + parseFloat(b);
}
We could write our directive like so:
angular.module("ng-click-x", [])
.directive('ngClickX', [function () {
return {
scope: {
// Reference the outer scope
fn: "&ngClickX",
},
restrict: "A",
link: function(scope, elem) {
function callFn () {
scope.$apply(scope.fn());
}
elem[0].addEventListener('click', callFn);
}
};
}]);
Here is a live demo:
http://plnkr.co/edit/4QOGLD?p=info
Here's what worked for me.
Html using the directive
<tr orderitemdirective remove="vm.removeOrderItem(orderItem)" order-item="orderitem"></tr>
Html of the directive: orderitem.directive.html
<md-button type="submit" ng-click="remove({orderItem:orderItem})">
(...)
</md-button>
Directive's scope:
scope: {
orderItem: '=',
remove: "&",
My solution:
on polymer raise an event (eg. complete)
define a directive linking the event to control function
Directive
/*global define */
define(['angular', './my-module'], function(angular, directives) {
'use strict';
directives.directive('polimerBinding', ['$compile', function($compile) {
return {
restrict: 'A',
scope: {
method:'&polimerBinding'
},
link : function(scope, element, attrs) {
var el = element[0];
var expressionHandler = scope.method();
var siemEvent = attrs['polimerEvent'];
if (!siemEvent) {
siemEvent = 'complete';
}
el.addEventListener(siemEvent, function (e, options) {
expressionHandler(e.detail);
})
}
};
}]);
});
Polymer component
<dom-module id="search">
<template>
<h3>Search</h3>
<div class="input-group">
<textarea placeholder="search by expression (eg. temperature>100)"
rows="10" cols="100" value="{{text::input}}"></textarea>
<p>
<button id="button" class="btn input-group__addon">Search</button>
</p>
</div>
</template>
<script>
Polymer({
is: 'search',
properties: {
text: {
type: String,
notify: true
},
},
regularSearch: function(e) {
console.log(this.range);
this.fire('complete', {'text': this.text});
},
listeners: {
'button.click': 'regularSearch',
}
});
</script>
</dom-module>
Page
<search id="search" polimer-binding="searchData"
siem-event="complete" range="{{range}}"></siem-search>
searchData is the control function
$scope.searchData = function(searchObject) {
alert('searchData '+ searchObject.text + ' ' + searchObject.range);
}
This should work.
<div my-method='theMethodToBeCalled'></div>
app.directive("myMethod",function($parse) {
restrict:'A',
scope: {theMethodToBeCalled: "="}
link:function(scope,element,attrs) {
$(element).on('theEvent',function( e, rowid ) {
id = // some function called to determine id based on rowid
scope.theMethodToBeCalled(id);
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}
I have a input element, which use the type-ahead plugin from bootstrap. I want to set the data-source attribute as the user typing. The effect is like instance search.
<input id="test" data-provide="typeahead" data-source="{{titles}}"/>
And in my AngularJS controller, I have
$scope.titles = my_array_object
After above code is executed, I see in Chrome developer tool that the data-source attribute is set correctly.
But in Chrome console, when I execute
$('#test').data('source')
the return value is "", an empty string
What is the correct way to set the element attribute?
I think you want something like this : http://plnkr.co/edit/ASeDxB5445HeiLKOq409?p=preview
var myApp = angular.module("app",[]);
myApp.directive('moDataSource',function() {
return {
restrict:"A",
scope:{
moDataSource:"=",
},
link: function(scope, elem, attrs) {
var typeahead = elem.typeahead().data("typeahead");
typeahead.source = scope.moDataSource;
}
};
});
function MainCtrl($scope) {
$scope.addItem = function (item) {
if(item.length > 0)
$scope.array.push(item);
};
$scope.array = ['Safari',"Internet Explorer","FireFox","Chrome"];
}