Angular 4 scroll with arrow keys from text field to <li> - html

I am trying to implement a google like search field, i have partially managed but what i cant make work is the arrow scrolling possibility.
I would like, same as in their search field, to be able to scroll through the search result list with the arrow keys from the search field:
What i have so far is the following:
<div>
<div class="searcher">
<input name="term" #term class="ng-valid ng-touched" size="60"
placeholder="Start typing something...." (keyup)="invokeAutoSuggest(term.value)" (click)="showElement = !showElement">
<br/>
</div>
</div>
<div id="autosuggestBox" class="autoSuggestGoogleLike" [hidden]="!showElement">
<div class="autoSuggestDiv" *ngFor="let suggestion of suggestions" (click)="searchFromAutosuggest(suggestion.type,suggestion.value)" (click)="showElement = !showElement">
{{suggestion.type}}:{{suggestion.value}}
</div>
</div>
The css:
.autoSuggestGoogleLike{
border: 1px solid grey;
width: 450px;
height: 200px;
position: relative;
z-index:3;
background-color : white;
margin-left:-228px;
position: absolute;
left:50%;
overflow: scroll;
}
So searcher is the text field, and autosuggestBox the placeholder showing the results as list, i would like to be able to click the down arrow from the searcher text field and access the results.
Any advice?
Does it have to do with relationship between the divs?

You have to check the keyCode first in your invokeAutoSuggest function, to detect up/down keypress then based on these you can increase/decrease the activeIndex. Like this ...
In component
...
private activeIndex: number = 0;
public invokeAutoSuggest(ev: Event) {
if (38 === ev.keyCode) {
return this.prevActiveMatch();
}
if (40 === ev.keyCode) {
return this.nextActiveMatch();
}
// your code ...
}
public nextActiveMatch() {
this.activeIndex = this.activeIndex < this.list.length - 1 ? ++this.activeIndex : this.activeIndex;
}
public prevActiveMatch () {
this.activeIndex = this.activeIndex > 0 ? --this.activeIndex : 0;
}
...
In template
...
<div class="searcher">
<input name="term" #term class="ng-valid ng-touched" size="60"
placeholder="Start typing something...." (keyup)="invokeAutoSuggest($event)" (click)="showElement = !showElement">
</div>
...
<div class="autoSuggestDiv" *ngFor="let suggestion of suggestions; let i = index" (click)="searchFromAutosuggest(suggestion.type,suggestion.value)" (click)="showElement = !showElement" [ngClass]="{active: i===activeIndex}">
{{suggestion.type}}:{{suggestion.value}}
</div>
...
To see the selected list item
styles: [`
.active {
background: blue;
color: white;
}
`]

Related

How to make new line input message by pressing shift + enter? Angular

