sourcetip

각도 정의TypeScript 및 $inject 메커니즘을 사용하는 JS 디렉티브

fileupload 2023. 3. 19. 18:26
반응형

각도 정의TypeScript 및 $inject 메커니즘을 사용하는 JS 디렉티브

최근에 TypeScript로 작업 중인 Angular 프로젝트 중 하나를 리팩터링하기 시작했습니다.컨트롤러를 정의하기 위해 TypeScript 클래스를 사용하는 것은 매우 편리하며 다음과 같은 기능을 통해 최소화된 JavaScript 파일을 사용할 수 있습니다.static $inject Array<string>소유물.또한 클래스 정의에서 Angular 종속성을 분할하지 않고 매우 깨끗한 코드를 얻을 수 있습니다.

 module app {
  'use strict';
  export class AppCtrl {
    static $inject: Array < string > = ['$scope'];
    constructor(private $scope) {
      ...
    }
  }

  angular.module('myApp', [])
    .controller('AppCtrl', AppCtrl);
}

지금 저는 지시적 정의에 대해 유사한 사례를 처리할 해결책을 찾고 있습니다.명령어를 함수로 정의할 수 있는 좋은 방법을 찾았습니다.

module directives {

  export function myDirective(toaster): ng.IDirective {
    return {
      restrict: 'A',
      require: ['ngModel'],
      templateUrl: 'myDirective.html',
      replace: true,
      link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ctrls) => 
        //use of $location service
        ...
      }
    };
  }


  angular.module('directives', [])
    .directive('myDirective', ['toaster', myDirective]);
}

이 경우 디렉티브 정의에서 Angular 종속성을 정의해야 합니다.이 경우 정의와 TypeScript 클래스가 다른 파일에 있을 경우 오류가 발생하기 쉽습니다.typescript와 typescript를 사용하여 디렉티브를 정의하는 가장 좋은 방법은 무엇입니까?$injectTypeScript 를 실장하기 위한 좋은 방법을 찾고 있었습니다.IDirectiveFactory하지만 제가 찾은 솔루션에 만족하지 못했습니다.

클래스를 사용하여 ng에서 상속합니다.IDirective는 TypeScript를 사용하는 방법입니다.

class MyDirective implements ng.IDirective {
    restrict = 'A';
    require = 'ngModel';
    templateUrl = 'myDirective.html';
    replace = true;

    constructor(private $location: ng.ILocationService, private toaster: ToasterService) {
    }

    link = (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ctrl: any) => {
        console.log(this.$location);
        console.log(this.toaster);
    }

    static factory(): ng.IDirectiveFactory {
        const directive = ($location: ng.ILocationService, toaster: ToasterService) => new MyDirective($location, toaster);
        directive.$inject = ['$location', 'toaster'];
        return directive;
    }
}

app.directive('mydirective', MyDirective.factory());

관련 답변: https://stackoverflow.com/a/29223360/990356

지시문에 대해 를 지정하고 거기에 의존관계만 주입하는 것을 선호합니다.

컨트롤러와 인터페이스가 갖추어진 상태에서 링크 기능의 번째 파라미터를 컨트롤러의 인터페이스에 강하게 입력해, 거기서부터의 이용을 즐깁니다.

링크 부분에서 디렉티브의 컨트롤러로 종속성에 대한 관심을 이동시키면 컨트롤러의 TypeScript를 활용할 수 있으며 디렉티브 정의 기능을 짧고 단순하게 유지할 수 있습니다( 디렉티브에 정적 공장 방식을 지정하고 구현해야 하는 디렉티브 클래스 접근 방식과는 다릅니다).

module app {
"use strict";

interface IMyDirectiveController {
    // specify exposed controller methods and properties here
    getUrl(): string;
}

class MyDirectiveController implements IMyDirectiveController {

    static $inject = ['$location', 'toaster'];
    constructor(private $location: ng.ILocationService, private toaster: ToasterService) {
        // $location and toaster are now properties of the controller
    }

    getUrl(): string {
        return this.$location.url(); // utilize $location to retrieve the URL
    }
}

function myDirective(): ng.IDirective {
    return {
        restrict: 'A',
        require: 'ngModel',
        templateUrl: 'myDirective.html',
        replace: true,

        controller: MyDirectiveController,
        controllerAs: 'vm',

        link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes, controller: IMyDirectiveController): void => {
            let url = controller.getUrl();
            element.text('Current URL: ' + url);
        }
    };
}

angular.module('myApp').
    directive('myDirective', myDirective);
}

이 경우 방향 정의에서 각도 종속성을 정의해야 합니다. 정의와 typescript 클래스가 다른 파일에 있으면 오류가 발생하기 쉽습니다.

솔루션:

 export function myDirective(toaster): ng.IDirective {
    return {
      restrict: 'A',
      require: ['ngModel'],
      templateUrl: 'myDirective.html',
      replace: true,
      link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ctrls) => 
        //use of $location service
        ...
      }
    };
  }
  myDirective.$inject = ['toaster']; // THIS LINE

이 파티에는 조금 늦었다.하지만 여기 제가 사용하고 싶은 솔루션이 있습니다.저는 개인적으로 이게 더 깨끗한 것 같아요.

