Angular 5 issue with contenteditable and input in firefox and safari - html

So I'm creating a table with content-editable cells, I need at the same time to update the visual and the typescript code, unfortunately all alternatives I found didn't work, here is a plunker with identical issue:
https://plnkr.co/edit/PCXNFSUqiHrYedx4E4mW?p=preview
import {Component, NgModule, VERSION} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
#Component({
selector: 'my-app',
template: `
<div contenteditable='true'
(input)='name=$event.target.innerText'
[(innerText)]='name'>
name
</div>
<br>
<div contenteditable='true'
(input)='name=$event.target.innerHtml'
[(innerHtml)]='name'>
name
</div>
<br>
<div contenteditable='true'
(input)='name=$event.target.textContent'
[(textContent)]='name'>
name
</div>
`,
})
export class App {
name:string;
constructor() {
this.name = `Angular! v${VERSION.full}`
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
It works well with google chrome and opera, safari and mozilla wrights the text backwards with textContent and innerText, and the value become undefined with innerHtml.
#Update
For my issue I end up using input-field inside the cells
<td [class.active]="selectedCell.row === rowIndex && selectedCell.colunm === 3"
(click)="setSelectedCell(rowIndex, 3, true)"
class="lassoer-info">
<input [id]="'entity' + rowIndex"
(keydown)="onkeyDown($event)"
[(ngModel)]="rowData.entity"
[disabled]="rowData.inscriptionId || !canEdit"
type="text"
class="input-field">
</td>

I'm just starting with Angular but I think I can spot one mistake. In Javascript it should be innerHTML instead of innerHtml (HTML in upper case).

Related

What is the purpose of <app-control-message> in angular 8 and how it is used?

I need to create a button for uploading a file in my form. I am using tag in my html but it is throwing an error : Can't bind to 'control' since it isn't a known property of 'app-contorl-message'.
Here is my html code -
<div class="col-lg-6">
<div class="form-group">
<div class="custom-file">
<input *ngIf="mode == 'CREATE'" type="file" (change)="onFileSelect($event)"
accept=".jpg,.jpeg,.png"/>
<app-contorl-message [control]="form.get('file')"></app-contorl-message>
</div>
</div>
</div>
here is def of onSelect($event) :
onFileSelect(event) {
this.form.get('file').setValue(event.srcElement.files);
this.fileData = event.srcElement.files;
console.log(event);
}
Thanks in advnace!
In your AppControlMessageComponent you need to create an #Input() named control. To learn more about inputs and output, visit: https://angular.io/guide/inputs-outputs
app-control-message.component.ts
import { Component, OnInit, Input } from "#angular/core";
#Component({
selector: "app-app-control-message",
templateUrl: "./app-control-message.component.html",
styleUrls: ["./app-control-message.component.css"]
})
export class AppControlMessageComponent implements OnInit {
#Input() control; // Input whose value parent component will provide
constructor() {}
ngOnInit() {}
}

Insert HTML into string

Using Angular 5 and Firebase, I have created a review website that allows users to create reviews on the CreateComponent and read them on the ReviewComponent. I'd like the body of the review to be able to contain HTML for links or images.
The ReviewComponent pulls the body of the review with:
<p style="margin-bottom: 2rem; white-space: pre-wrap;">{{review.body}}</p>
I am using pre-wrap to maintain the body's line breaks.
An example input from the CreateComponent would be:
I already can’t wait to see what comes next.
When I inspect the output of the body I see this:
<p _ngcontent-c1 style="margin-bottom: 2rem; white-space: pre-wrap;">I already can’t wait to see <a href="http://www.ign.com/articles/2017/11/30/marvels-the-avengers-infinity-war-trailer-breakdown">what comes next.</a></p>.
How can I change the HTML to enable the string to work correctly with links and images?
Binding review.body to [innerHTML] corrected this.
<p style="margin-bottom: 2rem; white-space: pre-wrap;">{{review.body}}</p>
is now
<p style="margin-bottom: 2rem; white-space: pre-wrap;" [innerHTML]="review.body"></p>
Here is solutions for your issue:
https://plnkr.co/edit/VHvVpvnTeoGWsA0d3eex?p=preview
Here is code:
//our root app component
import {Component, NgModule, VERSION, Pipe, PipeTransform, ChangeDetectorRef } from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
import { FormsModule } from '#angular/forms';
#Component({
selector: 'my-app',
template: `
<div [outerHTML]='selectedValue | html'></div>
`,
})
export class App {
selectedValue: string = 'I already can’t wait to see what comes next.';
constructor(private cd: ChangeDetectorRef) {
}
}
#Pipe({
name: 'html'
})
export class HtmlPipe implements PipeTransform {
transform(value: string) {
return `<div>${value}</div>`;
}
}
#NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ App, HtmlPipe ],
bootstrap: [ App ]
})
export class AppModule {}
You could write pipe for this application, like in this article:
How to allow html in return of angular2 pipe?

