Home>

https://en.stackoverflow.com/questions/47643/angular-%e3%82%ab%e3%82%b9%e3%82%bf%e3%83%a0%e3%81%97% e3% 81% 9fdirective% e3% 82% 92% e5% 8b% 95% e7% 9a% 84% e3% 81% ab% e3% 82% bf% e3% 82% b0% e3% 81% ab% e5% 9f% 8b% e3% 82% 81% e8% be% bc% e3% 81% bf% e3% 81% 9f% e3% 81% 84
This question is also asked on the above site.

In Angular6, I want to use a common directive on multiple screens.
Since multiple programmers embed directives individually in templates, omissions occur, so
For the input tag of name ="corp_name" ;, make sure to embed a directive called"HanToZen"
I want to implement with ngOnInit, but it doesn't work.
The code I tried is below.
Do I have to recompile the template after setAttribute?
If so, how do you recompile the template?
I have been investigating various things, but it has not been solved yet.
It would be very helpful if you could teach someone. Nice to meet you.

* convertHAN2ZEN called in the directive is a function that only converts half-width characters in the received string to full-width.
As long as you know how the directive is valid, you can skip any process.

Tried

directive-test.component.ts

import {Component, OnInit, ElementRef, Renderer2} from '@ angular/core';
@Component ({
  selector: 'app-directive-test',
  templateUrl: './directive-test.component.html',
  styleUrls: ['./directive-test.component.css']
})
export class DirectiveTestComponent implements OnInit {
  element: HTMLElement;
  constructor (private el: ElementRef, private renderer: Renderer2) {
    this.element = el.nativeElement;
   }
  ngOnInit () {
    // Simply set ZenToHan to HTMLelement, the DOM is as expected but the directive did not work
    var input = this.element.getElementsByTagName ("input");
    for (var i = 0;i<input.length;i ++) {
      if (input [i] .getAttribute ("name") === "corp_name") {
        input [i] .setAttribute ("HanToZen", "");
      }
    }
    // I tried create with Renderer2, but the result was the same as when I setAttribute an HTMLelement
    var rendInput = this.renderer.createElement ('input');
    this.renderer.setAttribute (rendInput, 'name', 'corp_name2');
    this.renderer.setAttribute (rendInput, 'HanToZen', '');
    this.renderer.appendChild (this.element, rendInput);
  }
}


han-to-zen.directive.ts

import {OnInit, Directive, ElementRef, HostListener} from '@ angular/core';
import {convertHAN2ZEN} from "./util";
@Directive ({
  selector: '[HanToZen]'})
export class HanToZenDirective implements OnInit {
  private element: HTMLInputElement;
  constructor (
    private elementRef: ElementRef,
  ) {
    this.element = this.elementRef.nativeElement;
  }
  ngOnInit () {
    this.element.value = convertHAN2ZEN (this.element.value);
  }
  @HostListener ("blur", ["$event.target.value"])
  onBlur (value) {
    this.element.value = convertHAN2ZEN (value);
  }
}


directive-test.component.html

<h2>Directive Test</h2>
<form # f = "ngForm" (ngSubmit) = "onSubmit (f)" novalidate>
  <!-First HanToZen works fine->
  <input name = "first" [(ngModel)] = "first" HanToZen>
  <!-HanToZen setAttribute to corp_name doesn't work->
  <input name = "corp_name" value = '100' [(ngModel)] = "corp_name">
  <button>Submit</button>
</form>
Supplemental information (FW/tool version etc.)

Angular CLI: 6.1.3
Node: 8.11.3
OS: win32 x64
Angular: 6.0.3
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Package Version

@ angular-devkit/architect 0.6.5
@ angular-devkit/build-angular 0.6.5
@ angular-devkit/build-optimizer 0.6.5
@ angular-devkit/core 0.6.5
@ angular-devkit/schematics 0.7.3 (cli-only)
@ angular/cli 6.1.3
@ ngtools/webpack 6.0.5
@ schematics/angular 0.7.3 (cli-only)
@ schematics/update 0.7.3 (cli-only)
rxjs 6.2.0
typescript 2.7.2
webpack 4.8.3

  • Answer # 1

    I don't think I can do it, but it's cumbersome and I don't need to be a directive anymore.
    I think that there is not much difference between the risk of missing all ngOnInit descriptions and forgetting to write directives in the template.
    Instead of writing a description to add directives to ngOnInit, what about writing the same thing you want to do with directives there?
    For example, like this

    ngOnInit () {
        const input = this.element.getElementsByTagName ('input');
        for (let i = 0;i<input.length;i ++) {
            if (input [i] .getAttribute ('name') === 'corp_name') {
                Observable.fromEvent (input [i], 'input')
                    .debounceTime (100)
                    .subscribe ((event: any) =>{
                        const replaceText = event.target.value.replace (/ [A-Za-z0-9]/g, function (s) {
                            return String.fromCharCode (s.charCodeAt (0) + 65248);
                        });
                        this.renderer.setProperty (event.target, 'value', replaceText);
                    });
            }
        }
    }

    I think there is a little more initial value setting, so I think that it will be refreshed if the process is divided into Services.

    "Append"

    It's perfectly fine to make a component inherit from another parent class
    Lifecycle method overrides are anti-patterns as you may encounter unexpected bugs
    https://qiita.com/okunokentaro/items/90b60fae2622f7c1f1a2

    Which way
    super.ngOnInit ()
    May not lead to a reduction in work.

    Although it is a method to implement with the

    directive, it is not recommended to say clearly, so please just take a look.
    https://stackblitz.com/edit/angular-k9n4a7
    It is implemented as a directive so that it can be used even with normal classes.
    Even if you don't understand it well, it's not helpful at all.

    "Additional notes"
    If the goal is to avoid forgetting to implement a programmer's directive
    If you create a custom rule in TSLint and there is no directive, you may get a compile error.

  • Answer # 2

    Keisukeh gave me two patterns of answers. Based on these answers, I will write a solution below.

    If the purpose is to prevent the implementation of the directive, TSLint seems to be fine.
    The purpose of this time was "Preventive omission of directive implementation" + "Reduce the amount of coding of implementers even a little" + "Mechanism that implementers need not be aware of decoration rules for controllers". I would like to adopt the idea of ​​"implementing what I want to do with".

    However, since the name attribute must be described in the controller, I want to create a custom rule for TSLint so that there is no omission.

    In fact, instead of implementing the initialization process of Form with ngOnInit of all components, create a parent class and let all components inherit it.
    I want to make it a rule that inherits, so I want to control it with TSLint.

    keisukeh also pointed out that overriding the parent class ngOnInit is an anti-pattern (https://qiita.com/okunokentaro/items/90b60fae2622f7c1f1a2). I will call it on ngOnInit for all components.
    (I would be happy if you could point out if there is a problem with this method.)

    export class AppParent {
        ngOnInit () {
            //do nothing
        }
        initialize () {
            // Form initialization process
        }
    }
    export class ValidateSampleComponent extends AppParent implements OnInit {
        ngOnInit () {
            this.initialize ();
        }
    }