AngularJS-ng-model을 사용하는 지시문 작성


294

지시문을 작성하는 요소와 동일한 ng-model을 사용하여 입력 필드를 작성하는 지시문을 작성하려고합니다.

지금까지 내가 생각해 낸 내용은 다음과 같습니다.

HTML

<!doctype html>
<html ng-app="plunker" >
<head>
  <meta charset="utf-8">
  <title>AngularJS Plunker</title>
  <link rel="stylesheet" href="style.css">
  <script>document.write("<base href=\"" + document.location + "\" />");</script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js"></script>
  <script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
  This scope value <input ng-model="name">
  <my-directive ng-model="name"></my-directive>
</body>
</html>

자바 스크립트

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.name = "Felipe";
});

app.directive('myDirective', function($compile) {
  return {
    restrict: 'E',
    scope: {
      ngModel: '='
    },
    template: '<div class="some"><label for="{{id}}">{{label}}</label>' +
      '<input id="{{id}}" ng-model="value"></div>',
    replace: true,
    require: 'ngModel',
    link: function($scope, elem, attr, ctrl) {
      $scope.label = attr.ngModel;
      $scope.id = attr.ngModel;
      console.debug(attr.ngModel);
      console.debug($scope.$parent.$eval(attr.ngModel));
      var textField = $('input', elem).
        attr('ng-model', attr.ngModel).
        val($scope.$parent.$eval(attr.ngModel));

      $compile(textField)($scope.$parent);
    }
  };
});

그러나 이것이이 시나리오를 처리하는 올바른 방법이라고 확신하지 못하며 ng-model 대상 필드의 값으로 제어가 초기화되지 않는 버그가 있습니다.

위 코드의 Plunker는 다음과 같습니다. http://plnkr.co/edit/IvrDbJ

이것을 처리하는 올바른 방법은 무엇입니까?

편집 : ng-model="value"템플릿에서를 제거한 후 정상적으로 작동하는 것 같습니다. 그러나 이것이 올바른 방법인지 다시 확인하고 싶기 때문에이 질문을 열어 두겠습니다.


1
제거 scope하고로 설정하면 scope: false어떻게됩니까? 이 경우 어떻게 바인딩 ng-model합니까?
Saeed Neamati

답변:


210

편집 :이 답변은 오래되어 오래된 것 같습니다. 그냥 머리 위로 올라가서 사람들을 타락시키지 않습니다. 더 이상 Angular를 사용하지 않으므로 개선하기에 좋은 위치에 있지 않습니다.


실제로 꽤 좋은 논리이지만 일을 약간 단순화 할 수 있습니다.

지령

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.model = { name: 'World' };
  $scope.name = "Felipe";
});

app.directive('myDirective', function($compile) {
  return {
    restrict: 'AE', //attribute or element
    scope: {
      myDirectiveVar: '=',
     //bindAttr: '='
    },
    template: '<div class="some">' +
      '<input ng-model="myDirectiveVar"></div>',
    replace: true,
    //require: 'ngModel',
    link: function($scope, elem, attr, ctrl) {
      console.debug($scope);
      //var textField = $('input', elem).attr('ng-model', 'myDirectiveVar');
      // $compile(textField)($scope.$parent);
    }
  };
});

지시문이있는 HTML

<body ng-controller="MainCtrl">
  This scope value <input ng-model="name">
  <my-directive my-directive-var="name"></my-directive>
</body>

CSS

.some {
  border: 1px solid #cacaca;
  padding: 10px;
}

Plunker 로 실제로 작동하는 것을 볼 수 있습니다 .

다음은 내가 보는 것입니다.

  • 'ng-model'을 사용하려는 이유를 이해하지만 귀하의 경우에는 필요하지 않습니다. ng-model은 기존 HTML 요소를 범위의 값과 연결 하는 것 입니다. 지시문을 직접 작성하기 때문에 'new'html 요소를 작성하므로 ng-model이 필요하지 않습니다.

편집 Mark가 자신의 의견에서 언급했듯이, ng-model을 사용할 수 없으며 컨벤션을 유지하기 위해 이유가 없습니다 .

  • 지시문 ( '격리 된'범위)에 범위를 명시 적으로 지정하면 지시문의 범위가 상위 범위의 'name'변수에 액세스 할 수 없습니다 (이것은 ng-model을 사용하려고 생각한 이유입니다).
  • 지시문에서 ngModel을 제거하고 원하는 이름으로 바꿀 수있는 사용자 정의 이름으로 바꿨습니다.
  • 모든 것이 여전히 작동하게 만드는 것은 범위에서 '='기호입니다. 워드 프로세서 체크 아웃 문서를 '범위'헤더 아래.

