AngularJS - ng-model kullanan bir direktif oluştur


294

Yönergeyi oluşturan öğe ile aynı ng-modeli ile bir giriş alanı oluşturacak bir yönerge oluşturmaya çalışıyorum.

Şimdiye kadar bulduğum şey:

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>

JavaScript

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);
    }
  };
});

Bununla birlikte, bu senaryoyu işlemenin doğru yolu olduğundan emin değilim ve kontrolümün ng-model hedef alanının değeri ile başlatılmadığı bir hata var.

Yukarıdaki kodun bir Dalgıç: http://plnkr.co/edit/IvrDbJ

Bunu yapmanın doğru yolu nedir?

EDIT : ng-model="value"şablondan kaldırdıktan sonra , bu iyi çalışıyor gibi görünüyor. Ancak, bu soruyu açık tutacağım çünkü bunu doğru bir şekilde kontrol etmek istiyorum.


1
Kaldırır scopeve olarak ayarlarsanız ne olur scope: false? ng-modelBu durumda nasıl bağlanılır ?
Saeed Neamati

Yanıtlar:


210

EDIT : Bu cevap eski ve muhtemelen eski. Sadece bir kafa yukarı, böylece insanlar yoldan sapmaz. Artık Angular kullanmıyorum, bu yüzden iyileştirmeler yapmak için iyi bir konumda değilim.


Aslında oldukça iyi bir mantık ama işleri biraz basitleştirebilirsiniz.

Direktif

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);
    }
  };
});

Yönetmeli 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;
}

Bu Plunker ile çalışırken görebilirsiniz .

İşte gördüğüm:

  • Neden 'ng-model' kullanmak istediğinizi anlıyorum ama sizin durumunuzda bu gerekli değil. ng-model, varolan html öğelerini kapsamdaki bir değere bağlamaktır . Kendiniz bir yönerge oluşturduğunuz için 'yeni' bir html öğesi oluşturuyorsunuz, bu nedenle ng-modeline ihtiyacınız yok.

DÜZENLEME olarak bunu hiçbir neden yok, onun yorumunda Mark bahsettiği olamaz sadece kongre ile tutmak için, ng-modeli kullanır.

  • Direktifinizde bir kapsam ('yalıtılmış' kapsam) oluşturarak, direktifin kapsamı ana kapsamdaki 'name' değişkenine erişemez (bu yüzden ng-modelini kullanmak istediniz).
  • NgModel'i direktifinizden kaldırdım ve yerine istediğiniz herhangi bir şeyi değiştirebileceğiniz özel bir ad verdim.
  • Her şeyi hala çalıştıran şey, kapsamdaki '=' işaretidir. 'Kapsam' başlığı altındaki doküman dokümanlarına göz atın.

Genel olarak, direktifleriniz izole edilmiş kapsamı (doğru yaptığınız) kullanmalı ve eğer direktifinizdeki bir değerin her zaman üst kapsamdaki bir değerle eşleşmesini istiyorsanız '=' tür kapsamını kullanmalıdır.


18
+1, ancak "ng-model varolan HTML öğelerini kapsamdaki bir değerle bağlamaktır" ifadesini kabul ettiğimden emin değilim. contenteditableAçısal dokümanlar - formlar sayfası , NgModelController sayfasındaki iki yönerge örneği her ikisi de ng-modelini kullanır. NgModelController sayfası, bu denetleyicinin "diğer yönergelerle genişletilmesi gerektiğini" söylüyor.
Mark Rajcok

33
Bu sorunun neden bu kadar yüksek derecelendirildiğinden emin değilim, çünkü ngModel'i kullanmak için orijinal sorunun sorduğu şeyi başaramıyor. Evet, bir kişi ana denetleyiciye durum koyarak ngModel kullanmaktan kaçınabilir, ancak bu iki denetleyicinin sıkıca bağlanması ve bağımsız olarak kullanılamaması / yeniden kullanılmaması pahasına olur. İki bileşen arasında bir dinleyici kurmak yerine küresel bir değişken kullanmak gibidir - teknik olarak daha basit olabilir, ancak çoğu durumda iyi bir çözüm değildir.
Pat Niemeyer

Üst denetleyiciye güvenmek istiyorsa, yine de 'requir: ^ parent' ile enjekte etmesi gerektiğini ekledim - böylece bağımlılığı açık ve isteğe bağlı hale getirebilir.
Pat Niemeyer

3
@Jeroen Ana avantajı gördüğüm şekilde, modelin geçildiği diğer yerlerle tutarlılık hg-model(ve bağlantı sorunu değil, IMO). Bu şekilde veri bağlamı, ister bir <input>ister özel bir yönerge olsun, her zaman ng-modelini kullanır , böylece HTML yazıcısı için bilişsel yükü basitleştirir. Yani my-directive-var, özellikle size yardımcı olacak otomatik tamamlama olmadığından , HTML yazarı her yönerge için adın ne olduğunu bulmak zorunda kalmadan kaydeder .
zai chang

2
umm ... tamam ... ama şimdi bu artık ng-model-optionsya da diğer model şeylerle çalışmıyor, değil mi?
George Mauer

68

