adding angular component dynamically to new html element - javascript

I have read about ViewContainerRef, ComponentFactoryResolver.
Basically:
put tag in your html
declare the following in your ts:
#ViewChild('parent', { read: ViewContainerRef })
parent: ViewContainerRef;
instantiate your component using:
const childComponent = this.componentFactoryResolver.resolveComponentFactory(ChildComponent );
this.parent.createComponent(childComponent);
However, it does not work on already exist in your html on your page load.
The problem is I am using a popup that is generated using google map api (the popup where it appears when you click somewhere on the map).
I need to put div parent tag after the popup appears
var infowindow = new google.maps.InfoWindow({
// content: this.contentString
content: " <div #parent></div>"
});
Therefore I tried to bind it into onclick event on the popup as the following:
marker.addListener('click', () => {
infowindow.open(this.map, marker);
const childComponent = this.componentFactoryResolver.resolveComponentFactory(CentreDetailsComponent);
this.parent.createComponent(childComponent);
});
However, it does not work as per the screenshot. Is it because of the div parent tag is not generated on page load? can someone please assist? Thx

The best Solution is. You just go for AgmCore
npm install --save #agm/core
for that you need to add core module in your ngModule like :
import { AgmCoreModule } from '#agm/core';
#NgModule({
imports: [
AgmCoreModule.forRoot({
apiKey: 'api_key_googlemap'
})
]})
and your html view file will looks like
<div style="height: 700px; width: 100%;">
<agm-map [latitude]="32.02" [longitude]="52.2" [zoom]="17">
<agm-marker [latitude]="52.2" [longitude]="32.02" title="Name" >
<agm-info-window [isOpen]="true" >
<h6>Name</h6>
</agm-info-window>
</agm-marker>
</agm-map>
</div>
if you want click function in marker
<agm-marker (markerClick)="yourClickFunction()"></agm-marker>
Hope this will resolve your problem.

Related

Inject Popper.js in Chrome Extension Content script

This is my flow: Content script -> Fetch data using background.js -> Return data and inject the new HTML into the webpage.
Now I have to show a tooltip using Popper.js but I get the error "document" is undefined.
It's strange because I am in content-scripts.js. How can I get the newly updated dom after my injection?
content-scripts.js
chrome.runtime.sendMessage({command: "sales_data", data: payload}, function(items) {
let view = new SearchResultsView(items)
view.injectToDom()
const popcorn = document.querySelector('#popcorn'); // document is undefined
const tooltip = document.querySelector('#tooltip'); // document is undefined
createPopper(popcorn, tooltip, {
placement: 'top',
});
});
SearchResultsView/InjectDom()
let html = `<div id="popcorn" aria-describedby="tooltip"></div>
<div id="tooltip" role="tooltip">
My tooltip
<div id="arrow" data-popper-arrow></div>
</div>`
//...
element.insertAdjacentHTML('afterbegin', html);
SOLVED.
I had to import tippy.js.
import { createPopper } from '#popperjs/core';
import tippy from 'tippy.js';
tippy('#button', {
content: 'My tooltip!',
});

Google Map Angular9 error with opening info window, getAnchor() is not found

