Directive inside a angular-translate translation - angularjs

I'm using angular-translate for i18n and want to use a directive inside a translation:
var translations = {
TEST_1: 'Hello from Test',
TEST_2: 'Hello from <user></user>'
};
app.directive('user', function() {
return {
template: 'Test'
};
});
Full plnkr example: http://plnkr.co/edit/jCCcvx7IEaAYUwyaQ7uH?p=preview
So
<p translate="TEST_1"></p>
<p translate="TEST_2"></p>
should be the same. The first (without directive) works, the second doesn't. It transcludes <user></user>, but Angular doesn't seem to be aware of it and doesn't do its directive magic.

Try to use the translate-compile directive:
<p translate="TEST_2" translate-compile></p>
From the docs:
Starting with version 2, the translation itself can be post processed
in context of the current scope (using $compile). This means any
directive used in a translation value itself will now work as
expected.

Related

Angular.js: Data binding not working with directives controller option

Why is angular's data binding not working when I specify a controller in the directives controller option? $scope.emailInvalid.text normally should get mapped to type.text but in my case, nothing get's displayed.
JS:
.directive('alert', function () {
return {
template: '<div>{{type.text}}</div>',
restrict: 'E',
scope: {
type: '='
},
controller: function ($scope) {
$scope.emailInvalid = {
text: 'Text Alert Two'
};
}
};
});
HTML:
<alert type="emailInvalid"></alert>
When I define a separate controller and pass it with ng-controller in the HTML, everything works like expected.
Here is a plunker.
Since you want to display type.text you need to define
$scope.type = {
text: 'Text Alert Two'
};
in your directive controller. By doing so, you don't event have to pass the object to the directive.
PLUNKR
OK, I found a solution:
The problem was that angular fails at mapping $scope.emailInvalid to $scope.type. What I have done in my example with <alert type="emailInvalid"></alert>, is passing an object emailInvalid to the directive. Angular is looking for this object in the scopes model of where I used the directive. Obviously this object doesn't exist and angular can't map anything.
The part I was missing is, that the controller I defined with the directives controller option is defined in a different scope than the controller I used with ng-controller.
To work around this problem I'm now passing a string instead of an object and use switch/case to map the alert type.
plunker

Function arguments in directive with templateUrl

