gpt4 book ai didi

javascript - AngularJS 在单击按钮时显示提前输入

转载 作者:数据小太阳 更新时间:2023-10-29 04:50:59 25 4
gpt4 key购买 nike

我在 AngularJS 中使用 typeahead 指令,它工作正常。但是,我希望在输入之外有一个按钮,单击该按钮会显示预先输入的下拉列表。这是我所追求的片段......

<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>

最佳答案

好吧,我在尝试为此创建一个 JSFiddle 甚至一个 Plunkr 时遇到了非常糟糕的时间,所以我将只为您提供该指令的代码。

这个指令最初来自..

This epic Bootstrap library!

..我偷了它玩了。如果您想使用它,您将需要我链接到的“Bootstrap”(它实际上是 Angular Directive(指令)的一个子集)库。您可以创建该库的自己的子集,但我不完全确定我的指令具有的所有依赖项,因为我在我的项目中使用了整个库。基本上,您需要任何以“typeahead”开头的指令。

如您所见,我已将指令命名为 wwTypeahead(“ww”代表 WebWanderer!)。这是一个非常易于使用的指令,并且与原始指令一样工作。

<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",它确实是许多在线讨论的核心。我设法做到了。

此指令旨在取代我链接到的库中的 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指令做一个工作示例!我会永远欠你的债! (嗯,不是真的,但它会让我很开心)

免责声明:

我明白这个答案绝不是正统的。我没有向被问者(askee?)提供问题的直接答案,但我确实提供了我认为获得他/她的答案所需的工具。我明白我应该花时间做一个工作示例,但我是一个非常忙碌的人,只是想与社区分享我的工作,因为我已经看到这个问题被问了太多次而我坐下来等待答案.如果您有任何问题、疑问或并发症,请告诉我。我很乐意提供帮助。

谢谢!

关于javascript - AngularJS 在单击按钮时显示提前输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31564461/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com