Changing size of a textbox in html string in React - html

I'm using a popup library sweetalert2-react-content to generate a popup box which contains a textbox and a textarea. In the popupbox I am generating an html string to create the textbox and text area. It's generating those elements just fine, however I am unable to change the size of the textarea. Here's a code snippet:
import React, { Component } from 'react';
import Swal from 'sweetalert2';
import withReactContent from 'sweetalert2-react-content';
submitButton() {
const MySwal = withReactContent(Swal)
MySwal.fire({
confirmButtonText: 'Submit Post',
showCancelButton: true,
title: "Submit a new Post",
html:
'<br /><br />' +
'<h4>Enter the title</h4>' +
'<input type="text" id="theTitle">' +
'<h4>Enter the body</h4>' +
'<textarea rows="10" cols="50"></textarea>',
input: 'file',
focusConfirm: false,
}).then(result => this.getPostValues(result.value))
}
Any ideas how I might be able to get the textbox to display a little larger? Thanks!!

The <textarea> is likely controlled via the library's css so even if you set rows and cols, the css dictates styling. If so, you'll have to either edit the library's css, override it with your own css, or give it an inline style.
MySwal.fire({
// Other settings...
html:
'...other html' +
'<textarea style="height: 200px; width: 400px"></textarea>'
});

Related

how can I use useRef to treat the div as textarea

I have a long message saved as html format. I want to show this message to the screen without Html element as textarea input.
message = <p>Mobil &auml ........ </p>
Before I upgrade React version to V6 it was working fine as the code below.
I could scroll down and adjust the textarea box size to see the message inside the box.
<div
id="textarea"
name="message"
className="form-control"
dangerouslySetInnerHTML={{__html: this.state.message }}
ref="textarea"
/>
after updating to React V6, when I write exactly the same code, it gives me an error saying
"Function components cannot have string refs. We recommend using useRef() instead. Learn more about using refs safely here".
My first approach was to just simply delete ref="textarea" but then the message is overflow from the box and cannot read other information below.
And my second approach is to use useRef() but im not really understanding how to incorporate it to my code.
any suggestion here plz.
First option:
import { useEffect, useRef } from 'react';
function Teste() {
const divElement = useRef<HTMLDivElement>(null);
useEffect(() => {
if (divElement.current) {
divElement.current.appendChild(document.createElement('textarea')).value = 'Hello World';
}
});
return <div ref={divElement} />;
}
export default Teste;
Second option:
https://www.radix-ui.com/docs/primitives/utilities/slot

Angular/Typescript Text with routerLink