I have a chat box with a single input field. I want to make handle new line input like Facebook's chatbox.
My Chat Box:
[1]: https://i.stack.imgur.com/NeG7d.png
My HTML of input field:
<form class="form" #msgForm="ngForm" (ngSubmit)="send(msgForm)" focus>
<div class="form-group">
<div class="col-xs-5">
<div class="input-group">
<input type="search" autocomplete="off" class="form-control left-border-none"
name="message" [(ngModel)]="message" placeholder="Say Something..."
(input)="onChange($event.target.value)" />
<span class="input-group-addon transparent">
<button type="submit" (keyup.enter)="send(msgForm)"
style="border: 0; background: none;cursor: pointer;"><i class="fa fa-paper-plane" [hidden]="isHidden"
style="font-size:20px;color: #6d5cae;"></i></button>
</span>
</div>
</div>
</div>
</form>
Here's my solution.
Brief Note It's not the exact answer because I had the same problem and after a lot of testing, found more adequate to let "Enter" for newlines and"Shift+Enter" for sending messages
This is the opposite logic of popular apps like WS and MSg but I found
more suitable for people having a conversations over a web based chat, since in a conversation you want sometimes to write long messages, and for people habituated to type on text editors, this is the logic function of pushing "Enter".
People may disagree, but just hope you can find it useful
What I did was to using a text-area as container, for easier auto-sizing and multi-line configuration
After that, I created a button that posts the message once clicked,and a #Hostlistener for the event to bind the button to the method.
Please refer to the code.
Best regards
HTML from the typing bar component
<!-- (keydown)="onKey($event)" -->
<!-- submit message with enter -->
<!-- (keydown.enter)="postMessage(messageContainer)" -->
<!-- (keyup.enter)="onKeydown($event)" // for event methods -->
<div class="typing-bar prim-color" >
<textarea
matInput
matAutosize
name=""
cdkTextareaAutosize
#messageContainer
#autosize="cdkTextareaAutosize"
cdkAutosizeMinRows="2"
cdkAutosizeMaxRows="7"
>
</textarea>
<button
mat-stroked-button
color="accent"
(click)="postMessage(messageContainer)"
[disabled]="messageContainer.value===''"
><mat-icon>send</mat-icon> Send</button>
</div>
TS Code for the component
import { Component, HostListener, OnInit } from '#angular/core';
#Component({
selector: 'app-typing-bar',
templateUrl: './typing-bar.component.html',
styleUrls: ['./typing-bar.component.scss']
})
export class TypingBarComponent implements OnInit {
displayBar: boolean = true;
constructor() { }
ngOnInit(): void {
}
#HostListener('body:keydown.shift.enter',['$event'])launchPost($event: KeyboardEvent): void{
return this.postMessage($event.target as HTMLTextAreaElement)
}
postMessage(textarea:HTMLTextAreaElement){
//post message here
// ....logic...
//reset textarea
textarea.value = ""
//give focus back
textarea.focus();
//debug: this is for test only. Delete after testing
alert('sent')
}
}
SCSS code for the component (maybe not the final version)
.typing-bar{
display: flex;
justify-content: space-around;
padding: 3px;
padding-right: 10px;
margin: 1px;
position: sticky;
bottom: 0;
}
textarea {
overflow: auto;
width: 93%;
// max-height: 20%;
border-radius: 10px;
font-size: 1.3em;
font-family: Arial, Helvetica, sans-serif;
}
// .typing-bar button{
// }

How to write jquery function for previous button?

