7 votes

Ajouter une directive personnalisée à une entrée existante qui possède déjà des directives angulaires [ng-model/ng-required].

Je voudrais utiliser un contrôle d'entrée standard qui est décoré de ng-model y ng-required et ensuite ajouter ma propre directive d'attribut personnalisée qui fournit uib-typeahead au contrôle.

J'ai utilisé ce lien pour faire fonctionner partiellement ma directive.

Ajouter des directives à partir d'une directive dans AngularJS

PLUNKR - La version 2 de la directive ne fonctionne pas correctement avec ng-model

Ma directive ajoute la fonctionnalité de typeahead et cela fonctionne très bien, mais elle ne lie pas le modèle au contrôle après la sélection de l'élément.

J'ai deux versions de ma directive.

Version 1 : est une directive de style d'élément et je l'ai utilisée avec succès pendant un certain temps, mais elle s'est révélée insuffisante lorsque j'ai voulu avoir un peu plus de contrôle sur l'élément d'entrée, notamment lorsque j'ai voulu utiliser ng-required='true' et d'autres directives ng-message.

Version 2 est une directive de style d'attribut, je l'ai choisi parce que j'ai pensé qu'il était préférable d'ajouter la fonctionnalité de typeahead que je voulais à n'importe quel HTML standard qui peut utiliser facultativement la directive ng-required='true' , ng-model etc...

Bien que cette directive fonctionne en grande partie, elle n'interagit pas correctement avec ng-model et je ne suis pas sûr de savoir comment le faire fonctionner.

angular.module(APP)

.directive('wkLocationSuggest', ['$compile', function ($compile) {
  return {
    restrict: 'A',
    require: 'ngModel',
    replace: false,
    //terminal: true,
    //priority: 0,
    scope: {
      wkApiModel: '=' // Provide access to the internal data that is returned via the API lookup
    },
    controller: 'LocationSuggestController',
    link: function (scope, element, attrs, ngModelCtrl) {
      if (!ngModelCtrl) {
        return;
      }

      element.attr('typeahead', 'location as row.location for row in typeAhead($viewValue)');
      element.attr('typeahead-wait-ms', '750');
      element.attr('typeahead-on-select', 'onSelectInternal($item, $model, $label)');
      element.attr('typeahead-min-length', '2');
      element.attr('typeahead-focus-first', 'true');

      element.removeAttr("wk-location-suggest");        //remove the location-suggest to avoid indefinite loop
      element.removeAttr("data-wk-location-suggest");   //also remove the same attribute with data- prefix if it exists

      // None of this is working
      //// invoked when model changes from the outside
      //ngModelCtrl.$render = function () {
      //  //scope.innerModel = ngModelCtrl.$modelValue;
      //};

      ////// invoked when model changes from the inside
      //scope.onChange = function (value) {
      //  ngModelCtrl.$setViewValue(scope.innerModel);
      //};

      scope.onSelectInternal = function ($item, $model, $label) {

        // This fires, but it effects the ng-model on the first input, 
        // but not the input that this directive is attached too
        ngModelCtrl.$setViewValue($item.location);

      };

      $compile(element)(scope);

    }
  };
}]);

Ces deux images illustrent une partie du problème, mais il est préférable de faire un test par vous-même en utilisant PLUNKR ci-dessus.

Version 1 & 2 of the directive in action

Version 2 not working correctly

2voto

Ahmad Baktash Hayeri Points 2076

J'ai d'abord essayé de ajouter dynamiquement des validateurs à votre wk-location-suggest-new en mettant en œuvre la directive blur sur l'élément d'entrée en combinaison avec ngModel 's $setValidity mais je ne sais pas exactement ce qui a empêché l'événement de se déclencher.

Par conséquent, je me suis tourné vers l'autre directive wk-location-suggest-old et l'a modifié un peu pour l'adapter aux deux comportements souhaités.

Là, j'ai remarqué qu'il te manquait deux ou trois choses :

  • Tout d'abord, pour qu'un élément de formulaire soit colle avec le formulaire lui-même ( wkProfileCompany dans votre cas), et de travailler avec ng-model l'élément ( dans le modèle de directive ) a besoin d'un nom .
  • Deuxièmement, ng-required (ou required ) ne fonctionnerait avec le formulaire que s'il est ajouté comme attribut à l'élément dans le modèle de directive, no la directive qui compile vers le modèle contenant l'élément.

Définition de la directive

Comme vous pouvez le remarquer, j'ai passé deux propriétés de la portée externe à la portée interne de la directive, à savoir :

  • le site name de l'élément d'entrée,
  • et un isRequired afin de spécifier si l'entrée est requis ou pas.

.

.directive('wkLocationSuggestOld', [function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    scope: {
      name: '@',      // <==
      isRequired: '=' // <==
    },
    template: '<input name="{{name}}" type="text" class="{{innerClass}}" ng-model="innerModel"'
       + ' ng-change="onChange()" uib-typeahead="location as row.location for row in typeAhead($viewValue)" '
       + ' typeahead-wait-ms="750" typeahead-on-select="onSelectInternal($item, $model, $label)" '
       + ' typeahead-min-length="2" typeahead-focus-first="true" '
       + ' ng-required="isRequired">',  // <== added ng-required here
    controller: 'LocationSuggestController',
    link: function (scope, element, attrs, ngModel) {
      if (!ngModel) {
          return;
      }          
      ...
}])

HTML

Enfin, vous pouvez utiliser la directive tweakée dans votre HTML comme tel :

<wk-location-suggest-old class="form-control" type="text" name="location2" ng-model="location2" is-required="true"></wk-location-suggest-old>

Plunker


Mise à jour

L'une des raisons possibles de ng-model ne sont pas correctement liés dans le wk-location-suggest-new à une valeur fournie ( c'est-à-dire location3 ), c'est que vous remplacez l'ensemble DOM avec un nouvel élément personnalisé DOM élément qui est compilé avec le isolé scope de la directive elle-même.

Puisque la directive wk-location-suggest-new a un champ d'application isolé, le champ d'application ne connaît pas du tout les éléments suivants location3 parce que location3 ( et toutes les autres valeurs de localisation ) sont définis dans la portée de MainCtrl et NON la portée de la directive elle-même ; par conséquent, vous finirez par lier la valeur de l'entrée à une directive undefined propriété.

link: function (scope, element, attrs, ngModelCtrl) {
   if (!ngModelCtrl) {
     return;
   }
 ...
$compile(element)(scope); // <== here

1voto

Rishi Tiwari Points 766

Vous devez mettre à jour votre modèle dans setTimout() comme ci-dessous car vous avez une portée isolée dans la directive.

setTimeout(function () {
                    scope.$apply(function () {
                        scope.location3 = 'Your selected value'
                    });
}, 2000);

Vous pouvez également utiliser $timeout pour obtenir le même résultat.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X