I need to add a button with the Material Angular 2 style. So I have a button that does this:
<button mat-button (click)="addElement($event, 'button')">Add button</button>
Where addElement is defined:
addElement(ev, tag) {
const htmlElem = ev.currentTarget;
const el = this.renderer.createElement(tag);
this.renderer.setAttribute(el, 'mat-button', '');
el.textContent = 'New Button';
htmlElem.parentNode.insertBefore(el, null);
}
Clicking on the button then creates a new element in my HTML, shown as:
<button mat-button">Add button</button>
The function code correctly generates the button, however it doesn't apply all children as shown in the original button code from the material code
So do I have a way to "refresh" the button so that the everything from the standard Mat-button applies to the button added via the JS code?
What went wrong?
You are right that you have added the mat-button attribute but since you are creating a button dynamically you did not be able to achieve to generate the whole structure of mat-button.
Below is the whole style and structure of mat-button
<button class="mat-button" mat-button="">
<span class="mat-button-wrapper">Add button</span>
<div class="mat-button-ripple mat-ripple"></div>
<div class="mat-button-focus-overlay"></div>
</button>
As you can see there are other html elements in the mat-button which includes ripple effects and focus overlay.
The Solution to your question
We are going to use Angular Renderer to create html element, set the attribute to the element and to append it on an element.
So what we did
create the button that will trigger the append
<button id="clickBtn" (click)="onClick()">Click here to add Button</button>
import the import Directive, ElementRef, Renderer2 in the component.
{ Component, Directive, ElementRef, Renderer2 } from '#angular/core';
add a directive that will target the html element where the button will get appended (#clickBtn [is the id tag of the button that we created)
#Directive({
selector: '#clickBtn'
})
create a constructor to inject renderer and elementref
constructor(private renderer: Renderer2,private elRef: ElementRef) {
}
trigger the click event to append the button
onClick() {
const btn = this.renderer.createElement('button');
const span = this.renderer.createElement('span');
const div1 = this.renderer.createElement('div');
const div2 = this.renderer.createElement('div');
const text = this.renderer.createText('I am a Generated Button');
const attrBtn = this.renderer.setAttribute(btn, 'class', 'mat-button');
const attrSpan = this.renderer.setAttribute(span, 'class', 'mat-button-wrapper');
const attrDiv1 = this.renderer.setAttribute(div1, 'class', 'mat-button-ripple mat-ripple');
const attrDiv2 = this.renderer.setAttribute(div2, 'class', 'mat-button-focus-overlay');
this.renderer.appendChild(span, text);
this.renderer.appendChild(btn, span);
this.renderer.appendChild(btn, div1);
this.renderer.appendChild(btn, div2);
this.renderer.appendChild(this.elRef.nativeElement, btn);
}
Woah so what going on here. as you can see we generate here all the structure of the mat-button
To know more about Renderer2 Please visit this link.
https://alligator.io/angular/using-renderer2/
Please see the link of live code on stackblitz
https://stackblitz.com/edit/dmgrave-ng-so-answer-dom?file=app%2Fapp.component.ts
Hope this helps.
I'm not sure why you want to generate the buttons dynamically but I would do it something like this
In your template:
<button mat-raised-button (click)="addNewButton()">Add new </button>
<div *ngFor="let btn of buttonsList">
<button mat-raised-button color="primary">
{{btn+1}}
</button>
</div>
In your component:
export class AppComponent {
buttonsList = [];
addNewButton():void{
const newId = this.buttonsList.length;
this.buttonsList.push(newId);
}
}
You just have to loop through your array and display the buttons. Every time you add a new button, you just have to push a new value to your array and let the framework do the heavy lifting.
Here's the stackblitz demo : https://stackblitz.com/edit/angular-7rfwrx?file=app%2Fapp.component.ts
Hope this helps.
Related
I create a stimulus controller and use on a div element.
Now when change innerHTML of this div element with click on a button, text change but color not change to red and connect() not work.
<div data-controller="test">
<span>This text color change to red</span>
<button type="button" data-action="test#changeColor">click</button>
</div>
test_controller.js
...
connect() {
this.element.style.color = "red";
}
changeColor(event) {
this.element.innerHTML = "<span>HelloWorld!</span>"
}
...
Two issues
You need to target the span, as this.element will be the element with the data-controller on it. See https://stimulus.hotwired.dev/reference/targets
You will need to set the target's color in the connect method AND the controller's changeColor method for it to change when the element connects (DOM loads) and when the button is clicked. See https://stimulus.hotwired.dev/reference/lifecycle-callbacks
import { Controller } from '#hotwired/stimulus';
export default class extends Controller {
static targets = ['label'];
connect() {
this.changeColor();
}
changeColor() {
const element = this.labelTarget;
element.innerText = 'HelloWorld!';
element.style.color = 'red';
}
}
<div data-controller="test">
<span data-test-target="label">This text color change to red</span>
<button type="button" data-action="test#changeColor">click</button>
</div>
I have a website on which I have crisp chat service running and I'm trying to click on the chat box from one of my component's typescript files.
The crisp chat box is a div with the id of crisp-client inside body tag. How do I click on it from inside my typescript?
Inject Renderer2 in constructor -
constructor(private renderer: Renderer2) {}
Get the crisp-client element in your component -
let crispClient = this.renderer.selectRootElement('#crisp-client');
Perform click() event -
crispClient.click();
Working demo here
Added a test method on div to check -
<div id="crisp-client" onClick="console.log('crisp client clicked')"></div>
You can use the ViewChild decorator and pass a template reference variable as a string, and manipulate the event. Here's a simple example of clicking Button 1, which programmatically clicks Button 2 and due to that click, the text of Button 2 is changed:
<button #button1 (click)="clickButton2()">
Click to change the text of Button 2
</button>
<button #button2 (click)="changeText()">{{ buttonText }}</button>
buttonText = 'Button 2';
#ViewChild('button2') button2: ElementRef<HTMLElement>;
clickButton2() {
let el: HTMLElement = this.button2.nativeElement;
el.click();
}
changeText() {
this.buttonText="Changed!"
}
Here's a Stackblitz demo
UPDATE
You can access elements of body using document object in Angular. This way, you can achieve the click of the element which is inside body. Here's one more example where I am clicking the button which is inside body from a component.
<button (click)="handleClick()">Programatically click the button inside body</button>
ngOnInit() {
let btn = document.getElementById('main');
btn.addEventListener('click', (e: Event) => btn.innerHTML = "Clicked!");
}
handleClick() {
document.getElementById('main').click();
}
index.html
<html>
<body>
<my-app>loading</my-app>
<button id="main">Click</button>
</body>
</html>
Here's a Stackblitz demo.
I want to show a modal when I click on and icon. The modal template is inside a template string in my js.
html
<i id="settings-icon" class="fa fa-cog" onClick='attachModal()'></i>
javascript
const attachModal = () => {
const modal = `
<div class='modal>
Modal
</div>
`;
document.body.insertAdjacentHTML('beforeend', modal);
};
There is not error messages however I don't see the modal code in the dom after I click
I'd recommend using [innerHTML][1] and [node.remove()][1] for this since the modal is only either supposed to be visible or hidden. You can append the modal code to the html by using document.body.innerHTML += modal. Using += will concatenate the new HTML to the existing one without replacing/removing any of the existing HTML.
If isModal is true (meaning the modal is already visible) then use isModal.remove() which is a method that a child node can use to remove itself from it's parent (in this case body).
By adding the logic that both hides or shows the modal conditionally, you can reuse this function in what I assume will be a close or cancel button within the modal.
*Note: I use a p tag instead of an i tag for demonstrative purposes. The p tag is not crucial to the solution.
const attachModal = () => {
var isModal = document.getElementById('modal')
const modal = `<div id="modal">
MODAL
</div>`
if (isModal){
isModal.remove()
} else {
document.body.innerHTML += modal
}
};
<p id="settings-icon" class="fa fa-cog" onClick='attachModal()'>test</p>
I have 'DIV' element, and when I click on the button, I want get the same 'DIV' on the page (including the content, the style and the images it has).
that is the code I tryed:
HTML:
<div id="taskId">
<p id="taskWritten">
the task will written here
</p>
</div>
<button id="button">add item</button>
JavaScript:
let button = document.querySelector('#button');
button.addEventListener('click', ()=>{
let task = document.querySelector('#taskId');
let newTask = document.createElement(task);
document.appendChild(newTask);
})
this code doesn't work.
Can anyone explain to me where my error is?
(I am an absolute beginner with programming)
You are on the right track, but need to only make minor changes. The code should look like this.
let task = document.querySelector('#taskId');
const newTask = task.cloneNode(true);
document.appendChild(newTask);
here you are creating a new node which will model your original node entirely including the style and data
You must create a 'div' with 'createElement'. You can use the code below with some adjustments.
let button = document.querySelector('#button');
button.addEventListener('click', ()=>{
task=document.querySelector('#taskId');
let newTask = document.createElement('div');
newTask.innerHTML='<div><p>the task will written here</p></div>'
task.appendChild(newTask);
})
<div id="taskId">
<p id="taskWritten">
the task will written here
</p>
</div>
<button id="button">add item</button>
Using Axios, I'm pulling in a static HTML file. This part is working
The user clicks on an edit button and I'm going through that static HTML and adding a new class if an existing class exists.
If that existing class exists, I want to add a new button with v-on in this static HTML template and re-render the content with this new button in the HTML which then spawns an overlay.
Is there anyway that I can add this new button in my code so that view re-renders and uses the Vue v-on directive?
Here is my code:
VIEW:
<template>
<div>
<div class="row">
<div id="kbViewer">
<b-button
class="request-edit"
#click="letsEditThisStuff({currentUrl: currentUrl})">Request An Edit</b-button>
<div v-html="htmlData">
{{ htmlData }}
</div>
</div>
</div>
</div>
</template>
data: function () {
return {
sampleElement: '<button v-on="click: test()">test from sample element</button>',
htmlData: '',
};
},
methods: {
pullView: function (html) {
this.axios.get('../someurl/' + html).then(response => {
let corsHTML = response.data;
let htmlDoc = (new DOMParser()).parseFromString(corsHTML, "text/html");
this.rawDog = htmlDoc;
this.htmlData = htmlDoc.documentElement.getElementsByTagName('body')[0].innerHTML;
})
},
letsEditThisStuff(item) {
let htmlDoDa = this.htmlData;
// This doesn't work - I'm trying to loop over the code and find all
// of the class that are .editable and then add a class name of 'editing'
// to that new class. It works with #document of course...
for (const element of this.htmlData.querySelectorAll('.editable')) {
element.classList.add('editing');
// Now what I want to do here is add that sampleElement from above - or however -
// to this htmlData and then re-render it.
let textnode = document.createElement(sampleElement);
textnode.classList.add('request-the-edit')
textnode.innerHTML = 'edit me!'
element.append('<button v-on="click: test()">test from sample element</button>')
console.log('what is the element?', element)
}
this.htmlData = htmlDoDa
},
}
I know that some of my variables are not defined above - I'm only looking at a solution that helps with this - basically take that stored data.htmlData, parse through it - find the classes with "editable" and append a button with a v-for directive to that specific node with "editable" ... Unfortunately, the HTML already exists and now I've got to find a slick way to re-parse that HTML and re-append it to the Vue template.
I found Vue Runtime Template and it works PERFECTLY!
https://alligator.io/vuejs/v-runtime-template/