I have created a step form with HTML & jquery which has next and previous buttons for navigating to different steps. I have written jquery function for next buttons, but could'nt do one for previous buttons. Please help me with code.
code to trigger next button
var allNextBtn = $('.nextBtn');
allNextBtn.click(function () {
var curStep = $(this).closest(".setup-content"),
curStepBtn = curStep.attr("id"),
nextStepWizard = $('div.setup-panel div a[href="#' + curStepBtn + '"]').parent().next().children("a"),
curInputs = curStep.find("input[type='text'],input[type='url']"),
isValid = true;
$(".form-group").removeClass("has-error");
for (var i = 0; i < curInputs.length; i++) {
if (!curInputs[i].validity.valid) {
isValid = false;
$(curInputs[i]).closest(".form-group").addClass("has-error");
}
}
if (isValid) nextStepWizard.removeAttr('disabled').trigger('click');
});
I would suggest another approach to your problem:
You want to create a stepper
In the stepper, you would like to show pages of a form
The stepper needs to have controls: prev, next (, first, last?)
The controls should take form validation into account (on the current page)
You want to do DOM manipulation with jQuery
Consider the following snippet:
var currentPage = 1
jQuery(document).ready(function($) {
// init first page
updatePage(0)
jQuery(".btn.next").on('click', function(e) {
e.preventDefault()
// checking for last item & if page input fields are valid
const direction = ($('.page').length > currentPage && validatePage()) ? 1 : 0
updatePage(direction)
})
jQuery(".btn.prev").on('click', function(e) {
e.preventDefault()
// checking for fist item
const direction = 1 < currentPage ? -1 : 0
updatePage(direction)
})
})
// simple validation function
function validatePage() {
const inputValue = jQuery(`[data-page-order="${ currentPage }"]`).find('input').val()
return inputValue !== ''
}
// simple page visibility update function
function updatePage(direction) {
if (direction === 1 || direction === -1) {
currentPage += direction
$(".page.visible").fadeOut().removeClass('visible')
$(`[data-page-order="${ currentPage }"]`).fadeIn().addClass('visible')
} else {
$(".page.visible").removeClass('visible')
$(`[data-page-order="${ currentPage }"]`).addClass('visible')
}
}
form {
width: 50%;
}
.pages {
position: relative;
height: 50px;
background-color: rgba(0, 0, 0, 0.2);
display: flex;
justify-content: center;
align-items: center;
}
.pages>.page {
display: block;
position: absolute;
display: none;
}
.pages>.page.visible {
display: block;
}
.controls {
display: flex;
justify-content: space-between;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<form>
<div class="pages">
<div class="page" data-page-order="1">
<label for="input1">Input 1:
<input id="input1" type="text" />
</label>
</div>
<div class="page" data-page-order="2">
<label for="input2">Input 2:
<input id="input2" type="text" />
</label>
</div>
<div class="page" data-page-order="3">
<label for="input3">Input 3:
<input id="input3" type="text" />
</label>
</div>
</div>
<div class="controls">
<button class="btn prev">PREV</button>
<button class="btn next">NEXT</button>
</div>
</form>
</div>
You can see that with the manipulation of a single variable (currentPage) prev & next steps can be done - with validation rules, form pages appear/disappear with a fadeIn/fadeOutand all.
I do hope that this snippet gives you the idea about how to solve a problem like this - break it down to requirements that are simple and then don't overcomplicate :) Just add one feature at a time (like prev/next, validation, etc.).

add a show password button inside of input field - crispy forms

I want to add a show password button inside of the password input field.
I am using crispy forms for styling so I am a little bit stuck on the best practice here.
Currently I have a seperate checkbox field which is showing the password on check. Js is working fine here.
I'd like to change de checkbox to a eye icon which is inside of the input field.
I've managed to add the eye icon to the input field via css background: url(path-to-icon).
I can't figure out how to change the icon to a button (1) and add my js to it (2).
My form:
class CustomSignupForm(SignupForm):
user_group = forms.ModelChoiceField(queryset=Group.objects.exclude(name='admin').exclude(name='personeel').exclude(name='onderhoud'),
widget=forms.RadioSelect(attrs={'placeholder': 'Soort klant:'}),
initial=('particulier'), label='Soort klant'
)
first_name = forms.CharField(max_length=30,
label='Voornaam',
widget=forms.TextInput(attrs={'placeholder': 'Voornaam'}),)
last_name = forms.CharField(max_length=30,
label='Achternaam',
widget=forms.TextInput(attrs={'placeholder': 'Achternaam'}),)
My HTML :
<div class="form-row">
<div class="form-group col-md-6 mb-0">
<a>{{ form.password1 | as_crispy_field}}</a>
</div>
<div class="form-group col-md-6 mb-0">
<a>{{ form.password2 | as_crispy_field}}</a>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12 mb-0">
<input class="ml-2" type="checkbox" onclick="ShowPassword()"> show password
</div>
</div>
My css:
#id_password1 {
background: url("http://127.0.0.1:8000/static/user/media/eye_icon.png") no-repeat;
background-size: 20px 20px;
background-position: right;
background-position-x: 97%;
}
#id_password2 {
background: url("http://127.0.0.1:8000/static/user/media/eye_icon.png") no-repeat;
background-size: 20px 20px;
background-position: right 10px;
background-position-x: 97%;
}
My js :
function ShowPassword() {
var x = document.getElementById("id_password");
if (x.type === "password") {
x.type = "text";
} else {
x.type = "password";
}
}
Does anyone have any ideas to make this work?
Did you try font awesome ?
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
Add this to base.html
<button class="btn"><i class="fa fa-eye"></i></button>
Add this where ever you'd like to place the icon.
.btn{
cursor: pointer;
}
.btn-hover{
background-color:YourChoice;
}
Add this to your css file accordingly .
Thanks! Made this work properly only need to make a custom input field with the button inside of the field via the crispy form helper. Can't figure out how to do this though. Got something like this right now:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Field('password1', on_click="ShowPassword()")
)
My working button right now:
<input type="image" class="ml-2" src="/static/user/media/eye_icon.png" onclick="ShowPassword()" id="show-password-button">
You could try :
Field(
PrependedText('password',mark_safe('<span class ="glyphicon glyphicon-eye-open"></span>'), placeholder=_("Enter Password")))
Made it almost!
Did everything in HTML, CSS and JS.
HTML
<a>{{ form.password2 | as_crispy_field}}</a>
<button class="toggle-password ml-2 fa fa-eye"></button>
CSS
.toggle-password {
position: absolute;
top: 50%;
right: 6%;
width: 20px;
height: 20px;
border: none;
background-color: transparent;
}
HTML
$("body").on('click','.toggle-password',function(){
var $pass1 = document.getElementById("id_password1");
var $pass2 = document.getElementById("id_password2");
var toggle = $(this).toggleClass("fa-eye fa-eye-slash");
[$pass1, $pass2].forEach(function($passwords) {
if ($passwords.type === "password") {
$passwords.type = "text";
toggle.toggleClass()
} else {
$passwords.type = "password";
}
})
});
Still figuring out how to make both icons toggle at the same time. Now they doing it seperately.

