2015-07-22 8 views
7

मैं AngularJS में टाइपहेड निर्देश का उपयोग कर रहा हूं और यह ठीक काम करता है। हालांकि, मैं इनपुट के बाहर एक बटन रखना चाहता हूं जो क्लिक किए जाने पर टाइपहेड ड्रॉपडाउन दिखाएगा। यहाँ है कि मैं क्या कर रहा हूँ के बाद का एक टुकड़ा है ...बटन पर AngularJS शो टाइपहेड

<li class="input"> 
    <input focus-me="click" ng-model="something" 
    typeahead="state for state in Suggestions | filter:$viewValue:stateComparator" typeahead-focus typeahead-focus-first="false" typeahead-on-select="updateTagInput(newTagName)"> 
    <a href="" ng-click="openTypeAhead()">Open</a> 
</li> 
+0

आप वास्तव में क्या दिखाने की उम्मीद कर रहे हैं? – charlietfl

+0

यदि उपयोग-केस वास्तविक है, तो यह उनके github https://github.com/angular-ui/bootstrap – Okazari

+0

@charlietfl पर एक महान सुविधा अनुरोध करेगा जो मैं इसके बाद कुछ हूं [लिंक] (http: //plnkr.co/edit/bZMEOx0Qwo6VzW7oSuEE?p=preview) लेकिन फ़ोकस ईवेंट पर टाइपहेड मेनू खोलने के बजाय मैं दाईं ओर एक बटन जोड़ना चाहता हूं जो मेनू खोल देगा। – kgalb

उत्तर

1

ठीक है, मैं एक JSFiddle या यहाँ तक कि इसके लिए एक Plunkr बनाने का प्रयास कर एक बिल्कुल भयानक समय चल रहा है, तो मैं बस आप इस के लिए कोड दे देंगे निर्देश।

यह निर्देश मूल रूप से आता है ..

This epic Bootstrap library!

..और मैं इसे चुरा लिया है और इसके साथ खेला। यदि आप इसका उपयोग करना चाहते हैं, तो आपको "बूटस्ट्रैप" (वास्तव में कोणीय निर्देशों का एक उप-समूह) लाइब्रेरी की आवश्यकता होगी जिसे मैंने लिंक किया था। आप इस लाइब्रेरी का अपना सबसेट बना सकते हैं, लेकिन मेरे निर्देश में सभी निर्भरताओं के बारे में पूरी तरह से यकीन नहीं है क्योंकि मैं अपनी परियोजना में पूरी लाइब्रेरी का उपयोग कर रहा हूं। असल में, आपको "टाइपहेड" से शुरू होने वाले किसी भी निर्देश की आवश्यकता होती है।

जैसा कि आप देख सकते हैं, मैंने निर्देश wwTypeahead ("ww" वेबवांडर के लिए है!) नाम दिया है। यह निर्देश का उपयोग करना बहुत आसान है और यह मूल की तरह ही काम करता है।

<input 
    class="form-control" 
    type="text" 
    spellcheck="false" 
    ng-model="selection" 
    ng-trim="false" 
    placeholder="Search Here" 
    ww-typeahead="key as key.label for key in list" 
    typeahead-on-select="selectionMade($item, $model, $label)" 
    typeahead-min-length="0" 
/> 

वास्तव में महत्वपूर्ण हिस्सा नोट करने के लिए विशेषता typeahead-min-length="0" जो वास्तव में ऑनलाइन कई चर्चाओं के दिल की गई है। मैं वह काम करने में कामयाब रहा।

यह निर्देश 0 पुस्तकालय में typeahead निर्देश की जगह लेने के लिए है। आपकी टाइपहेड सूची आपके इनपुट बॉक्स के focus पर दिखाई जाएगी। नहीं, सूची एक बटन के क्लिक पर नहीं दिखती है, लेकिन उम्मीद है कि यहां से बच्चे के कदम होंगे। अगर आपको इसे लागू करने में मदद की ज़रूरत है, तो मुझे मदद करने में खुशी होगी।

/* 
    NOTE: 

    The following directive is a modification of the 
    Angular typeahead directive. The normal directives, 
    unfortunately, do not allow matching on 0 length values 
    and the user may want a returned list of all values during 
    the lack of input. 

    This directives was taken from ... 

     http://angular-ui.github.io/bootstrap/ 

    ..and modified. 
*/ 
angular.module('ui.directives', []).directive('wwTypeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser', 
function($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) 
{ 
    var HOT_KEYS = [9, 13, 27, 38, 40]; 

    return { 
     require:'ngModel', 
     link:function(originalScope, element, attrs, modelCtrl) 
     { 
      //SUPPORTED ATTRIBUTES (OPTIONS) 

      //minimal no of characters that needs to be entered before typeahead kicks-in 
      //var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1; 
      var testEval = originalScope.$eval(attrs.typeaheadMinLength); 
      var minSearch = !isNaN(parseFloat(testEval)) && isFinite(testEval) || 1; 

      //minimal wait time after last character typed before typehead kicks-in 
      var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; 

      //should it restrict model values to the ones selected from the popup only? 
      var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; 

      //binding to a variable that indicates if matches are being retrieved asynchronously 
      var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; 

      //a callback executed when a match is selected 
      var onSelectCallback = $parse(attrs.typeaheadOnSelect); 

      var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; 

      //INTERNAL VARIABLES 

      //model setter executed upon match selection 
      var $setModelValue = $parse(attrs.ngModel).assign; 

      //expressions used by typeahead 
      var parserResult = typeaheadParser.parse(attrs.cmcTypeahead); 


      //pop-up element used to display matches 
      var popUpEl = angular.element('<typeahead-popup></typeahead-popup>'); 
      popUpEl.attr({ 
       matches: 'matches', 
       active: 'activeIdx', 
       select: 'select(activeIdx)', 
       query: 'query', 
       position: 'position' 
      }); 
      //custom item template 
      if(angular.isDefined(attrs.typeaheadTemplateUrl)) 
      { 
       popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); 
      } 

      //create a child scope for the typeahead directive so we are not polluting original scope 
      //with typeahead-specific data (matches, query etc.) 
      var scope = originalScope.$new(); 
      originalScope.$on('$destroy', function() 
      { 
       scope.$destroy(); 
      }); 

      var resetMatches = function() 
      { 
       scope.matches = []; 
       scope.activeIdx = -1; 
      }; 

      var getMatchesAsync = function(inputValue) 
      { 
       var matchParsePrefix = originalScope.$eval(attrs.typeaheadParsePrefix); 
       var locals = { 
        $viewValue: inputValue.indexOf(matchParsePrefix) === 0 ? inputValue.substring(matchParsePrefix.length, (inputValue.length + 1)) : inputValue 
       }; 
       isLoadingSetter(originalScope, true); 
       $q.when(parserResult.source(scope, locals)).then(function(matches) 
       { 
        //it might happen that several async queries were in progress if a user were typing fast 
        //but we are interested only in responses that correspond to the current view value 
        //if(matches && inputValue === modelCtrl.$viewValue) 

        /* 
         Ehh.. that didn't seem to work when I "cleared" the input box 
        */ 
        if(matches) 
        { 
         if(matches.length > 0) 
         { 
          scope.activeIdx = 0; 
          scope.matches.length = 0; 

          //transform labels 
          for(var i = 0; i < matches.length; i++) 
          { 
           locals[parserResult.itemName] = matches[i]; 
           scope.matches.push({ 
            label: parserResult.viewMapper(scope, locals), 
            model: matches[i] 
           }); 
          } 

          scope.query = inputValue; 
          //position pop-up with matches - we need to re-calculate its position each time we are opening a window 
          //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page 
          //due to other elements being rendered 
          scope.position = $position.position(element); 
          scope.position.top = scope.position.top + element.prop('offsetHeight'); 

         } 
         else if(minSearch === 0) 
         { 
          resetMatches();//temp 
         } 
         else 
         { 
          resetMatches(); 
         } 
         isLoadingSetter(originalScope, false); 
        } 
       }, function() 
       { 
        resetMatches(); 
        isLoadingSetter(originalScope, false); 
       }); 
      }; 

      resetMatches(); 

      /* 
       Can't figure out how to make this work...*/ 
      if(attrs.hasOwnProperty('typeaheadBindMatchReloader')) 
      { 
       $parse(attrs.typeaheadBindMatchReloader).assign(scope, function() 
       { 
        getMatchesAsync(element[0].value); 
       }); 
      } 




      //we need to propagate user's query so we can higlight matches 
      scope.query = undefined; 

      //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later 
      var timeoutPromise; 

      //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM 
      //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue 
      modelCtrl.$parsers.unshift(function(inputValue) 
      { 
       resetMatches(); 
       if((inputValue && inputValue.length >= minSearch) 
       || minSearch === 0) 
       { 
        if(waitTime > 0) 
        { 
         if(timeoutPromise) 
         { 
          $timeout.cancel(timeoutPromise);//cancel previous timeout 
         } 

         timeoutPromise = $timeout(function() 
         { 
          getMatchesAsync(inputValue); 
         }, waitTime); 
        } 
        else 
        { 
         getMatchesAsync(inputValue); 
        } 
       } 

       if(isEditable) 
       { 
        return inputValue; 
       } 
       else 
       { 
        modelCtrl.$setValidity('editable', false); 
        return undefined; 
       } 
      }); 

      modelCtrl.$formatters.push(function(modelValue) 
      { 
       var candidateViewValue, emptyViewValue; 
       var locals = {}; 

       if(inputFormatter) 
       { 
        locals['$model'] = modelValue; 
        return inputFormatter(originalScope, locals); 
       } 
       else 
       { 
        //it might happen that we don't have enough info to properly render input value 
        //we need to check for this situation and simply return model value if we can't apply custom formatting 
        locals[parserResult.itemName] = modelValue; 
        candidateViewValue = parserResult.viewMapper(originalScope, locals); 
        locals[parserResult.itemName] = undefined; 
        emptyViewValue = parserResult.viewMapper(originalScope, locals); 

        return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue; 
       } 
      }); 

      scope.select = function(activeIdx) 
      { 
       //called from within the $digest() cycle 
       var locals = {}; 
       var model, item; 

       locals[parserResult.itemName] = item = scope.matches[activeIdx].model; 
       model = parserResult.modelMapper(originalScope, locals); 
       $setModelValue(originalScope, model); 
       modelCtrl.$setValidity('editable', true); 

       onSelectCallback(originalScope, { 
        $item: item, 
        $model: model, 
        $label: parserResult.viewMapper(originalScope, locals) 
       }); 

       resetMatches(); 

       //return focus to the input element if a mach was selected via a mouse click event 
       element[0].focus(); 
      }; 

      //bind keyboard events: arrows up(38)/down(40), enter(13) and tab(9), esc(27) 
      element.bind('keydown', function(evt) 
      { 
       //typeahead is open and an "interesting" key was pressed 
       if(scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) 
        return; 

       evt.preventDefault(); 

       if(evt.which === 40) 
       { 
        scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; 
        scope.$digest(); 
       } 
       else if(evt.which === 38) 
       { 
        scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1; 
        scope.$digest(); 
       } 
       else if(evt.which === 13 || evt.which === 9) 
       { 
        scope.$apply(function() 
        { 
         scope.select(scope.activeIdx); 
        }); 
       } 
       else if(evt.which === 27) 
       { 
        evt.stopPropagation(); 
        resetMatches(); 
        scope.$digest(); 
       } 
      }); 

      // Keep reference to click handler to unbind it. 
      var dismissClickHandler = function(evt) 
      { 
       if(element[0] !== evt.target) 
       { 
        resetMatches(); 
        scope.$digest(); 
       } 
       else 
       { 
        getMatchesAsync(element[0].value); 
       } 
      }; 

      $document.bind('click', dismissClickHandler); 

      originalScope.$on('$destroy', function() 
      { 
       $document.unbind('click', dismissClickHandler); 
      }); 

      element.after($compile(popUpEl)(scope)); 
     } 
    }; 
}]); 