Hide elements using Typescript

I'm new to Typescript and Angular Material. I want to hide elements inside elements like this.
<div id="abc">
<div id="123">
<p>Hello!</p>
</div>
<p>World</p>
</div>
<div id="def">
<p>Hello World</p>
</div>
I want to hide div block(id:123).I tried in this way.
var formElement = <HTMLFormElement>document.getElementById('123');
formElement.style.display='block';
It gets an error saying Cannot read property 'style' of null.... How can I solve this problem.
This is not the way to hide elements in Angular. Bind your element's style attribute to a boolean, like this:
<form [style.display]="isVisible ? 'block' : 'none'">... contents</form>
And in your component class:
this.isVisible = false; // whenever you need to hide an element
Or you can use *ngIf:
<form *ngIf="isVisible">... contents</form>
Please, note that *ngIf removes the node and its children completely from the DOM tree completely if the conditions turns to false, and fully recreates them from scratch when the condition turns true.
You can access the dom element using ViewChild with #localvariable as shown here,
import {Component, NgModule,ViewChild,ElementRef} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
#Component({
selector: 'my-app',
template: `
<div id="abc">
<div #id id="123">
<p>Hide!</p>
</div>
<p>World</p>
</div>
<div id="def">
<p>Hello World</p>
</div>
`,
})
export class App {
name:string;
#ViewChild('id') el:ElementRef;
constructor() {
this.name = `Angular! v${VERSION.full}`
}
ngAfterViewInit()
{
this.el.nativeElement.style.display='none';
console.log(this.el.nativeElement);
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
#Component({
selector: 'my-app',
template: `
<div #id>
<p>Hide This!</p>
</div>
<div>
<p>Hello World</p>
</div>
`,
})
export class AppComponent {
#ViewChild('id') id:ElementRef;
constructor() {}
ngOnInit()
{
this.id.nativeElement.hidden=true;
}
}
The simplest way to hide elements using DOM is
document.getElementById('123').hidden = true;
in typescript.

New Line not working properly in Angular 2 editable div

I have a contenteditable div that works great until I assign the changed text back into initial variable. When editing and I press enter, a new line appears properly in the editable div, but does not get stored into 'name'. The next character typed ends up at the end of the previous line, and the cursor goes to the beginning of the first line. If I remove the (input) line, the div behaves properly, but 'name' is not updated. How do I get the div to behave properly and update 'name'?
Plunk Demo
import {Component, NgModule} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
#Component({
selector: 'my-app',
template: `
<div style="white-space: pre-wrap;"
[textContent]=name
(input)="name=$event.target.textContent"
contenteditable>
</div>
<div style="white-space: pre-wrap;">{{name}}</div>
`,
})
export class App {
name:string;
constructor() {
this.name = `TEST`
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
Expected Result:
Test
Test
Actual Result:
estTestT
With some little changes you can make it work.
Change textContent attribute to innerText.
Set the the innerText value value in ngOnInit and forgo ngModel.
Typescript:
#ViewChild('myEditable') private myEditable;
name:string;
constructor() {
this.name = 'I am editable';
}
ngOnInit(){
this.myEditable.nativeElement.innerText = this.name;
}
HTML:
<div #myEditable contentEditable (input)="name=$event.target.innerText"></div>
<div style="white-space:pre">{{name}}</div>
Demo

Angular 2 Component property shadowing

I have declared one property called foobar in the Angular 2 Foobar component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-foobar',
templateUrl: './foobar.component.html',
styleUrls: ['./foobar.component.css']
})
export class FoobarComponent implements OnInit {
foobar= 'test';
ngOnInit() {
}
}
then in foobar.component.html file I output that property like this:
<div>
<br>
{{foobar}}
</div>
and it outputs word test, however, once I have added element with id foobar:
<div>
<br>
{{foobar}}
<br>
<input type="text" #foobar>
</div>
I'm no longer able to get value from the component and it outputs [object HTMLInputElement], because it was shadowed by the input field id.
How can I get value from the component without changing component property name and input field id?
You can try this
<div>
<br>
{{ this['foobar'] }}
<br>
<input type="text" #foobar>
</div>
but it is not documented and may break in future releases
Try with two-way databinding:
<div>
<br />
{{foobar}}
<br />
<input type="text" [(ngModel)]="foobar">
</div>`
To accomplish that you need the module: #angular/forms in your package.json and don´t forget to import it to your app-module:
import {FormsModule} from "#angular/forms";
#NgModule({
declarations: [ ... ],
imports: [
FormsModule,
...
]
...
})
export class AppModule{ ... }