I'm trying to add a "clipboard" directive using this example.
However I want to compute the string to copy to the clipboard.
I want to pass the result of a function which returns a string, to my directive.
Currently, it passes "getCopyDetails(supplier)" string, to the directive.
As I am trying to pass a computed string to the button (which contains the directive) in html:
<button [clipboard]="foo, getCopyDetails(supplier)" (clipboardSuccess)="onSuccess()">Copy</button>
Which causes the error.
code:
clipboard.directive.ts:
import {Directive,ElementRef,Input,Output,EventEmitter, ViewChild, AfterViewInit} from "#angular/core";
import Clipboard from "clipboard";
import { SupplierSearchComponent } from "../components/suppliersearch.component"
declare var jQuery:any;
#Directive({
selector: "[clipboard]"
})
export class ClipboardDirective implements AfterViewInit {
clipboard: Clipboard;
#Input("clipboard")
elt:ElementRef;
text: string;
#Output()
clipboardSuccess:EventEmitter<any> = new EventEmitter();
#Output()
clipboardError:EventEmitter<any> = new EventEmitter();
constructor(private eltRef:ElementRef, text) {
this.text = text
}
ngAfterViewInit() {
this.clipboard = new Clipboard(this.eltRef.nativeElement, {
target: () => {
return this.elt;
}
} as any);
this.clipboard.on("success", (e) => {
this.clipboardSuccess.emit();
});
this.clipboard.on("error", (e) => {
this.clipboardError.emit();
});
}
ngOnDestroy() {
if (this.clipboard) {
this.clipboard.destroy();
}
}
}
search.component.html:
<div class="website" *ngIf="xxx.website !== undefined"><a #foo href="{{formatUrl(xxx.website)}}" target="_blank" (click)="someclickmethod()">{{xxx.website}}</a></div>
<button [clipboard]="foo, getCopyDetails(supplier)" (clipboardSuccess)="onSuccess()">Copy</button>
snippet from search.component.ts (same component as above):
getCopyDetails(supplier: Supplier): string {
var details: string = "";
details += xxx.yyy + "\n\n";
details += xxx.yyy + "\n";
details += xxx.yyy + "\n";
details += xxx.yyy + "\n";
details += xxx.yyy + "\n";
return details;
}
Another programmer's old html code (he used "ngx-clipboard": "^5.0.8" plugin which is a wrapper around clipboard.js which I am using for the clipboard) has been commented out and I have basically tried to apply it to my directive in the html above:
<!--<button class="btn anchor" (click)="supplierCopyDetailsClick()" ngxClipboard [cbContent]="getCopyDetails(supplier)">Copy details</button>-->
How do I pass the string obtained from getCopyDetails() to my directive?
Judging from this SO answer I can do my-object='{"Code":"test"}' within the HTML and then use that in my directive to copy it. I am a little unsure what code I place in my directive to hold that object though.
You can inject using Angular DI ONLY: #Injectable() services, Parents Components or InjectionToken.
So text is incorrect.
constructor(private eltRef:ElementRef, text) {
this.text = text
}
Related
I am working on an angular project where I have to make form in multiple steps, problem arises when I upload file in input field on particular step. If I move to any other step and come back I get the error : " Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string. "
My html file:-
<div *ngIf="step==1" [formGroup]="myForm">
<input type="file" name="inp" id="inp" formControlName="inp">
</div>
<div *ngIf="step==2"></div>
<button (click)="change()">Change step</button>
{{myForm.value | json}}
My .ts file:-
export class AppComponent implements OnInit {
myForm;
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.myForm = this.fb.group({
inp: this.fb.control('')
});
}
step = 1;
change() {
if (this.step == 1) {
this.step = 2;
} else {
this.step = 1;
}
}
}
In Angular 9+ I can successfully convert a string to a html and then load that that html using innerHtml and bypassSecurityTrustHtml().
My question is it possible to also dynamically load/render the converted html to include and recognise angular/javascript markup language eg *ngIf, handle bars and click events.
Below is the code and stackblitz at the attempt so far but as you can see it doesn't recognise the markup.
https://stackblitz.com/edit/dynamic-angular?file=app/app.component.ts
export class AppComponent implements OnInit {
text: string = "Hello world";
content: any;
constructor(private domSantizer: DomSanitizer) {}
ngOnInit() {
let body: any =
'<div>{{text}}<div><br><button (click)="test()">Test</button>';
this.content = this.domSantizer.bypassSecurityTrustHtml(body);
}
test() {
alert("It works");
}
}
Html
<div [innerHTML]="content"></div>
I have researched and tried many solutions.
My research and trial results are below.
html
<div #container></div>
typescript side as below
export class AppComponent implements OnInit {
#ViewChild("container", { read: ViewContainerRef })
container: ViewContainerRef;
constructor(private compiler: Compiler) {}
text: string = "asdasd";
ngOnInit() {
this.addComponent(
`<div>{{text}}<div><br><button (click)="test()">Test</button>
`,
{
text: "Hello word",
test: function() {
alert("It's work");
}
}
);
}
private addComponent(template: string, properties?: any = {}) {
#Component({ template })
class TemplateComponent {}
#NgModule({ declarations: [TemplateComponent] })
class TemplateModule {}
const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
const factory = mod.componentFactories.find(
comp => comp.componentType === TemplateComponent
);
const component = this.container.createComponent(factory);
Object.assign(component.instance, properties);
// If properties are changed at a later stage, the change detection
// may need to be triggered manually:
// component.changeDetectorRef.detectChanges();
}
demo
some posts I have reviewed
compile dynamic Component
angular-html-binding
I think it makes the most sense :)
Error when component loading dynamic
DynamicBuilderComponent.ngfactory.js:198 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-pristine: true'. Current value: 'ng-pristine: false'.
Problem
after binding json in select2data to select2 component Angular throw exception.
component code
#Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'select2',
Imported changeDetection in component.
template: `
<div [formGroup]="form">
<ng-container>
<ng-select2
[data]="select2data"
[options]="options"
[width]="500"
[formControlName]="field.code"
(keyup)="changed($event.target.value)">
</ng-select2>
</ng-container>
</div>`
})
select2 component class
export class Select2Component implements OnInit {
#Input() field: any = {};
#Input() form: FormGroup;
public exampleData: Array<Select2OptionData>;
public options: Options;
public value: string[];
select2data: any;
public selected: string;
constructor(public cl: Services,private cd: ChangeDetectorRef) {
this.options = {
width: '258',
multiple: true,
tags: false
};
}
Problem Area After Binding subscribe data in ng select2 component
changed(search: any) {
//call service pass search text to service
return this.cl.searchFunc(search).subscribe(
res1 =>
this.select2data = res1.data;
this.cd.markForCheck(); // marks path
}
}
},
error => {
console.log('error = ', error);
});
}
}
i tried to print this.select2data in console.log its return me json.
Vendor.js
function expressionChangedAfterItHasBeenCheckedError(context, oldValue, currValue, isFirstCheck) {
var msg = "ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '" + oldValue + "'. Current value: '" + currValue + "'.";
if (isFirstCheck) {
msg +=
" It seems like the view has been created after its parent and its children have been dirty checked." +
" Has it been created in a change detection hook ?";
}
return viewDebugError(msg, context);
}
Great Article
https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html
Reference
Expression ___ has changed after it was checked
any suggestion is most welcome.
I believe that you put your component select2 inside another component which contains a form which you then pass to select2 for create another <form> tag, is that correct? I mean do you have something like that?
<form [formGroup]="form">
<!-- Some code -->
<select2 [field]="something" [form]="form"></select2>
</form>
If so, then your select2 component SHOULD NOT contain re-declaration of form, it should not contain anything related to forms at all. It should be a form control. Please read a post by Netanel Basal on how to create custom form controls. You will need to create ControlValueAccessor for your select2 and wire it up to Angular forms through a custom provider.
The issue you're facing is that since you include form object twice in the DOM data changes are propagated twice as well and you run into issues. There should be only one reference to a specific instance of FormGroup in your templates.
Solution that worked
#Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'select2',
export class Select2Component implements OnInit {
constructor(public cl: Services,private cd: ChangeDetectorRef) {
this.options = {
width: '258',
multiple: true,
tags: false
};
}
Binding function
changed(search: any) {
//call service pass search text to service
return this.cl.searchFunc(search).subscribe(
res1 =>
this.select2data = res1.data;
this.cd.markForCheck(); // marks path
this.cd.detectChanges();
}
}
},
error => {
console.log('error = ', error);
});
}
I have an Angular project that stores data inside firebase. The data is stored as string (prdName: string;) inside the database. I want to ask if is it possible if i put a html tag inside the string like <b>this is text</b> and store it and then bind/view them as html text format? (the text become bold)
//service.ts
getData() {
this.List = this.firebase.list('Product');
return this.List;
}
insertProduct(Product: Product) {
this.productList.push({
prdName: Product.prdName,
prdCategory: Product.prdCategory,
prdSup: Product.prdSup,
prdImage: Product.prdImage,
prdDescription: Product.prdDescription
});
}
//component.ts
ngOnInit() {
var x = this.ListService.getData();
x.snapshotChanges().subscribe(item => {
this.List = [];
item.forEach(element => {
var y = element.payload.toJSON();
y["$prdKey"] = element.key;
this.List.push(y as List);
});
});
}
<!--component.html-->
<label>Product Name: </label> {{ListService.selectedProduct.prdName}}
Please let me know if more snippets are needed. Thank you very much in advance.
You have to use innerHtml to bind html tags :
<div [innerHTML]="ListService.selectedProduct.prdName"></div>
Check this : https://angular.io/guide/template-syntax#!#property-binding-or-interpolation-
I used such pipe in my project to make it work right
import { PipeTransform, Pipe } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser'
#Pipe({ name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value) {
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
then in the place where you want to have your html you simply do
<div [innerHTML]="someHtmlContent | safeHtml"></div>
pipe is needed to make this html content trusted, more information about this:
https://angular.io/guide/security#bypass-security-apis
The idea:
<h2 *ngIf="tag == 'h2'"></h2>
<h3 *ngIf="tag == 'h3'"></h3>
<p *ngIf="tag == 'p'"></p>
I want to get the tag be dynamic, depending on the tag property value.
The tag is an Input() parameter
P.S.: I have tried to do:
<{{tag}></{{tag}}>, but it gives and error and is not working
<div (mouseenter)="setEditMode()" [innerHTML]="result | safeHtml" *ngIf="!editMode"></div>
<div (mouseleave)="setViewModeIfNotFocused()" *ngIf="editMode">
<input type="text" [(ngModel)]="content" #inputEl>
</div>
-
import { Component, Input, OnInit, ViewChild } from '#angular/core';
#Component({
selector: 'ui-edit-field',
templateUrl: 'edit-field.component.html'
})
export class UiEditFieldComponent implements OnInit {
#ViewChild('inputEl')
public inputEl: any;
#Input('tag')
public tag: string;
#Input('classes')
public classes: string;
#Input('content')
public content: string;
public result: string;
public editMode = false;
constructor() {
}
ngOnInit() {
this.result = '<' + this.tag + ' class="' + this.classes + '">' + this.content + '</' + this.tag + '>';
}
setEditMode() {
this.editMode = true;
}
setViewModeIfNotFocused() {
if (true) {
this.editMode = false;
}
}
}
You can use
<div [outerHTML]="tag"></div>
but tag needs to contain the <...>, because they can't be added in the template.
If tag is supposed to become an Angular component, then you need a different approach. Above approach only allows to add HTML without any Angular functionality.
See also https://stackoverflow.com/a/41089093/217408
update
_content:string;
#Input('content')
public set content(val:string) : void { this._content = val; updateContent();}
ngOnInit() {
this._updateContent();
}
_updateContent() {
this.result = '<' + this.tag + ' class="' + this.classes + '">' + this._content + '</' + this.tag + '>';
}
You can achieve this if you write all the content as a string in your file.ts
<div [innerHtml]="YourHtmlString"></div>
and
this.YourHtmlString = `<${yourInput}>whatEver</${yourInput}>`
https://angular.io/guide/template-syntax#property-binding-or-interpolation