일반적으로 지시문의 값이 항상 상위 범위의 값에 매핑되도록하려면 지시문이 격리 된 범위 (올바른 작업)를 사용하고 '='유형 범위를 사용해야합니다.


18
+1이지만 "ng-model은 기존 HTML 요소를 범위의 값과 연결하는 것"이라는 문장에 동의하지 않습니다. contenteditableAngular 문서- 양식 페이지 , NgModelController 페이지 의 두 지시문 예제는 모두 ng-model을 사용합니다. 그리고 ngModelController 페이지는이 컨트롤러가 "다른 지시어에 의해 확장 될 것"이라고 말합니다.
Mark Rajcok

33
왜이 답변이 그렇게 높은 등급을 받았는지 확실하지 않습니다. 왜냐하면 원래 질문에 대한 답변을 얻지 못했기 때문에 ngModel을 사용해야합니다. 예, 부모 컨트롤러에 상태를 넣어 ngModel 사용을 피할 수는 있지만 두 컨트롤러가 단단히 묶여 있고 독립적으로 사용 / 재사용 할 수는 없습니다. 두 구성 요소 사이에 리스너를 설정하는 대신 전역 변수를 사용하는 것과 같습니다. 기술적으로 간단하지만 대부분의 경우 좋은 해결책이 아닙니다.
Pat Niemeyer

부모 컨트롤러에 의존하고 싶다면 어쨌든 'require : ^ parent'로 주입해야하므로 원하는 경우 종속성을 명시적이고 선택적으로 만들 수 있습니다.
Pat Niemeyer

3
@Jeroen 주요 이점은 모델이 전달되는 다른 위치와의 일관성 hg-model(및 커플 링 문제, IMO가 아님)입니다. 이런 식으로 데이터 컨텍스트 <input>는 사용자 지정 지시문 이든 관계없이 항상 ng-model을 사용 하므로 HTML 작성기의인지 오버 헤드가 단순화됩니다. 즉, HTML 작성기가 my-directive-var각 지시문 의 이름이 무엇인지 알아 내야 합니다. 특히 자동 완성 기능이 없으므로 도움이되지 않습니다.
zai chang

2
음 ... 확인 ...하지만 이제는 더 이상 ng-model-options다른 모델 과 작동하지 않습니다 . 그렇지 않습니까?
George Mauer

68

모든 답변을 결합하여 ng-model 속성 으로이 작업을 수행하는 두 가지 방법이 있습니다.

  • ngModel을 복사하는 새로운 범위로
  • 링크에서 컴파일하는 것과 동일한 범위

링크 타임에 컴파일이 마음에 들지 않습니다. 그러나 요소를 다른 요소로 바꾸는 경우에는 그렇게 할 필요가 없습니다.

대체로 나는 첫 번째를 선호합니다. 범위를 {ngModel:"="}설정하고 설정 하기 만하면됩니다.ng-model="ngModel" 하고 템플릿에서 원하는 위치를 하십시오.

업데이트 : 코드 스 니펫을 인라인하고 Angular v1.2 용으로 업데이트했습니다. 특히 jQuery를 사용하지 않는 경우 격리 범위가 여전히 가장 좋습니다. 따라서 다음과 같이 요약됩니다.

  • 단일 요소를 바꾸는 경우 : 그냥 바꾸고 스코프를 그대로 두십시오. 그러나 v2.0에서는 replace가 더 이상 사용되지 않습니다.

    app.directive('myReplacedDirective', function($compile) {
      return {
        restrict: 'E',
        template: '<input class="some">',
        replace: true
      };
    });
  • 그렇지 않으면 이것을 사용하십시오 :

    app.directive('myDirectiveWithScope', function() {
      return {
        restrict: 'E',
        scope: {
          ngModel: '=',
        },
        template: '<div class="some"><input ng-model="ngModel"></div>'
      };
    });

1
플런저를 세 가지 범위 가능성과 템플릿의 하위 요소 또는 템플릿의 루트 요소로 업데이트했습니다.
w00t

1
이것은 훌륭하지만 어떻게 기본적으로 이것을 선택적으로 만드나요? UI 라이브러리에 대한 텍스트 상자 지시문을 만들고 있는데 모델을 선택적으로 사용하고 싶습니다. 즉, ngModel이 설정되어 있지 않으면 텍스트 상자가 계속 작동합니다.
Nick Radford

