Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Commit

Permalink
feat(directive): introduce standalone translate-attr directive
Browse files Browse the repository at this point in the history
Relates to #1027, usage:

```html
<input translate-attr="{ placeholder: 'TRANSLATION_ID' }" />
```

Also, a few translate directive cleanups.
  • Loading branch information
odedniv authored and knalli committed Sep 5, 2016
1 parent e3aba1c commit bcb0f2c
Show file tree
Hide file tree
Showing 4 changed files with 337 additions and 13 deletions.
25 changes: 20 additions & 5 deletions docs/content/guide/en/05_using-translate-directive.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,32 @@ $translateProvider.usePostCompiling(true);
## Translate HTML Attributes
You can already translate any HTML element's attribute using the `translate` filter,
but as mentioned above this can create a ton of unnecessary watches on your pages.
Here's a better way to translate those attributes using the `translate` directive!
Here's a better way to translate those attributes using the `translate-attr` directive (starting version 2.12)!

```
<ANY translate translate-attr-ATTRIBUTE_NAME="TRANSLATION_ID></ANY>
<ANY translate-attr="{ ATTRIBUTE_NAME: 'TRANSLATION_ID' }"></ANY>
```

Substitute the HTML element attribute's name for `ATTRIBUTE_NAME` and you will get
all the benefits of the `translate` directive used for that attribute!

```
<img src="mylogo.png" translate translate-attr-alt="LOGO"></img>
<img src="mylogo.png" translate-attr="{ alt: 'LOGO' }"></img>
```

This attribute is parsed and watched, so you can also supply a controller-provided object,
and you can use one-time binding by prefixing your statement with `::`. Here's an example of both:

```
// given a scope variable attrTranslations = { alt: 'LOGO', title: 'TITLE' }
<img src="mylogo.png" translate-attr="::attrTranslations">
```

If your angular-translate did not reach version 2.12, you can use the deprecated way to translate attributes
using the `translate` directive:

```
<ANY translate translate-attr-ATTRIBUTE_NAME="TRANSLATION_ID"></ANY>
```

## Example
Expand All @@ -104,7 +119,7 @@ After that we update our view with our new translation IDs:

<pre>
<p>{{ 'HEADLINE' | translate }}</p>
<img src="mylogo.png" translate translate-attr-alt="LOGO"></img>
<img src="mylogo.png" translate-attr="{ alt: 'LOGO' }"></img>
<p>{{ 'PARAGRAPH' | translate }}</p>

<p translate>PASSED_AS_TEXT</p>
Expand Down Expand Up @@ -142,7 +157,7 @@ Our updated app then looks like this:
</script>
<div ng-controller="Ctrl">
<p>{{ 'HEADLINE' | translate }}</p>
<img src="mylogo.png" translate translate-attr-alt="LOGO"></img>
<img src="mylogo.png" translate-attr="{ alt: 'LOGO' }"></img>
<p>{{ 'PARAGRAPH' | translate }}</p>

