Search code examples
javascriptangularjsangularjs-directiveangularjs-components

AngularJS use component as Input tag placeholder


I have a localization component that looks like this:

(function() {
  'use strict';

  angular
    .module('app.core')
    .component('i18n', {
      templateUrl: './i18n.html',
      bindings: {
        code: '@'
      },
      controller: i18n,
      controllerAs: 'vm'
    });


  i18n.$inject = ['langStrings'];

  function i18n(langStrings) {
    const vm = this;
    this.$onInit = () => {
      vm.text = langStrings.get(vm.code);
    };
  }
})();

The i18n template consists of a single line:

{{vm.text}}

This works great for displaying strings, but when I want to use the string in another component or in template as a placeholder for input tag, I don't know how to apply it. So for example how would I apply the components end result for inputs placeholder in another context?

Input in some other component or template.

<input 
    placeholder="<i18n code='searchPlaceholder'/>"
>

I am using angularJs 1.7.2


Solution

  • Input placeholder attribute is waiting for a string. <i18n> is a component rendering an HTML element like its name (restriction in directives as Element witch component actually acts).

    Angular can support interpolation on an attribute. so basically, you need to create something like placeholder="{{exp}}" witch the exp evaluation will have to return a string.

    1. Either you create a component rendering an input as an HTML
    2. Use the method {{langStrings.get(v)}} and cast it to a var so use placeholder="{{vm.text}}" or ng-init="z= lngs('asdf');" placeholder="{{z}}"
    3. Access element and change its attribute which can be done on link function or template function of the component.
    4. Or basically, you create something that it casts a string and not render its HTML. A Custom Service 4 example using i18n.

    try an easier way to change elements attribute on a directive using the link function

    (function() {
      'use strict';
    
      function i18n(langStrings) {
        return {
          restrict: 'CA',
          scope: {
            code: '@'
          },
          link: function(scope, elem, attrs) {
    
            //attrs.$set('placeholder', langStrings.get(scope.code));
            // observe changes to interpolated attribute
            attrs.$observe('code', function(value) {
              let text = langStrings.get(value);
              elem.attr('placeholder', text);
            });
            //https://stackoverflow.com/questions/14876112/angularjs-difference-between-the-observe-and-watch-methods
            //scope.$watch('code', function(newval) {
            //let text = langStrings.get(newval);
            //elem.attr('placeholder', text);
            //});
          },
        }
      }
    
      function testCtrl($scope, langStrings) {
        $scope.lngs = function(v) {
          return langStrings.get(v);
        };
        return this;
      }
    
      angular
        .module('app', [])
        .component('i18c', {
          bindings: {
            code: '@'
          },
          template: function($element, $attrs, langStrings) {
            let c = langStrings.get($attrs.code);
            return `<input placeholder="` + c + `">`;
          }
        })
        .directive('i18nCustom', i18n)
        .controller('testCtrl', testCtrl)
        //i18n.$inject = ['langStrings'];
        .factory('langStrings', function() {
          this.get = (v) => {
            return v + '-transformed';
          };
          return this;
        })
    
    
    })();
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.js"></script>
    
    <body ng-app="app" ng-controller="testCtrl">
      <input class="i18n-custom" code='test-class'>
      <input i18n-custom code='test-attr'>
      <input ng-init="z = lngs('test ctrl fun');" placeholder="{{z}}">
      <i18c code="Test Comp"></i18c>
    </body>