먼저 도우미 클래스를 정의하면 어디서든 사용할 수 있습니다.(도움말 기능을 조금만 바꾸면 실제로 어떤 용도로도 사용할 수 있습니다.config run 등에 사용할 수 있습니다.

module Helper{
    "use strict";

    export class DirectiveFactory {
        static GetFactoryFor<T extends ng.IDirective>(classType: Function): ng.IDirectiveFactory {
            var factory = (...args): T => {
                var directive = <any> classType;
                //return new directive(...args); //Typescript 1.6
                return new (directive.bind(directive, ...args));
            }
            factory.$inject = classType.$inject;
            return factory;
        }
    }
}

여기 메인 모듈이 있습니다.

module MainAppModule {
    "use strict";

angular.module("App", ["Dependency"])
       .directive(MyDirective.Name, Helper.DirectiveFactory.GetFactoryFor<MyDirective>(MyDirective));

    //I would put the following part in its own file.
    interface IDirectiveScope extends ng.IScope {
    }

    export class MyDirective implements ng.IDirective {

        public restrict = "A";
        public controllerAs = "vm";
        public bindToController = true;    
        public scope = {
            isoVal: "="
        };

        static Name = "myDirective";
        static $inject = ["dependency"];

        constructor(private dependency:any) { }

        controller = () => {
        };

        link = (scope: IDirectiveScope, iElem: ng.IAugmentedJQuery, iAttrs: ng.IAttributes): void => {

        };
    }
}

이 기사에서는 거의 그것에 대해 다루고 있으며, tanguy_k의 답변은 기사에 제시된 예시와 거의 일치합니다.그것은 또한 왜 당신이 수업을 이런 식으로 쓰기를 원하는지에 대한 모든 동기부여도 가지고 있다.상속, 활자 검사, 그리고 다른 좋은 것들...

http://blog.aaronholmes.net/writing-angularjs-directives-as-typescript-classes/

저의 솔루션은 다음과 같습니다.

지시:

import {directive} from '../../decorators/directive';

@directive('$location', '$rootScope')
export class StoryBoxDirective implements ng.IDirective {

  public templateUrl:string = 'src/module/story/view/story-box.html';
  public restrict:string = 'EA';
  public scope:Object = {
    story: '='
  };

  public link:Function = (scope:ng.IScope, element:ng.IAugmentedJQuery, attrs:ng.IAttributes):void => {
    // console.info(scope, element, attrs, this.$location);
    scope.$watch('test', () => {
      return null;
    });
  };

  constructor(private $location:ng.ILocationService, private $rootScope:ng.IScope) {
    // console.log('Dependency injection', $location, $rootScope);
  }

}

모듈(지시문을 등록...):

import {App} from '../../App';
import {StoryBoxDirective} from './../story/StoryBoxDirective';
import {StoryService} from './../story/StoryService';

const module:ng.IModule = App.module('app.story', []);

module.service('storyService', StoryService);
module.directive('storyBox', <any>StoryBoxDirective);

데코레이터(디렉티브 오브젝트를 삽입하여 생성):

export function directive(...values:string[]):any {
  return (target:Function) => {
    const directive:Function = (...args:any[]):Object => {
      return ((classConstructor:Function, args:any[], ctor:any):Object => {
        ctor.prototype = classConstructor.prototype;
        const child:Object = new ctor;
        const result:Object = classConstructor.apply(child, args);
        return typeof result === 'object' ? result : child;
      })(target, args, () => {
        return null;
      });
    };
    directive.$inject = values;
    return directive;
  };
}

생각중이야module.directive(...),module.service(...) 파일 "클래스 파일")로 합니다.StoryBoxDirective.ts 아직 하지 않았다

완전한 동작 예는, https://github.com/b091/ts-skeleton 에서 확인할 수 있습니다.

디렉티브는, https://github.com/b091/ts-skeleton/blob/master/src/module/story/StoryBoxDirective.ts 를 참조해 주세요.

이 답변은 @Mobiletainment의 답변에 근거하고 있습니다.초보자도 조금 더 읽기 쉽고 이해하기 쉽도록 하기 위해 포함시켰을 뿐입니다.

module someModule { 

    function setup() { 
        //usage: <some-directive></some-directive>
        angular.module('someApp').directive("someDirective", someDirective); 
    };
    function someDirective(): ng.IDirective{

        var someDirective = {
            restrict: 'E',
            templateUrl: '/somehtml.html',
            controller: SomeDirectiveController,
            controllerAs: 'vm',
            scope: {},
            link: SomeDirectiveLink,
        };

        return someDirective;
    };
    class SomeDirectiveController{

        static $inject = ['$scope'];

        constructor($scope) {

            var dbugThis = true;
            if(dbugThis){console.log("%ccalled SomeDirectiveController()","color:orange");}
        };
    };
    class SomeDirectiveLink{
        constructor(scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes, controller){
            var dbugThis = true;
            if(dbugThis){console.log("%ccalled SomeDirectiveLink()","color:orange");}
        }
    };
    setup();
}

다른 솔루션은 클래스를 만들고 정적 $inject 속성을 지정하여 새 연산자를 사용하여 클래스가 호출되는지 여부를 감지하는 것입니다.그렇지 않으면 새 연산자를 호출하여 디렉티브클래스의 인스턴스를 만듭니다.

다음은 예를 제시하겠습니다.

module my {

  export class myDirective {
    public restrict = 'A';
    public require = ['ngModel'];
    public templateUrl = 'myDirective.html';
    public replace = true;
    public static $inject = ['toaster'];
    constructor(toaster) {
      //detect if new operator was used:
      if (!(this instanceof myDirective)) {
        //create new instance of myDirective class:
        return new (myDirective.bind.apply(myDirective, Array.prototype.concat.apply([null], arguments)));
      }
    }
    public link(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ctrls:any) {

    }
  }

}

답변의 모든 옵션은 2개의 엔티티(ng)라는 아이디어를 주었습니다.IDirective 및 Controller)는 컴포넌트를 설명하기에 너무 많습니다.그래서 간단한 래퍼 프로토타입을 만들었습니다.다음은 시제품 https://gist.github.com/b1ff/4621c20e5ea705a0f788의 요지입니다.

언급URL : https://stackoverflow.com/questions/26920055/define-angularjs-directive-using-typescript-and-inject-mechanism

반응형