I'm using this google map angular component tutorial and it's working pretty good! BUT opening up an info window throws an exception.
Here is my code that calls "this.infoWindow.open" method on a "MapInfoWindow" component from the npm package.
import {
MapInfoWindow,
MapMarker,
GoogleMap
} from '#angular/google-maps';
export class YogabandEventsComponent implements OnInit {
#ViewChild(MapInfoWindow, {
static: false
}) infoWindow: MapInfoWindow;
#ViewChild(GoogleMap, {
static: false
}) googleMap: GoogleMap;
openInfo(marker: MapMarker, content) {
this.infoContent = content;
this.infoWindow.open(marker);
}
}
<google-map [options]="options" [zoom]="zoom" [center]="center" class="h-100" height="100%" width="100%">
<map-marker #markerElem *ngFor="let marker of markers" (mapClick)="openInfo(markerElem, marker.info)" [position]="marker.position" [label]="marker.label" [title]="marker.title" [options]="marker.options">
</map-marker>
<map-info-window>{{ infoContent }}</map-info-window>
</google-map>
When
infoWindow.open(marker)
is called it enters
google-maps.js // line 1122
but receives an error on line 1122, because there is no "getAnchor()" method
this.infoWindow.open(this._googleMap.googleMap, anchor ? anchor.getAnchor() : undefined);
// in google-maps.js
open(anchor) {
this._assertInitialized();
this._elementRef.nativeElement.style.display = '';
this.infoWindow.open(this._googleMap.googleMap, anchor ? anchor.getAnchor() : undefined); // line 1122
}
I looked through the google docs and I don't see any "getAnchor" method.
Here is what I see in the debugger when setting a breakpoint in my component.
Here is what it shows in the debug console when I look at 'marker', so it has values and is instantiated!
I can copy and paste the whole thing but it's long.
Here is another pic of debug console, inside google-maps.js file, trying to call the getAnchor() with no luck becasue it doesn't exist.
Found the answer.
Looked at the repo on Github and at an example. It was different than the tutorial from the link I posted above.
https://github.com/angular/components/tree/master/src/google-maps#readme
Map marker needed to have this prop
<map-marker
#somemarker="mapMarker" // not #markerElem like from the link
(mapClick)="openInfoWindow(somemarker, marker.info)">
</map-marker>

Vue.js - inject a v-on button to spawn an overlay from static html sourced from an external source

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/

Add a class to expanded row using vanilla JS

In my angular application I am attempting a workaround because the ag-Grid api getRowClass() is not working at intended. All I need to do is add a css class to an expanded row and remove it when the row is collapsed.
The original method using ag-Grid api that does not work looks as follows:
setRowNode(params) {
this.gridOptions.getRowStyle = (params) => {
if(params.node.expanded) {
return { background: 'red' };
}
}
}
Ideally I would be able to selected the DOM and append a class to it. I tried this with some JQuery and it worked, but for obvious reasons I do not want to have JQuery in this app. I wrote something along these lines:
$('.ag-row[row="'+params.node.id+'"]',self.api.grid.gridPanel.eBodyContainer).addClass('ag-row-focus');
How would I fulfill this req using vanilla JS?
You can do it by creating a custom directive, like this one :
//row-focus.directive.ts
import { Directive, HostBinding, HostListener } from '#angular/core';
#Directive({
selector: '[appRowFocus]' // you can target classes, id etc.: '.grid-Holdings' for example
})
export class RowFocusDirective {
#HostBinding('class.ag-row-focus') isFocused = false;
#HostListener('click') toggleOpen() {
this.isFocused = !this.isFocused;
}
}
Import this directive on your module, and then attach the directive to your elements :
// your-component.component.ts :
<div class="row" appRowFocus>
These elements will toggle the ag-row-focus on click. You can add different #HostListener for other events.

Angular 2/Typescript Delete Object On Button Click