कार्रवाई के लिए कॉल:

किसी कृपया इस typeahead निर्देश का एक काम उदाहरण है! मैं हमेशा तुम्हारे लिए ऋण में रहूंगा! (ठीक है, वास्तव में नहीं है, लेकिन यह मुझे बहुत खुश हो जाएगा)

अस्वीकरण:

मैं समझता हूँ कि इस उत्तर के कोई रास्ता नहीं रूढ़िवादी है। मैंने प्रश्नकर्ता के प्रत्यक्ष प्रश्न के साथ पूछताछकर्ता (पूछताछ?) प्रदान नहीं किया था, फिर भी मैंने उन औजारों को प्रदान किया जो मुझे विश्वास है कि उनके जवाब में जाने के लिए आवश्यक हैं। मैं समझता हूं कि मुझे एक कामकाजी उदाहरण बनाने के लिए समय बिताना चाहिए, लेकिन मैं बहुत व्यस्त व्यक्ति हूं और समुदाय के साथ अपना काम साझा करना चाहता हूं, क्योंकि मैंने देखा है कि इस सवाल ने कई बार पूछा है जबकि मैं वापस बैठकर जवाब पकड़ता हूं । अगर आपको कोई समस्या, प्रश्न या जटिलता है तो कृपया मुझे बताएं। मदद करके मुझे खुशी होगी।

धन्यवाद!

संबंधित मुद्दे