Updated Question for more Clarity:
Need to display some texts and links as innerHTML(data from service/DB) in the Angular HTML and when user clicks, it should go to Typescript and programmatically navigates by router.navigate
Also, How to add DomSanitizer from #ViewChild/ElementRef
Added all example in below code
Here is the updated stackblitz code
As shown in screenshot from angular.io some texts and some links
Sorry, I didn't realize you answered my comment. Angular routing is not secondary, if you don't use Angular modules you'll end up with just an HTML/CSS/Typescript application. you need at least the RouterModule for Angular to be able to use routing and hence, do what it's supposed to with the DOM.
First:
You are not importing RouterModule
solution:
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([]) // this one
]
Second:
You can't bind Angular events through innerHTML property
fix:
Make use of #ViewChild directive to change your innerHTML property and manually bind to the click event, so change in your app.component.html from
<div id="box" [innerHTML]="shouldbedivcontent" ></div>
to
<div #box id="box"></div>
Now, in your app.component.ts, add a property to hold a reference to that "box" element so you can later make some changes to the dom with it:
#ViewChild('box') container: ElementRef;
Implement AfterViewInit, that hook is where you will be able to actually handle your container, if you try using it for example in OnInit you'd get undefined because that component's html is not in the dom yet.
export class AppComponent implements AfterViewInit {
and
ngAfterViewInit() {
this.container.nativeElement.innerHTML = this.shouldbedivcontent;
this.container.nativeElement.addEventListener('click',
() => this.goto('bar')
);
}
change shouldbedivcontent property from:
'1) this is a click
<a (click)="goto("bar")">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto("bar")">Click</a><br>
3)This only works with href
bar and test'
to
'1) this is a click
<a id="link_1">Click</a><br>
2)this is with routerlink
<a [routerLink]="" (click)="goto(\'bar\')">Click</a><br>
3)This only works with href
bar and test'
And even so you'd still not get the default anchor style unless you apply some styling yourself.
Third
You are not HTML sanitizing, which could be dangerous. read more here
MY SUGGESTION:
Seems like a lot to do for you and a lot to read for someone else working alongside you for something you could easily do like in the example below!
Move your html to your app.component.html:
<div id="box">
1) this is a click
<a (click)="goto('bar')">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto('bar')">Click</a><br>
3)This only works with href
bar and test
</div>
<p>Below is actual content</p>
You'll notice that everything works now, except the anchor without routerLink or href, because that's not a link.
EDIT:
Looking at the new stackblitz, i suggest a change of approach, binding to innerHTML is ok when working with plain text or even some simple html but not a great choice to bind events or routing logic.
Angular's Renderer2 provides with a bunch of methods to dyncamically add elements to the DOM. With that on the table, you just need a little effort to take that simple html you get from your backend and turn it into something like (paste this property in your code to test it along the rest of the code provided below):
public jsonHTML = [
{
tagName: '',
text: 'some text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'bar',
attributes: {
value: 'bar' // goto parameter
}
},
{
tagName: '',
text: ' some more text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'foo',
attributes: {
value: 'foo' // goto parameter
}
}
]
Once you have it, it's way easier to create all of those elements dynamically:
this is for the code in your Q1:
Inject Renderer2 with private r2: Renderer2
And replace the Q1 related code in AfterViewInit hook to:
const parent = this.r2.createElement('div'); // container div to our stuff
this.jsonHTML.forEach((element) => {
const attributes = Object.keys(element.attributes);
const el = element.tagName && this.r2.createElement(element.tagName);
const text = this.r2.createText(element.text);
if (!el) { // when there's no tag to create we just create text directly into the div.
this.r2.appendChild(
parent,
text
);
} else { // otherwise we create it inside <a></a>
this.r2.appendChild(
el,
text
);
this.r2.appendChild(
parent,
el
);
}
if (attributes.length > 0) {
attributes.forEach((name) => {
if (el) {
this.r2.setAttribute(el, name, element.attributes[name]); // just the value attribute for now
if (name === 'value') {
this.r2.listen(el, 'click', () => {
this.goto(element.attributes[name]); // event binding with property "value" as parameter to navigate to
})
}
} else {
throw new Error('no html tag specified as element...');
}
})
}
})
this.r2.appendChild(this.container.nativeElement, parent); // div added to the DOM
No html sanitizer needed and no need to use routerLink either just inject Router and navigate to the route you want! Make improvements to the code t make it fit your needs, it should be at least a good starting point
Good Luck!
You have a css problem.
looks like a link
<a [routerLink]="something"></a> looks like a link, because if you inspect the HTML it actually gets an href property added because of routerLink
<a (click)="goTo()"></a> does NOT look like a link, because there is no href
Chrome and Safari default user agents css will not style <a> without an href (haven't confirmed Firefox but I'm sure its likely). Same thing for frameworks like bootstrap.
Updated stackblitz with CSS moved to global, not app.css
https://stackblitz.com/edit/angular-ivy-kkgmkc?embed=1&file=src/styles.css
This will style all links as the default blue, or -webkit-link if that browser supports it. It should be in your global.css file if you want it to work through the whole app.
a {
color: rgb(0, 0, 238);
color: -webkit-link;
cursor: pointer;
text-decoration: underline;
}
this works perfectly for me :D
#Directive({
selector: "[linkify]",
})
// * Apply Angular Routing behavior, PreventDefault behavior
export class CustomLinkDirective {
#Input()
appStyle: boolean = true;
constructor(
private router: Router,
private ref: ElementRef,
#Inject(PLATFORM_ID) private platformId: Object
) {}
#HostListener("click", ["$event"])
onClick(e: any) {
e.preventDefault();
const href = e.target.getAttribute("href");
href && this.router.navigate([href]);
}
ngAfterViewInit() {
if (isPlatformBrowser(this.platformId)) {
this.ref.nativeElement.querySelectorAll("a").forEach((a: HTMLElement) => {
const href = a.getAttribute("href");
href &&
this.appStyle &&
a.classList.add("text-indigo-600", "hover:text-indigo-500");
});
}
}
}
HOW I USE IT
<p linkify
class="mt-3 text-lg text-gray-500 include-link"
[innerHtml]="apiSectionText"
></p>
result

Angular 6 - bind HTML created after compilation to click event