<p translate>PASSED_AS_TEXT</p>
Expand Down
141 changes: 141 additions & 0 deletions src/directive/translate-attr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
angular.module('pascalprecht.translate')
/**
* @ngdoc directive
* @name pascalprecht.translate.directive:translate-attr
* @restrict A
*
* @description
* Translates attributes like translate-attr-ATTR, but with an object like ng-class.
* Internally it uses `translate` service to translate translation id. It possible to
* pass an optional `translate-values` object literal as string into translation id.
*
* @param {string=} translate-attr Object literal mapping attributes to translation ids.
* @param {string=} translate-values Values to pass into the translation ids. Can be passed as object literal string.
*
* @example
<example module="ngView">
<file name="index.html">
<div ng-controller="TranslateCtrl">
<input translate-attr="{ placeholder: translationId, title: 'WITH_VALUES' }" translate-values="{value: 5}" />
</div>
</file>
<file name="script.js">
angular.module('ngView', ['pascalprecht.translate'])
.config(function ($translateProvider) {
$translateProvider.translations('en',{
'TRANSLATION_ID': 'Hello there!',
'WITH_VALUES': 'The following value is dynamic: {{value}}',
}).preferredLanguage('en');
});
angular.module('ngView').controller('TranslateCtrl', function ($scope) {
$scope.translationId = 'TRANSLATION_ID';
$scope.values = {
value: 78
};
});
</file>
<file name="scenario.js">
it('should translate', function () {
inject(function ($rootScope, $compile) {
$rootScope.translationId = 'TRANSLATION_ID';
element = $compile('<input translate-attr="{ placeholder: translationId, title: 'WITH_VALUES' }" translate-values="{ value: 5 }" />')($rootScope);
$rootScope.$digest();
expect(element.attr('placeholder)).toBe('Hello there!');
expect(element.attr('title)).toBe('The following value is dynamic: 5');
});
});
</file>
</example>
*/
.directive('translateAttr', translateAttrDirective);
function translateAttrDirective($translate, $rootScope) {

'use strict';

return {
restrict: 'A',
priority: $translate.directivePriority(),
link: function linkFn(scope, element, attr) {

var translateAttr,
translateValues,
previousAttributes = {};

// Main update translations function
var updateTranslations = function() {
angular.forEach(translateAttr, function (translationId, attributeName) {
if (!translationId) return;
previousAttributes[attributeName] = true;

// if translation id starts with '.' and translateNamespace given, prepend namespace
if (scope.translateNamespace && translationId.charAt(0) === '.') {
translationId = scope.translateNamespace + translationId;
}
$translate(translationId, translateValues, attr.translateInterpolation, undefined, scope.translateLanguage)
.then(function (translation) {
element.attr(attributeName, translation);
}, function (translationId) {
element.attr(attributeName, translationId);
});
});

// Removing unused attributes that were previously used
angular.forEach(previousAttributes, function (flag, attributeName) {
if (!translateAttr[attributeName]) {
element.removeAttr(attributeName);
delete previousAttributes[attributeName];
}
});
}

// Watch for attribute changes
watchAttribute(
scope, attr.translateAttr,
function (newValue) { translateAttr = newValue; },
function() { updateTranslations() }
);
// Watch for value changes
watchAttribute(
scope, attr.translateValues,
function (newValue) { translateValues = newValue; },
function() { updateTranslations() }
);

if (attr.translateValues)
scope.$watch(attr.translateValues, updateTranslations, true);

// Replaced watcher on translateLanguage with event listener
scope.$on('translateLanguageChanged', updateTranslations);

// Ensures the text will be refreshed after the current language was changed
// w/ $translate.use(...)
var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);

updateTranslations();
scope.$on('$destroy', unbind);
}
};
}

function watchAttribute(scope, attribute, valueCallback, changeCallback) {
if (!attribute) return;
if (attribute.substr(0, 2) === '::') {
attribute = attribute.substr(2);
} else {
scope.$watch(attribute, function(newValue) {
valueCallback(newValue);
changeCallback();
}, true);
}
valueCallback(scope.$eval(attribute));
}

translateAttrDirective.displayName = 'translateAttrDirective';
12 changes: 4 additions & 8 deletions src/directive/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ angular.module('pascalprecht.translate')
/**
* @ngdoc directive
* @name pascalprecht.translate.directive:translate
* @requires $q,
* @requires $interpolate,
* @requires $compile,
* @requires $parse,
Expand Down Expand Up @@ -95,7 +94,7 @@ angular.module('pascalprecht.translate')
</example>
*/
.directive('translate', translateDirective);
function translateDirective($translate, $q, $interpolate, $compile, $parse, $rootScope) {
function translateDirective($translate, $interpolate, $compile, $parse, $rootScope) {

'use strict';

Expand Down Expand Up @@ -217,7 +216,7 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
});

for (var translateAttr in iAttr) {
if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr') {
if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr' && translateAttr.length > 13) {
observeAttributeTranslation(translateAttr);
}
}
Expand Down Expand Up @@ -314,7 +313,7 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
}

// Replaced watcher on translateLanguage with event listener
var unbindTranslateLanguage = scope.$on('translateLanguageChanged', updateTranslations);
scope.$on('translateLanguageChanged', updateTranslations);

// Ensures the text will be refreshed after the current language was changed
// w/ $translate.use(...)
Expand All @@ -332,10 +331,7 @@ function translateDirective($translate, $q, $interpolate, $compile, $parse, $roo
observeElementTranslation(iAttr.translate);
}
updateTranslations();
scope.$on('$destroy', function(){
unbindTranslateLanguage();
unbind();
});
scope.$on('$destroy', unbind);
};
}
};
Expand Down

0 comments on commit bcb0f2c

Please sign in to comment.