I have an Angular 2 app that uses PrimeNG components.
The UI has autocomplete component with multi-select (p-autoComplete) similar to the one from the documentation:
<p-autoComplete [(ngModel)]="countries"
[suggestions]="filteredCountriesMultiple"
(completeMethod)="filterCountryMultiple($event)"
[minLength]="1"
placeholder="Countries"
field="name"
[multiple]="true">
</p-autoComplete>
The only difference is that in my case the input field has fixed dimensions and scroll bar.
Problem:
Every time when I remove an element from the middle of the autocomplete list it moves focus to the bottom of the input field. It looks like this:
It is very annoying for users, especially when there are several fields that should be removed.
Question: How to force scroll to stay on the same position after removing an element?
How to reproduce:
To be more specific, you can reproduce the issue by adding the next css
max-width: 150px;
max-height: 100px;
overflow-y: auto;
directly on the documentation page into ui-autocomplete-multiple-container.ui-inputtext css class using browser console.
UPDATE:
I managed to get the scroller position by setting up onUnselect method in the component using code like this:
onUnselect(event: any) {
document.querySelector("div.my-input-class").scrollTop
}
UPDATE 2: I managed to make it work
The solution is combination of onUnselect and onFocus event handlers.
First. I save the last scroller position into a field in onUnselect call.
Second. I set this value to the specified element during onFocus call.
The corresponding code looks like this:
scrollPosition: number = 0;
onUnselect(event: any) {
let input = document.querySelector("div.my-input-class");
this.scrollPosition = input.scrollTop;
}
onFocus(event: any) {
let input = document.querySelector("div.my-input-class");
input.scrollTop = this.scrollPosition;
}
It works well and probably this will be the final solution.
However I don't like it. It would be much better if PrimeNG guys embed such a useful functionality in their component. For me it is strange why doesn't it come out of the box.
If you find better solution please propose.
You should be using onFocus event to handle the same as below,
<p-autoComplete [(ngModel)]="countries"
[suggestions]="filteredCountriesMultiple"
(completeMethod)="filterCountryMultiple($event)"
styleClass="width12" (onFocus)="onFocus($event)">
....
onFocus(event){
window.scrollTo(0, 0);
}
LIVE DEMO
onUnselect(event: any) {
// get index of the removed element
// get total options count
// get height of the element that contains the options
// divide the height by number of options
// set scroll potion to the result of division multiplied by the index
document.querySelector('.ui-autocomplete-multiple-container.ui-inputtext').scrollTop = calculatedValue;
}
Related
The Problem:
Before I began adding the div swaps, I could only type into the left (from_value) input and the result of the calculations would be applied via ajax to the right (to_value) input.
I would like to allow the user to type into either box and have the results display in the opposite box they're typing in.
What I am doing to make this happen:
I am swapping the left div with the right div on mouseover of the to_value input. Here's the code i'm using to do the swap:
$.fn.swapWith = function (that) {
var $this = this;
var $that = $(that);
// create temporary placeholder
var $temp = $("<div>");
// 3-step swap
$this.before($temp);
$that.before($this);
$temp.after($that).remove();
return $this;
};
var leftSide = $('#left-side');
var rightSide = $('#right-side');
$('#to_value_input').on('mouseover', function () {
$(rightSide).swapWith(leftSide);
});
This effectively swaps the divs, bringing along with it ids and names of the inputs which retains functionality of my server-side script to perform calculations. As expected, the from_unit select list and to_unit select list are swapped and their values / displayed text are also swapped from one side to the other.
I would like to swap the values and text of the two select boxes either directly before or more likely, directly after the div swap so it appears as if nothing changed on screen.
Similar questions that I have reviewed:
How to swap values in select lists with jquery?
How to swap selected option in 2 select elements?
I have tried several variations of each of the provided solutions. This seems like it should be a fairly simple thing to do but I find myself stuck. Any further help would be greatly appreciated.
The full code base is available on github if you need to see it: https://github.com/pschudar/measurement-conversion
I have made minor changes to the code hosted there to accommodate the div swaps.
I've a datable with responsive active.
Inside a column i've a input field PRICE. When i change it, it update automatically 1 or more input fields inside others column.
Now if i've a large screen and responsive is not active. All works ok.
But if i've a small screen and responsive active itself, i've problem: input field OUTSIDE the screen are not update.
See the fiddle example trying change PRICE input: http://jsfiddle.net/ebRXw/5349/
$(document).ready(function() {
$('#example').DataTable( {
responsive: true
} );
$(".price").on("change", function(e) {
e.preventDefault();
var tr = $(this).closest("tr");
var total = $(this).val() * ( 1 + tr.find(".vat").val() / 100 );
tr.find(".txt_no_responsive_column").val(total);
tr.find(".txt_responsive_column").val(total);
});
} );
I think i've to modify this logic: var tr = $(this).closest("tr"); .Now i've disabled responsive in all my tables and activated scrollX to get it working. But i'd like to activate responsive in all my tables without pain and without doing deep update to javascript! Any solution valid for responsive triggered and responsive not triggered using unique code?
UPDATE: I tried solution provided by #Yash Shukla: jsfiddle.net/ebRXw/5361 but i get problem if I resize windows. Value inside new TR back to original value. See pic
I checked you jsfiddle in both cases if responsive is true or false the value in txt_responsive_column input is getting updated.
What is your main issue? If responsive is false then there is no x scroll bar and you are not able to see the input is this the issue?
Edited :
When you click on + sign it creates a new row with child class, so I have updated the jsfiddle with updated code. You just need to pick that new row also and update input values accordingly.
//code here
http://jsfiddle.net/ebRXw/5361/
Is it possible to control (enable/disable) Google Places Autocomplete SearchBox (google.maps.places.SearchBox) service predictions?
Or in other words: is it possible to temporarily detach HTML input element from Autocomplete SearchBox service and then reattach it?
The thing is that I display service results just bellow HTML input element attached to SearchBox service. Problem is that after results are displayed and user focuses back on the input element, predictions are displayed over results and obscure their view. I would like to disable predictions until text in input element is changed by the user.
EDIT 26/Aug/2016:
Disable predictions is currently not supported by the Javascript API. Therefore I have just opened a feature request on Google. If you are interested in the feature please vote for it: Autocomplete SearchBox - Control (enable/disable) predictions..
EDIT 07/Sep/2016 - bounty award update:
Thanks to all of you who participated in answering and also in promoting the question.
Primary objective of the award was to find solution using currently available means. I am afraid this did not happen so I decided not to award the bounty.
Although none of answers bellow provides a solution, each provides some sort of lead, so thank you! Maybe those leads will point to a solution in future.
Secondary objective of the award (although not communicated directly) was to promote Autocomplete SearchBox - Control (enable/disable) predictions feature request. Its status changed to NeatIdea and has been assigned internal tracking number. It is a good sign.
What you can do is, After the user selects the place, you can add a class disabled to that input field.., This will help you enable/disable predictions based on class name.
And where you have the autocomplete code, you can wrap it within if else statement.
let field = document.getElementById('location');
if ( field.className.indexOf('disabled') > -1 ) {
google.maps.event.clearInstanceListeners(field);
}
else {
let autocomplete = new google.maps.places.Autocomplete( field, {types: ['geocode']} );
autocomplete.addListener('place_changed', () => {
let place = autocomplete.getPlace();
let filed_val = field.value; // incase you need it
field.classList.add('disabled');
});
}
This will remove the autocomplete after user selects a place.. and later if you want, you can remove the disabled class from this field and it will work again.
My solution in AngularJS – it is extract from a directive.
.pac-contained is created after instance of an Autocomplete service is created, e.g.: new google.maps.places.Autocomplete(…) or new google.maps.places.SearchBox(…).
What I do is to find just created .pac-container in the document, store its reference and mark that container as already processes (by adding an arbitrary class .predictions-control on it). "Marking" the container is needed only when more than one .pac-container is expected to be present in application.
Now with the reference I can control visibility (hide or show) of the .pac-contained with predictions.
// Container element with predictions.
var pacContainer = null;
/***
* Find predictions container without predictions-control class set.
* Then set predictions-control class to it and convert it into
* Angular's jqLite object.
* #return {jqLite object} - container or null when not found.
*/
function getPredictionsContainer() {
// Get div.pac-container without predictions-control class.
var e = document.querySelector('div.pac-container:not(.predictions-control)');
if (e){
var container = angular.element(e);
container.addClass('predictions-control');
console.log('predictions-control: Container found.');
return container;
} else {
console.warn('predictions-control: Container not found!');
}
return null;
} // getPredictionsContainer
/***
* Loop in 50ms intervals until container is found.
*/
function untilContainerFound(){
pacContainer = getPredictionsContainer();
if (pacContainer == null){
$timeout(untilContainerFound, 50);
}
} // untilContainerFound
this.init = function() {
untilContainerFound();
}; // this.init
/***
* Prevent predictions to be displayed when user clicks on the
* input element. It is achieved by adding ng-hide CSS class to
* predictions container. Predictions container is identified by
* ".pac-container" CSS class selector.
*/
this.hidePredictions = function() {
// If predictions container was not found at directive
// initialization try to find it now.
if (pacContainer === null){
pacContainer = getPredictionsContainer();
}
if (pacContainer){
console.log('predictions-control: Hiding predictions.');
pacContainer.addClass('ng-hide');
} else {
console.warn('predictions-control: Container not found!');
}
}; // this.hidePredictions
/***
* Show predictions again by removing ng-hide CSS class from
* predictions container.
*/
this.showPredictions = function() {
console.log('predictions-control: Showing predictions.');
if (pacContainer){
pacContainer.removeClass('ng-hide');
}
}; // this.showPredictions
Call init() right after service instance is created:
// Create SearchBox service for auto completing search terms.
autocomplete = new google.maps.places.SearchBox( inputElem[0] );
// OR
// autocomplete = new google.maps.places.Autocomplete( ..... );
autocomplete .addListener('places_changed', callback);
predictionsCtrl.init();
Note:
As long as it is guaranteed that two Autocomplete services are not created at the same time (e.g.: each service is on different tab) or can wait with creation of next service until .pac-container for previous service is found, it reliably works even with multiple instances of Autocomplete service.
There is no way, or much point in having one: predictions are the whole point of SearchBox, its reason to be. If you don't want predictions, you can just use Text Search in the Places library.
If the user clicks/focuses again on the search box, s/he probably didn't care for the results that get obscured by the suggestions. The same behavior is in Google Maps, and it's not an issue, is it?
If you can't put some space between the SearchBox and results (like in this tool), and you absolutely must disable suggestions for a moment, I'd say you can destroy the google.maps.places.SearchBox object and create a new one later, attached to the same HTML input element.
Possibly valuable information.
This is relevant around API V3.29 (not sure if it will always be accurate).
The div element that the API creates for the autocomplete has a class of "pac-container pac-logo".
Utilizing document.querySelector('.pac-container') you may be able to set it's style attribute to display: none on a click event elsewhere.
NOTE: When your users click back in the searchBox google will change the style attribute back to whatever is appropriate, so you only have to set it once, you shouldn't have to set it back again.
(this may be easier and cleaner than getting angular involved).
Hope that helps someone (I had to add a CSS rule to increase the z-index in an application to make the autocomplete show up)
If you add the disabled attributed to the textbox this will disable predictions.
Using readonly attribute does not.
This may help in some circumstances.
I am using handsontable in my project. Selection of continous rows i.e using shift key to select multiple continous rows is working fine, but when i am trying to select random rows using control key it is not working. I have been told that there is no way to select multiple random rows in handsontable.
We are initializing handson constructor in the following way :-
$element.handsontable({
columns:$scope.handsontable.columns,
data: $scope.handsontable.data,
colHeaders: $scope.handsontable.columnHeaders,
colWidths:$scope.handsontable.columnWidths,
readOnly:$scope.handsontable.readOnly,
fillHandle: false,
**currentRowClassName: 'currentRow',
currentColClassName: 'currentCol',**
variableRowHeights : false,
cells: function () {
this.renderer = $scope.gridConfiguration;
}
});
.css class for handson "currentRowClassName" and "currentColClassName" are provided as :-
.handsontable .currentRow {
background-color: #66A3FF !important;
}
.handsontable .currentCol {
background-color: #F9F9FB;
}
When looking into handsontable.js i found the following if block :-
if (priv.settings.currentRowClassName || priv.settings.currentColClassName) {
instance.view.wt.selections.highlight.clear();
instance.view.wt.selections.highlight.add(priv.selRange.from);
instance.view.wt.selections.highlight.add(priv.selRange.to);
}
i tried commenting out its first line( instance.view.wt.selections.highlight.clear();) so that the selection does not get cleared but even this not working.
Please help me with selection of random(not necessarily continuous) rows using handsontable. Thanks
and tried changing it but nothing worked
I don't think HOT supports this sort of selection but you may be able to use some DOM manipulations to achieve it. What you can try is creating a .beforeMouseDown event where if the ctrl key and nothing else is selected, you append to some array the cell that you are currently on.
Then look through the source code to where this same thing is done for Shift. My guess is that it says that when Shift is held down, set SelectedCells to all cells between start and end of the selections. You just need to change it to ONLY start and end, not things in between.
i am adding textfields dynamically as
var input = $('<input class="EditorFields" id="textfield"/>');
$(input).igEditor({
width: 140,
required:true
});
this is working fine.
But when i am trying to add listItems property then its not working.
$(input).igEditor({
width: 140,
required:true,
listItems : ["red","blue","yellow"]
});
i do not want to change the base element to select.
Please help.
First things first - the Ignite UI jQuery API docs state that the button option defaults to none - if you want to have a drop-down add this:
button : "dropdown",
Why this still won't work? Dynamically adding dynamic controls is tricky sometimes if you don't know them in great detail. Same for jQuery, it's a common thing people miss - jQuery creates items in a document fragment so you need to be careful how you append them to the DOM.
When you provide an INPUT element and request a complex editor, the control's additional items (buttons, drop-down, validation messages, etc.) are built around that input and the whole thing wrapped in a container SPAN. Because you refer to the input and it works the first time I assume you attach it directly. Basically when you create a complex editor and only afterwards attach to eh DOM using the same input reference you are leaving all the extra markup stuck in the document fragment.
I can think of about 3 ways to get this to work, you pick what suits best yopur needs:
1) Append item first so the Editor can properly initialize in the DOM afterwards:
var input = $('<input class="EditorFields" id="textfield"/>').appendTo("body");
2) Povide the container, instead the input if possible:
var input1 = $('<span class="EditorFields" id="textfield1"/>');
3) Select the parent when you know you have one and append it instead:
$(input2).igEditor({
width: 140,
required: true,
listItems: ["red2", "blue2", "yellow2"],
button: "dropdown"
}).parent().appendTo("body");
Here's a JSFiddle with all of those: http://jsfiddle.net/damyanpetev/ZRqb2/