Html: change image by clicking on the same image

guys, I want to choose image by clicking on the current image.
This is my code. url contains default image. I want to change my current image clickable and when use click on it, he can select new image. Is this possible without <input type="file"> ?
onFileChanged(event) {
if (event.target.files && event.target.files[0]){
var reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.onload = (event) => { //
this.url = event.target.result;
}
}
}
<div class="col-md-3">
<img [src]="url" style="bordered:5px; double-black; border-radius: 8px; max-height: 200px; max-width: 390px; border: 2px solid #ddd;">
</div>
you need a hidden file input field
<input type="file" hidden (change)="onFileChanged($event)" #file>
<img [src]="url" *ngIf="url" (click)="file.click()" width="200" />
for working example check stackblitz

Flicker with ngMessages and ngShow

On a form with required-validation and ng-messages I use ng-show to hide the directive on startup and only show error messages after the input got ng-dirty.
To still keep the element filling it's space in the layout I have following css rule to overwrite the default ng-hide behaviour:
ng-messages.ng-hide:not(.ng-hide-animate), [ng-messages].ng-hide:not(.ng-hide-animate)
{
display: block !important;
visibility: hidden;
}
When I now enter text in the input field the error message is shortly visible before it is then hidden again (due to the required field being filled). It somehow feels like ng-dirty is resolved before the form validation is done, resulting in this behaviour.
See this Fiddle
or check out the
Code:
var $scope;
var app = angular.module('myapp', ['ngMessages', 'ngAnimate']);
app.controller('UserCtrl', ['$scope', UserCtrl]);
function UserCtrl($scope) {
$scope.showField = true;
$scope.reset = function() {
var master = { name: '' };
$scope.temp = angular.copy(master);
$scope.user_form.$setPristine();
}
}
ng-messages.ng-hide:not(.ng-hide-animate), [ng-messages].ng-hide:not(.ng-hide-animate)
{
display: block !important;
visibility: hidden;
}
ng-messages, [ng-messages]
{
display: block;
height: 1em;
}
input
{
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular-animate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-messages/1.5.5/angular-messages.min.js"></script>
<div ng-app="myapp">
<div ng-controller="UserCtrl">
<form name="user_form" novalidate>
<input name="name" ng-model="temp.name" ng-show="showField" placeholder="Name" required autocomplete="off"/>
<ng-messages ng-show="user_form.name.$dirty" for="user_form.name.$error">
<ng-message when="required">
Please enter your name
</ng-message>
</ng-messages>
<button type="button" class="button" ng-click="reset()">Reset</button>
</form>
<p>
Pristine: {{user_form.$pristine}}
</p>
<pre>Errors: {{user_form.$error | json}}</pre>
</div>
</div>
<ng-messages ng-show="user_form.name.$dirty && !user_form.name.$valid" for="user_form.name.$error">