I created a custom directive with an isolated scope that binds to a function from the enclosing controller and with references to a templateUrl. Here's what my code looks like:
the html
<div ng-controller='MyCtrl as my'>
<custom-directive data='my.data' on-search="my.find(param1, param2)"></custom-directive>
</div>
the directive
app.directive('customDirective', function() {
return {
restrict : 'E',
scope : {
data : '=data'
search : '&onSearch',
},
templateUrl : 'customDirective.html'
};
});
the template
<div>
<input ng-model='data.id'>
<a ng-click='find(param1, param2)'></a>
</div>
The arguments received by function find is also stored in data. The controller data binds to the directive but not the function. My log inside the function won't even show.
It seems there are different ways to do it as I have seen in many examples (see below) but none seems to work in my case.
Example 1: pass a mapping of parameter and values in the template
<div>
<input ng-model='data.id'>
<a ng-click='find.({param1: data.value1, param2: data.value2})'></a>
</div>
Example 2: put a link in the directive
app.directive('customDirective', function() {
return {
restrict : 'E',
scope : {
data : '=data'
search : '&onSearch',
},
templateUrl : 'customDirective.html',
link : function(scope, elem, attr) {
scope.retrieve({param1: scope.data.value1,
param2: scope.data.value2});
}
};
});
Example 3 : use scope.$apply(), $parse in link but haven't tried this
Could someone show me how to do it and also explain to me the link part (I don't understand that part) and if you're feeling generous, show the working alternatives as shown by the examples. Thanks
You don't have to passe params for your function just the reference so in your html
<custom-directive data='my.data' on-search="my.find"></custom-directive>
and your template directive directly call
<div>
<input ng-model='data.id'>
<a ng-click='find(data.value1, data.value2)'></a>
</div>
I also suggest you to use $scope and not the controller. So in your controller define
$scope.data = {
id: 1,
value1: "value1",
value2: "value2"
}
$scope.find = function (param1, param2) {
//Your logic
}
And in your template put directly
<custom-directive data='data' on-search="find"></custom-directive>
I hope this answer to your question
About link this text from angular js doc is pretty clear I think
Directives that want to modify the DOM typically use the link option.
link takes a function with the following signature, function
link(scope, element, attrs) { ... } where:
scope is an Angular scope object. element is the jqLite-wrapped
element that this directive matches.
attrs is a hash object with key-value pairs of normalized attribute names and their
corresponding attribute values.
In our link function, we want to update the
displayed time once a second, or whenever a user changes the time
formatting string that our directive binds to. We will use the
$interval service to call a handler on a regular basis. This is easier
than using $timeout but also works better with end-to-end testing,
where we want to ensure that all $timeouts have completed before
completing the test. We also want to remove the $interval if the
directive is deleted so we don't introduce a memory leak.

How do I assign an attribute to ng-controller in a directive's template in AngularJS?

I have a custom attribute directive (i.e., restrict: "A") and I want to pass two expressions (using {{...}}) into the directive as attributes. I want to pass these attributes into the directive's template, which I use to render two nested div tags -- the outer one containing ng-controller and the inner containing ng-include. The ng-controller will define the controller exclusively used for the template, and the ng-include will render the template's HTML.
An example showing the relevant snippets is below.
HTML:
<div ng-controller="appController">
<custom-directive ctrl="templateController" tmpl="template.html"></custom-directive>
</div>
JS:
function appController($scope) {
// Main application controller
}
function templateController($scope) {
// Controller (separate from main controller) for exclusive use with template
}
app.directive('customDirective', function() {
return {
restrict: 'A',
scope: {
ctrl: '#',
tmpl: '#'
},
// This will work, but not what I want
// Assigning controller explicitly
template: '<div ng-controller="templateController">\
<div ng-include="tmpl"></div>\
</div>'
// This is what I want, but won't work
// Assigning controller via isolate scope variable from attribute
/*template: '<div ng-controller="ctrl">\
<div ng-include="tmpl"></div>\
</div>'*/
};
});
It appears that explicitly assigning the controller works. However, I want to assign the controller via an isolate scope variable that I obtain from an attribute located inside my custom directive in the HTML.
I've fleshed out the above example a little more in the Plunker below, which names the relevant directive contentDisplay (instead of customDirective from above). Let me know in the comments if this example needs more commented clarification:
Plunker
Using an explicit controller assignment (uncommented template code), I achieve the desired functionality. However, when trying to assign the controller via an isolate scope variable (commented template code), it no longer works, throwing an error saying 'ctrl' is not a function, got string.
The reason why I want to vary the controller (instead of just throwing all the controllers into one "master controller" as I've done in the Plunker) is because I want to make my code more organized to maintain readability.
The following ideas may be relevant:
Placing the ng-controller tags inside the template instead of wrapping it around ng-include.
Using one-way binding ('&') to execute functions instead of text binding ('#').
Using a link function instead of / in addition to an isolate scope.
Using an element/class directive instead of attribute directive.
The priority level of ng-controller is lower than that of ng-include.
The order in which the directives are compiled / instantiated may not be correct.
While I'm looking for direct solutions to this issue, I'm also willing to accept workarounds that accomplish the same functionality and are relatively simple.
I don't think you can dynamically write a template key using scope, but you certainly do so within the link function. You can imitate that quite succinctly with a series of built-in Angular functions: $http, $controller, $compile, $templateCache.
Plunker
Relevant code:
link: function( scope, element, attrs )
{
$http.get( scope.tmpl, { cache: $templateCache } )
.then( function( response ) {
templateScope = scope.$new();
templateCtrl = $controller( scope.ctrl, { $scope: templateScope } );
element.html( response.data );
element.children().data('$ngControllerController', templateCtrl);
$compile( element.contents() )( templateScope );
});
}
Inspired strongly by this similar answer.

Angular: What is a good way to hide undefined attributes that exist in isolated scopes?

I wanted to rewrite this fiddle as it no longer worked in angular 1.2.1. From this exercise, I learned that a template is apparently always needed now in the isolated scopes.
somewhere in the directive:
template: '<p>myAttr1 = {{myAttr1}} // Passed by my-attr1<br>
myAttr2 = {{myAttr2}} // Passed by my-alias-attr2 <br>
myAttr3 = {{myAttr3}} // From controller
</p>',
I was not able,however, to successfully add this to the template:
<p ng-show="myAttr4">myAttr4= {{myAttr4}} // Hidden and missing from attrs</p>
What is a good way to hide undefined attributes that are defined on the isolated scope but not given a value from the dom?
my humble fiddle
EDIT: I use a directive called my-d1 to encapsulate the bootstrap tags. I use my-d2 to demo how to use the # in isolated scopes.
Working version merged with Sly's suggestions
I ran into the same template issue in Angular 1.2.0, see the first entry in the 1.2.0 breaking changes:
Child elements that are defined either in the application template or in some other directives template do not get the isolate scope. In theory, nobody should rely on this behavior, as it is very rare - in most cases the isolate directive has a template.
I'm not exactly sure what the issue is that you are encountering - it might be some incorrect markup or you are misnaming the scope variables listed in your isolate scope.
Using ng-show will correctly hide the element if the attribute has not been passed in.
i.e. your example here is correct: <p ng-show="myAttr4">myAttr4= {{myAttr4}}</p>
Updated version of your Fiddle: http://jsfiddle.net/Sly_cardinal/6paHM/1/
HTML:
<div ng-app='app'>
<div class="dir" my-directive my-attr1="value one" my-attr3='value three'>
</div>
<div class="dir" my-directive my-attr1="value one" my-attr3='value three' my-attr4='value four'>
</div>
</div>
JavaScript:
var app = angular.module('app', []);
app.directive('myDirective', function () {
return {
// can copy from $attrs into scope
scope: {
one: '#myAttr1',
two: '#myAttr2',
three: '#myAttr3'
},
controller: function ($scope, $element, $attrs) {
// can copy from $attrs to controller
$scope.four = $attrs.myAttr4 || 'Fourth value is missing';
},
template: '<p>myAttr1 = {{one}} // Passed by my-attr1</p> '+
'<p ng-show="two">myAttr2 = {{two}} // Passed by my-alias-attr2 </p>'+
'<p>myAttr3 = {{three}} // From controller</p>'+
'<p ng-show="four">myAttr4= {{four}} // Has a value and is shown</p>'
}
});

Angular directive does not work if we change name

I am seeing a strange behaviour when using angular directive.
In the code below
HTML
<body ng-app="loadTweetsModule">
<div tweets> Load </div>
<div loadTweets> loadTweets </div>
</body>
Javascript
var loadTweetsModule = angular.module("loadTweetsModule",[]);
loadTweetsModule.directive('tweets',function(){
return {
link : function(scope,element){
element.bind("mouseenter", function(){
console.log("tweets");
});
}
};
});
loadTweetsModule.directive('loadTweets',function(){
return {
link : function(scope,element){
element.bind("mouseenter", function(){
console.log("loadTweets");
});
}
};
});
JSFiddle Link
The two directive loadTweets and tweets are the same except for the name. The directive tweets works as expected but loadTweets does not work. I am not able to find out the reason for this behaviour. Can somebody explain this?
if you write in js loadTweets you should write in html load-tweets
from http://docs.angularjs.org/guide/directive
Directives have camel cased names such as ngBind. The directive can be
invoked by translating the camel case name into snake case with these
special characters :, -, or _. Optionally the directive can be
prefixed with x-, or data- to make it HTML validator compliant. Here
is a list of some of the possible directive names: ng:bind, ng-bind,
ng_bind, x-ng-bind and data-ng-bind.

Resources