I have an Angular 2 app using Typescript but i am new to this, what i have is a table with a 'Delete' button,
I can pass the object data to my confirmation modal but when i 'Confirm' it, its still in my table.
delete-modal.component
import { Component, OnInit, Inject, Input } from '#angular/core';
import { TestService } from '../../ABC/TestService/TestService.service';
import { MdDialog, MdDialogRef, MD_DIALOG_DATA } from '#angular/material';
import { testModal } from 'models/test';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.css']
})
export class testDeleteModalComponent implements OnInit {
#Input('test') test: testModal;
constructor(private TestService: TestService, private accountService: AccountService,
#Inject(MD_DIALOG_DATA) private dialogData: any) { }
ngOnInit() {
console.log('test', this.dialogData.beneficiary);
this.test = this.dialogData.test;
}
deleteTest() {
if (this.dialogData.test.identifier) {
// this.dialogData.beneficiary.splice(this.dialogData.beneficiary.indexOf(this.beneficiaryAnt), 1);
// this.dialogData.beneficiary.splice(this.beneficiary);
// delete this.beneficiary;
this.dialogData.test.splice(this.dialogData.test.indexOf(this.dialogData.test), 1);
} else {
this.dialogData.test.operation = 'X';
}
}
}
HTML
<button md-icon-button (click)="deleteTest()" name="deleteTestDetails">
<md-icon>delete forever</md-icon>
</button>
All other HTML is in a main component and the 'Delete' button is used as shown below
<app-test-main-page-delete-button [test]="test"></app-test-main-page-delete-button>
The 'deleteTest' method is called when the user click the confirm button.
I have also included above some ways i have tried in the IF but they always come back
... is not a function
It is good that you asked this question, my projects of three peoples also struggling with this. we have found is two ways. what i will show is two ways of doing typescriptdelete.
solution a.
because it is object, it will need identifier. First is
var objectdelete = {
identifier: 'Mydelte',
value: '168%'
}
Next what we need is now service. some people call them directives but from my experience they are the same thing. We have alert so user knows if they did not set identifier that they must go back. I do not see service on your side, i see array being deleted. if you combine the array and the service, this will then be working across whole website.
export class DeleteService
delete(objectToDelete: string) {
if (!objectToDelete.identifier) {
alert('No identifer');
}else {
// Delete from your array here.
}
}
Solution 2.
If the above does not meed your needs, our tema also experimented with interfaces in typescript. You can see them here https://www.typescriptlang.org/docs/handbook/interfaces.html
so it becomes
export class myDeleteService {
deleter: IDeleter
}
export interface IDeleter {
delete: this.delete.delete(deletE);
deleteArray: this.array =[];
}
then simply in your html it will be
<button (click)='delete(dieleter)'>Delete me!</button>
These are all common typescript behaviours for angular2/4/5 so we are hoping to become more used to them when we have hads more time to use them!
The easiest way to delete data object on button click and refresh instantly when it's done :
Your parent html has to call children like this :
<app-component [inputData]="dataTable" (inputDataChange)="resetData()"/>
Add dataTable as class variable and implement the output function :
resetData() { this.dataTable=[] }
Then in children html leave your code (you can use this changes)
<button class="fa fa-delete" (click)="deleteTest()" name="deleteTestDetails">Delete</button>
Finaly in your children ts file set your data object for each change, and implement your input function
myDataTable: any = [];
#Input set inputData(data: DataTable) {
if(data) {
this.myDataTable = data;
}}
#Output() inputDataChange: EventEmitter<any> = new EventEmitter();
deleteTest() {
this.inputDataChange.emit(true);
}
What does this code do ?
It will emit and event to the parent when the delete button is clicked, then your parent will delete the dataTable, and finally, your children input will refresh it, as setter will catch the changes and refresh the variable.
If you want to apply those rules to table changes, then simply emit your dataTable and reassign it instead of reset it.
I am in a project with and our team have struggled on this for a whiles.
First thing I will say is this, Angular has not made this an easy task, so we will attempt to ignore the framework and write pure Java instead to make our lives easyer on ourselves.
SO looking at your button, I can see that you have started on the right track.
If the button is calling your component like the following
Html/Java
<button ng-click="delete()">Click me<button>
Component.ts
function delete = deleteMethod(testIdentifier) {
var abc = this.beneficiary.beneficiaryIdentifier.test.splice(this.beneficiary.beneficiaryIdentifier.test.indexOf(testIdentifier));
component2.deleteFunction();
}
Component2.ts
Then we can pass our identifiers into our parent or child components and remove the beneficiary like so:
deleteMethod(deetle) {
this.beneficiary.removeAtIndex(testIdentifier.splice(1), 1);
}
Nice and easy looking back, but it took our team of threes a long whiles to figure that ones out.

Categories

Resources