Tüm cevapların bir kombinasyonunu aldım ve şimdi bunu ng-model özelliğiyle yapmanın iki yolu var:

  • NgModel'i kopyalayan yeni bir kapsamla
  • Aynı kapsamda bağlantı üzerinde derleme yapar

Bağlantı zamanında derlemeyi sevdiğimden emin değilim. Ancak, öğeyi yalnızca bir başkasıyla değiştiriyorsanız, bunu yapmanız gerekmez.

Sonuçta ilkini tercih ederim. Kapsamı {ngModel:"="}ayarlayın ng-model="ngModel"ve şablonunuzda istediğiniz yeri ayarlayın .

Güncelleme : Kod snippet'ini satır içine aldım ve Angular v1.2 için güncelledim. İzole kapsamın, özellikle jQuery kullanılmadığında hala en iyi olduğu ortaya çıkıyor. Yani şu şekilde kaynar:

  • Tek bir öğeyi değiştiriyor musunuz: Sadece değiştirin, kapsamı yalnız bırakın, ancak v2.0 için değiştirmenin kullanımdan kaldırıldığını unutmayın:

    app.directive('myReplacedDirective', function($compile) {
      return {
        restrict: 'E',
        template: '<input class="some">',
        replace: true
      };
    });
  • Aksi takdirde bunu kullanın:

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

1
Pistonu üç kapsam seçeneğiyle ve şablonun alt öğeleri veya şablonun kök öğesi için güncelledim.
w00t

1
Bu harika, ama bunu nasıl isteğe bağlı yapıyorsunuz? Bir UI kütüphanesi için bir metin kutusu yönergesi oluşturuyorum ve modelin isteğe bağlı olmasını istiyorum, ngModel ayarlanmamışsa metin kutusu hala çalışacak.
Nick Radford

1
@NickRadford ngModel'in $ kapsamında tanımlanıp tanımlanmadığını kontrol edin, eğer değilse kullanmayın?
w00t

1
ng-modelYalıtılmış bir kapsamda yeniden kullanımda herhangi bir sorun veya ek yük olacak mı?
Jeff Ling

2
@jeffling emin değilim ama sanmıyorum. Model kopyalama oldukça hafiftir ve yalıtılmış kapsam pozlamayı sınırlar.
w00t

52

o kadar karmaşık değil: yönlendiricinizde, bir takma ad kullanın: scope:{alias:'=ngModel'}

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

HTML'nizde normal olarak kullanın

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

1
Kendo UI gibi kütüphanelerle uğraşırken bu çok daha kolay. Teşekkürler!
bytebender

30

Ng-modeline yalnızca modelin $ viewValue veya $ modelValue öğesine erişmeniz gerektiğinde ihtiyacınız vardır. Bkz. NgModelController . Ve bu durumda kullanırsınız require: '^ngModel'.

Geri kalanı için Roys cevabı bölümüne bakınız .


2
ng-model, $ viewValue veya $ modelValue'ya ihtiyacınız olmasa bile kullanışlıdır. @ Kolrie'nin örneği gibi sadece ng-modelinin veri bağlama özelliklerini isteseniz bile yararlıdır.
Mark Rajcok

1
Ve ^ng modeli bir ana öğeye uygulanırsa orada olmalıdır
georgiosd

18

Bu biraz geç cevabı, ama bu müthiş buldum yazıyı hakkında NgModelControllerben aradığınız tam olarak ne olduğunu düşünüyorum.

TL; DR - Bağlama işlevinizi kullanabilir require: 'ngModel've sonra ekleyebilirsiniz NgModelController:

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

Bu şekilde, saldırıya gerek yok - Angular'ın yerleşik ng-model



0

Açısal 1.5'ten beri Bileşenler kullanmak mümkündür. Bileşenler en kolay çözümdür ve bu sorunu kolayca çözer.

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

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

Denetleyicinizin içinde yapmanız gereken tek şey:

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

Bulduğum şey, gerçekten "=" yerine "<" yerine Bileşenleri kullanarak en iyi uygulama olan çalışır olmasıdır. Bu cevabın "iç YourController" kısmının ne anlama geldiğinden emin değilim, bunun amacı bileşen içinde ngModel ayarlamak değil?
Marc Stober

1
@MarcStober "YourController" ın içinde sadece ngModel'in alıcı ve ayarlayıcı olarak kullanılabildiğini göstermek istedim. Bu örnekte $ ctrl.result "x" olur.
Niels Steenbeek

Tamam. Bence diğer önemli olan, kontrolör şablonunuzda da yapabileceğiniz input ng-model="$ctrl.ngModel"ve $ ctrl.result ile de senkronize edeceğinizdir.
Marc Stober

0

Bir izolat kapsamı oluşturmak istenmeyen bir durumdur. Scope niteliğini kullanmaktan kaçınır ve böyle bir şey yaparım. scope: true size yeni bir çocuk kapsamı sağlar, ancak yalıtılmaz. Daha sonra, yerel bir kapsam değişkenini kullanıcının ngModel özniteliğine sağladığı nesneye yönlendirmek için ayrıştırma işlevini kullanın.

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);
        }
    };
}]);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.