Im adding HTML elements at run-time when the user clicks a button.
I do this by setting the inner html of a div to a built up string then using DOMSanitizer.
Visually this look fine but the click events in the new HTML are not bound so nothing works, I guess because the HTML is generated after compilation.
Here is the code called when the user clicks to add a new component (it get populated with the correct data), can anyone suggest how I should hook it up to the click event in the delete image?
html on the page:
<div class="col-sm-9" >
<div [innerHtml]="contentHtml"></div>
</div>
code:
async AddText(contentText: string) {
this.htmlToAdd = this.htmlToAdd + ( '<br> <div class="card text-left">' +
'<div class="card-header text-secondary">Attraction Text' +
'<img align="right" class="image-hover pull-right table-header-padding" src="assets/images/LockLineIcon.png" />' +
'<img #delete class="image-hover float-right text-danger icon-pad draft-icon-indent" src="assets/images/DeleteIcon.png" (click)="this.delete(0)"/>' +
'</div>' +
'<div class="card-body" >' +
'<textarea id="text" name="text" type="text" class="form-control" required maxlength="2048" >' + contentText + '</textarea>' +
'</div>' +
'<div class="card-footer">' +
'<img align="right" class="pull-right table-header-padding" src="assets/images/DragUpDownIcon.png" />' +
'</div>' +
'</div>');
this.contentHtml = this.sanitizer.bypassSecurityTrustHtml(this.htmlToAdd);
}
Your DOM may be sanitized, but it's not part of Angular's DOM. If you want Angular to see the DOM, you have to let Angular make the DOM - and that means dynamic components. Something like this would work:
#Component({
selector: 'my-component',
template: `<h2>Stuff bellow will get dynamically created and injected<h2>
<div #vc></div>`
})
export class MyComponent {
#ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
private cmpRef: ComponentRef<any>;
constructor(private compiler: Compiler,
private injector: Injector,
private moduleRef: NgModuleRef<any>,
private backendService: backendService,
) {}
ngAfterViewInit() {
// Here, get your HTML from wherever.
this.someService.getThatAsyncHTMLOfYours.subscribe(rawHTML => this.createComponentFromRaw(rawHTML));
}
// Here we create the component.
private createComponentFromRaw(template: string) {
// Let's say your template looks like `<h2><some-component [data]="data"></some-component>`
// As you see, it has an (existing) angular component `some-component` and it injects it [data]
// Now we create a new component. It has that template, and we can even give it data.
const tmpCmp = Component({ template, styles })(class {
// the class is anonymous. But it's a quite regular angular class. You could add #Inputs,
// #Outputs, inject stuff etc.
data: { some: 'data'};
ngOnInit() {
/**
* HERE'S YOUR STUFF
* do stuff here in the dynamic component, like binding to locally available variables and services.
*/
}
});

Adding HTML content to angular material $mdDialog

I have written the following piece of code to display some contents in angular material dialog box. it works fine when i add plain text to textContent . when i add HTML its displays HTML as text. how do i bind HTML to textContent
This Works
Sample Link
$scope.Modal = function () {
$mdDialog.show(
$mdDialog.alert()
.parent(angular.element(document.querySelector('body')))
.clickOutsideToClose(true)
.textContent('sample text')
.ok('Ok')
);
}
This Doesn't Works
Sample Link
$scope.Modal = function () {
$mdDialog.show(
$mdDialog.alert()
.parent(angular.element(document.querySelector('body')))
.clickOutsideToClose(true)
.textContent('<div class="test"><p>Sample text</p></div>')
.ok('Ok')
);
}
Thanks in advance
You need to append to the template,
$mdDialog.show({
parent: angular.element(document.body),
clickOutsideToClose: true,
template: '<md-dialog md-theme="mytheme">' +
' <md-dialog-content>' +
'<div class="test"><p>Sample text</p></div>' +
' <md-button ng-click="closeDialog();">Close</md-button>' +
' </md-dialog-content>' +
'</md-dialog>',
locals: {
},
controller: DialogController
});
DEMO
You can add html in template and just add variable in displayOption. This will work.
Template Code
<script type="text/ng-template" id="confirm-dialog-answer.html">
<md-dialog aria-label="confirm-dialog">
<form>
<md-dialog-content>
<div>
<h2 class="md-title">{{displayOption.title}}</h2>
<p>{{displayOption.content}} <img src="{{displayOption.fruitimg}}"/></p>
<p>{{displayOption.comment}}</p>
</div>
</md-dialog-content>
<div class="md-actions" layout="row">
<a class="md-primary-color dialog-action-btn" ng-click="cancel()">
{{displayOption.cancel}}
</a>
<a class="md-primary-color dialog-action-btn" ng-click="ok()">
{{displayOption.ok}}
</a>
</div>
</form>
</md-dialog>
</script>
Controller Code
$mdDialog.show({
controller: 'DialogController',
templateUrl: 'confirm-dialog-answer.html',
locals: {
displayOption: {
title: "OOPS !!",
content: "You have given correct answer. You earned "+$scope.lastattemptEarnCount,
comment : "Note:- "+$scope.comment,
fruitimg : "img/fruit/"+$scope.fruitname+".png",
ok: "Ok"
}
}
}).then(function () {
alert('Ok clicked');
});
Use template instead of textContent, textContent is used for show plan text in a model. It does not render HTML code
$mdDialog.show({
controller: function ($scope) {
$scope.msg = msg ? msg : 'Loading...';
},
template: 'div class="test"><p>{{msg}}</p></div>',
parent: angular.element(document.body),
clickOutsideToClose: false,
fullscreen: false
});
You can use htmlContent instead of textContent to render HTML. Heres an excerpt from the documentation available at https://material.angularjs.org/latest/#mddialog-alert
$mdDialogPreset#htmlContent(string) - Sets the alert message as HTML.
Requires ngSanitize module to be loaded. HTML is not run through
Angular's compiler.
It seems a bit counter intuitive to use a template when you only need to inject one or two things in. To avoid using a template, you need to include 'ngSanitize' for it to work.
angular.module('myApp',['ngMaterial', 'ngSanitize'])
.controller('btnTest',function($mdDialog,$scope){
var someHTML = "<font>This is a test</font>";
$scope.showConfirm = function(ev) {
// Appending dialog to document.body to cover sidenav in docs app
var confirm = $mdDialog.confirm()
.title('Please confirm the following')
.htmlContent(someHTML)
.ariaLabel('Lucky day')
.targetEvent(ev)
.ok('Please do it!')
.cancel('Sounds like a scam');
//Switch between .htmlContent and .textContent. You will see htmlContent doesn't display dialogbox, textContent does.
$mdDialog.show(confirm).then(function() {
$scope.status = 'Saving Data';
},
function() {
$scope.status = 'You decided to keep your debt.';
});
};
})
Notice the injected HTML:
var someHTML = "<font>This is a test</font>";
I found this example here.
The latest version of Angular Material Design API has predefined function for add HTML content to the alert dialog:
an $mdDialogPreset with the chainable configuration methods:
$mdDialogPreset#title(string) - Sets the alert title.
$mdDialogPreset#textContent(string) - Sets the alert message.
$mdDialogPreset#htmlContent(string) - Sets the alert message as HTML. Requires ngSanitize module to be loaded. HTML is not run through Angular's compiler.
$mdDialogPreset#ok(string) - Sets the alert "Okay" button text.
$mdDialogPreset#theme(string) - Sets the theme of the alert dialog.
$mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, the location of the click will be used as the starting point for the opening animation of the the dialog.
The link to the documentation: Angular MD API

how to get the html of a span tag in a dynamically created input tag

<span class="edt">ddddd</span>
<span class="edt">ssss</span>
<span class="edt">aaaaa</span>
<a id="edit">edit</a>
$('#edit').click(function(){
$('.edt').html("<input type=\"text\" id=\"\" value=\"\" /> ")
});
what i want to do is get the html part of span tag in input created by jquery. i knw the better solution will be to pass the html by id in variable but the span tags are dynamically created on page load.
This would loop through each edt class and replace the content of the span with and input element with same value as the content it is replacing.
$('#edit').click(function(){
$('.edt').each(function() {
var value = $(this).text();
$(this).html('<input type="text" id="" value="'+value+'" />');
});
});
If the #edit element is created dynamically you should delegate the event.
$(document).on('click', '#edit', function(){
$('span').html(function(index, oldHTML){
return '<input type="text" id="id'+ index +'" value="'+ oldHTML +'" />';
})
});
http://jsfiddle.net/yHbsa/
It seems you want to edit the element's content, some suggestions:
Focus on the input (programatically) and on blur event replace the inputs with their values.
If handler should be executed once, use one method instead of on.
If you are using HTML5 doctype you can also use contenteditable attribute instead of replacing elements.
Is this what you are trying to achieve? DEMO
Click on some input elements generated to see binded event.
$('#content').on("click", "#edit", function () {
$.each($('.edt'), function (i, k) {
$(k).removeClass("edt");
k.innerHTML = "<input type='text' id='' value='" + k.innerHTML + "' />";
});
});
$('#content').on("click", "input", function () {
alert("Some event.");
});