1
@NickRadford ngModel이 $ scope에 정의되어 있는지 확인하고 그렇지 않은 경우 사용하지 않습니까?
w00t

1
ng-model격리 된 범위에서 재사용 할 때 문제점이나 추가 오버 헤드가 있습니까?
Jeff Ling

2
@ jeffling 확실하지 않지만 그렇게 생각하지 않습니다. ngModel을 복사하면 매우 가벼우 며 격리 된 범위로 노출이 제한됩니다.
w00t

52

그렇게 복잡하지 않습니다 : 당신의 dirctive에서 별명을 사용하십시오 : scope:{alias:'=ngModel'}

.directive('dateselect', function () {
return {
    restrict: 'E',
    transclude: true,
    scope:{
        bindModel:'=ngModel'
    },
    template:'<input ng-model="bindModel"/>'
}

귀하의 HTML에서 정상적으로 사용하십시오

<dateselect ng-model="birthday"></dateselect>

1
이것은 Kendo UI와 같은 라이브러리를 다룰 때 훨씬 쉽습니다. 감사!
바이트 벤더

30

모델의 $ viewValue 또는 $ modelValue에 액세스해야하는 경우 ng-model 만 필요합니다. NgModelController를 참조하십시오 . 이 경우을 사용 require: '^ngModel'합니다.

나머지는 Roys answer를 참조하십시오 .


2
ng-model은 $ viewValue 또는 $ modelValue가 필요하지 않은 경우에도 유용합니다. @kolrie의 예와 같이 ng-model의 데이터 바인딩 기능 만 원하더라도 유용합니다.
Mark Rajcok

1
그리고는 ^NG-모델은 부모 요소에 적용된 경우에만이 있어야한다
georgiosd

18

이것은 약간 늦은 답변이지만 에 대한 이 멋진 게시물을 찾았습니다.이 게시물NgModelController 은 정확히 당신이 찾고있는 것 같습니다.

TL; DR- 링크 기능을 사용 require: 'ngModel'하여 추가 NgModelController할 수 있습니다.

link: function(scope, iElement, iAttrs, ngModelCtrl) {
  //TODO
}

이렇게하면 해킹이 필요하지 않습니다-Angular의 내장 기능을 사용하고 있습니다 ng-model



0

Angular 1.5부터 구성 요소를 사용할 수 있습니다. 구성 요소는 이동이 간편하며이 문제를 쉽게 해결합니다.

<myComponent data-ng-model="$ctrl.result"></myComponent>

app.component("myComponent", {
    templateUrl: "yourTemplate.html",
    controller: YourController,
    bindings: {
        ngModel: "="
    }
});

YourController 안에서해야 할 일은 :

this.ngModel = "x"; //$scope.$apply("$ctrl.ngModel"); if needed

내가 찾은 것은 실제로 구성 요소를 사용하는 것이 가장 좋은 방법 인 "<"대신 "="를 사용하면 작동한다는 것입니다. 이 답변의 "내부 YourController"부분이 무엇을 의미하는지 잘 모르겠습니다.이 점은 구성 요소 내부에 ngModel을 설정하지 않는 것입니까?
Marc Stober

1
@MarcStober "Inside YourController"로 ngModel을 getter 및 setter로 사용할 수 있다는 것을 보여주고 싶었습니다. 이 예에서 $ ctrl.result는 "x"가됩니다.
Niels Steenbeek

확인. 중요한 다른 부분은 컨트롤러 템플릿에서 수행 할 수 input ng-model="$ctrl.ngModel"있고 $ ctrl.result 와도 동기화된다는 것입니다.
Marc Stober

0

격리 범위를 만드는 것은 바람직하지 않습니다. scope 속성을 사용하지 말고 이와 같은 작업을 수행하십시오. scope : true는 새로운 자식 범위를 제공하지만 격리하지는 않습니다. 그런 다음 구문 분석을 사용하여 사용자가 ngModel 속성에 제공 한 것과 동일한 객체를 로컬 범위 변수로 지정하십시오.

app.directive('myDir', ['$parse', function ($parse) {
    return {
        restrict: 'EA',
        scope: true,
        link: function (scope, elem, attrs) {
            if(!attrs.ngModel) {return;}
            var model = $parse(attrs.ngModel);
            scope.model = model(scope);
        }
    };
}]);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.