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{
// }
Related
So I am making an HTML sidebar in Google Sheets using Apps Script. I am also using the Skeleton CSS framework.
So I'd like the submit button here to be centered:
I've tried this: making an align class in CSS, and then applying it to the button. I forgot to mention that all my other elements are aligned except the buttons.
<style>
body{
background-color: white;
color: black;
}
.align{
text-align: center;
}
.margin{
margin-bottom: 10px;
}
h1{
font-size: 20pt;
}
h2{
font-size: 16pt;
}
</style>
Here is my HTML code:
<body>
<h1 class = "align ">Welcome to the clothing expense custom menu bar!</h1>
<h2 class = "align">Here are the custom functions you can use:</h2>
<p class = "align"> See how much you've spent on a brand.</p>
<form onsubmit="runFunc()">
<input class = "u-full-width " id="brand" type = "text" placeholder="Enter brand name">
<div>
<button type="submit" class="button-primary align">Submit</button>
</div>
</form>
<p class = "align"> See how much you've spent on a type of clothing.</p>
<form onsubmit="runFuncTwo()">
<input class = "margin u-full-width" id="type" type = "text" placeholder="Enter clothing brand">
<div>
<button type="submit" class="button-primary align">Submit</button>
</div>
</form>
</body>
Thanks!
All of these 3 solutions should work, I would choose the second or third one.
If you make the div full-width and add align to it, it should work
<div class="u-full-width align">
<button type="submit" class="button-primary">Submit</button>
</div>
You can also make the div a flex, like so (Use classes instead of inline style)
<div class="u-full-width" style="display:flex; justify-content: center">
<button type="submit" class="button-primary">Submit</button>
</div>
You can also add margin:auto and float:none to the button (Use classes instead of inline style)
<button type="submit" class="button-primary"
style="margin:auto; float:none">Submit</button>
The code is:
button.button-primary.align {
width: 100%;
max-width: 150px;
margin: 0 auto;
display: block;
}
And here it is in action:
body{
background-color: white;
color: black;
}
.align{
text-align: center;
}
.margin{
margin-bottom: 10px;
}
h1{
font-size: 20pt;
}
h2{
font-size: 16pt;
}
/*new code from here*/
button.button-primary.align {
width: 100%;
max-width: 150px;
margin: 0 auto;
display: block;
}
<body>
<h1 class = "align ">Welcome to the clothing expense custom menu bar!</h1>
<h2 class = "align">Here are the custom functions you can use:</h2>
<p class = "align"> See how much you've spent on a brand.</p>
<form onsubmit="runFunc()">
<input class = "u-full-width " id="brand" type = "text" placeholder="Enter brand name">
<div>
<button type="submit" class="button-primary align">Submit</button>
</div>
</form>
<p class = "align"> See how much you've spent on a type of clothing.</p>
<form onsubmit="runFuncTwo()">
<input class = "margin u-full-width" id="type" type = "text" placeholder="Enter clothing brand">
<div>
<button type="submit" class="button-primary align">Submit</button>
</div>
</form>
</body>
css:
h1{
font-size: 20pt;
width:100%;
}
h2{
font-size: 16pt;
width:100%;
}
html:add all text content within span tag:
<h1 class = "align "><span>Welcome to the clothing expense custom menu bar! </span></h1>
I created a form that contains a textbox. If you try to submit the form without filling in the textbox, a validation message is displayed.
If the textbox is filled, I intend to clean all its content when submitting the form. My problem is that I can clean the textbox after submitting without the validation message appearing, however, when I switch to another Tab and then return to it, that validation message is displayed. How can I solve this?
PROBLEM
DEMO
html
<div class="container">
<div class="tab-slider--nav">
<ul class="tab-slider--tabs">
<li class="tab-slider--trigger" [class.active]="viewMode == 'tab1'" rel="tab1" (click)="viewMode ='tab1'">
Tab 1 - list</li>
<li class="tab-slider--trigger" [class.active]="viewMode == 'tab2'" rel="tab2" (click)="viewMode ='tab2'">
Tab 2 - Create</li>
</ul>
</div>
<div class="tab-slider--container" [ngSwitch]="viewMode">
<div id="tab1" class="tab-slider--body" *ngSwitchCase="'tab1'">
LIST
</div>
<div id="tab2" class="tab-slider--body" *ngSwitchCase="'tab2'">
<form (submit)="SaveGroup($event)" style="width: 100%; height: 92%;">
<dx-validation-group #CreateGroup>
<div style="width: 100%; height: 100%; overflow: auto;">
<div style="
display: flex;
align-items: center;
flex-direction: column;
width: 100%;
">
<span class="title1">Name</span>
<div class="mytextbox">
<dx-text-box [(ngModel)]="Name" [ngModelOptions]="{standalone: true}"
placeholder="Enter Name">
<dx-validator>
<dxi-validation-rule type="required" message="Name is required">
</dxi-validation-rule>
</dx-validator>
</dx-text-box>
</div>
</div>
</div>
<div style="padding-top: 10px;">
<dx-button class="customButtom" type="submit" id="button" text="Save"
[useSubmitBehavior]="true">
</dx-button>
</div>
</dx-validation-group>
</form>
</div>
</div>
</div>
.TS
SaveGroup(e) {
let self = this;
self.viewMode = 'tab1';
self.validationGroup1.instance.reset();
}
If you comment this line self.viewMode = 'tab1'; (do not change the tab after submitting) the textbox is successfully cleared. If you change tab and then return to the one with the textbox, the validation message is always displayed.
It seems that field is in "touched mode" because the value is stored in two way binding in Name.
I have tried this :
SaveGroup(e) {
const savedNameValue = this.Name;
this.validationGroup1.instance.reset();
this.Name = null;
this.viewMode = 'tab1';
}
Let's assume that I have Angular 5 project with routings. For instance /home, /landing-page, etc. Moreover, let's assume that in my landing page with url - localhost:4200. I want to create login panel. I have two fields - username and password, one button sign in and two other buttons forgot password? and Don't have an account?. My problem is that when user will click Forgot password? or Don't have an account? he will not be routed to another page with url like localhost:4200/sign-up but he will stay at the same page with url localhost:4200 and fields username, password, sign in, forgot password? and Don't have an account? will disappear and in their place will be displayed fields associated with registration. I am not sure whether you know what I mean. The good example what I wanna to achieve is https://www.instagram.com. No matter whether you click Sign up or Log in you are still on the same url and only one component changes. Do you know how can I achieve this? I am not sure whether I should use routes or maybe another way is more optimal to do this? Thanks in advance.
My code looks in this way. I added only the most important code from selected files.
index.html:
</head>
<body>
<app-root></app-root>
</body>
</html>
app.component.html:
<app-navbar></app-navbar>
<router-outlet></router-outlet>
<app-footer *ngIf="removeFooter()"></app-footer>
At the moment my home.component looks in this way:
home.component.html:
<div *ngIf="isSignIn()">
<app-sign-in></app-sign-in>
</div>
<div *ngIf="isSignUp()">
<app-sign-up></app-sign-up>
</div>
<div *ngIf="isForgotPassword()">
<app-forgot-password></app-forgot-password>
</div>
home.component.ts:
constructor() {
this.signin = true;
this.signup = false;
this.forgot = false;
}
isSignUp() {
if (this.signup === true) {
return true;
}
else {
return false;
}
}
isSignIn() {
if (this.signin === true) {
return true;
}
else {
return false;
}
}
isForgotPassword() {
if (this.forgot === true) {
return true;
}
else {
return false;
}
}
sign-in.component.html:
<div class="content-center">
<div class="container">
<div class="title-brand">
<div align="center">
<input style="background-color: black; border-color: white; color:white; width: 270px" type="text" value="" class="form-control" placeholder="USERNAME">
<br>
<input style="background-color: black; border-color: white; color:white; width: 270px" type="text" value="" class="form-control" placeholder="PASSWORD">
<div class="row">
<div class="col-md-8">
<br>
<button style="background-color: black; border-color: white; color:white; width: 270px" type="button" class="btn btn-danger">Log in</button>
</div>
</div>
<br>
<h6 style = "color: white" [routerLink]="['/main']" skipLocationChange class=pointer>Forgot Password?</h6>
<h6 style="color: white" [routerLink]="['/sign-up']" class=pointer>Don't have an account?</h6>
</div>
</div>
</div>
</div>
UPDATE
I added source code of sign-in.component.html to the question. Can you show me how I can switch the component sign-in.component.html after clicking Forgot Password? or Do not have an account? to the component forgot.component.html or sign-up.component.html
Use skipLocationChange from the route NavigationExtras. E.g.
<h6 style = "color: white" [routerLink]="['/main']" skipLocationChange>Forgot Password?</h6>
<h6 style="color: white" [routerLink]="['/sign-up']" skipLocationChange>Don't have an account?</h6>
Here is an example of using a boolean and buttons to allow you to switch between different components on a single page:
stackblitz example
This example could be improved but I hope it shows you how to easily swap the visible component.
You can view the code at this link
EDIT
You need to remove the responsibility of what component to show to a container component (i.e. a parent component). In the updated stackblitz example I've made the HomeComponent responsible for showing the correct component. This means the SignUp/SignIn/ForgotPassword components have no responsibility for switching between each other - that is the job for the HomeComponent (or whichever component you want to use for that job).
Hope that helps
home.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-home',
template:
`<div *ngIf="signin">
<app-sign-in></app-sign-in>
<button (click)="showSignUp()">Sign up</button>
<button (click)="showForgot()">Forgot password</button>
</div>
<div *ngIf="signup">
<app-sign-up></app-sign-up>
<button (click)="showSignIn()">Sign in</button>
<button (click)="showForgot()">Forgot password</button>
</div>
<div *ngIf="forgot">
<app-forgot-password></app-forgot-password>
<button (click)="showSignUp()">Sign up</button>
<button (click)="showSignIn()">Sign in</button>
</div>`,
styles: [`h1 { font-family: Lato; }`]
})
export class HomeComponent {
public signup: boolean = true;
public signin: boolean = false;
public forgot: boolean = false;
constructor() {}
public showSignIn() {
this.signin = true;
this.signup = this.forgot = false;
}
public showSignUp() {
this.signup = true;
this.signin = this.forgot = false;
}
public showForgot() {
this.forgot = true;
this.signin = this.signup = false;
}
}
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;
}
`]
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">