Angular: Rendering and Getting the HTML of a Component Dynamically - html

Render the HTML of a component so that it could be used to open a new tab of the type about:blank (a blank new html page that nothing has to do with the application itself).
Why?
To avoid creating the HTML using a string variable as, for example:
var html = '<div>
<h3>My Template</h3>
' + myProperty + '
</div>';
I saw that you can render a component dynamically using ViewContainerRef.createComponent and ComponentFactoryResolver. The problem with this is that the component is rendered inside the view container, in the application. What I would like to do is generate the HTML of that component so that then I can use that HTML to put it wherever I want. (in this case, in the document property of the object given by the window.open() method)
Example:
#Component({
selector: 'app-component',
template: `
<div>
<h3>My Template</h3>
{{ myProperty }}
</div>
`,
styleUrls: ['./my.component.less']
})
export class MyComponent{
myProperty: string;
}
I expect to use it in this way:
//get new window tab
var newTab = window.open('about:blank', '_blank');
//get the HTML of the component
//use it to open the new tab
newTab.document.write(html);

It may help other people so I post here what I did and which problems I found. After looking to some solutions, this one worked for me. The problem was then I realize that Angular sanitizes your HTML removing all possible <script> tags. Unfortunately I had like 3 of them. In addition, if you don't want them to be sanitized, you have to use a service called DomSanitizerand use the method bypassSecurityTrustScript (doc) passing the script as a parameter. So the idea of don't 'stringify' the code was gone. Saying that, I used the original approach, where the HTML is stored in a variable then passed as a parameter to window.open

Related

Angular, make links in comments clickable

I am working on an application where user can add comments to certain fields. these comments can also be links. So, as a user I want to be able to click on those links rather than copy pasting them in a new tab.
If a normal web link ([http://|http:]... or [https://|https:]...) occurs in a comment/attribute value, it should be presented as a clickable link.
Multiple links may occur in the same comment/attribute value.
Clicking on a link opens a new browser tab that calls up this link.
This is how the formControl is being managed. I think i can identify multiply links with the help of regex but how do I make them clickable as well?
Thanks for answering and helping in advance.
this.formControl = new FormControl('', [this.params.customValidations(this.params)]);
this.formControl.valueChanges.subscribe(() => {
this.sendStatusToServices();
});
Outside the form editor/input (most likely what you're looking for)
Either before saving the value of the Form Field to the Database, or editing the received body from the database just before presenting to the user, you can use Regex to replace links with anchor tags.
function replaceURLWithHTMLLinks(text) {
var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
return text.replace(exp,"<a href='$1'>$1</a>");
}
Rich text editor
If however, you're trying to enable links INSIDE the form input (like WordPress's text editor), that's going to be a bit more difficult. You'll need a <textarea> to enable custom HTML elements. Then you need to detect when the user has typed a URL, so you can call replaceURLWithHTMLLinks(). Honestly, you should just use a package. There's several good one out there.
Angular Rich Text Editor - A WYSIWYG Markdown Editor, by SyncFusion
NgxEditor, by sibiraj-s
typester-editor
Hope this helps
Using a regex approach and a pipe I was able to come up with something like below.
What I'm doing is replacing the links with hyperlink tags using a proper regex.
url replacement regex is taken from here
Supports multiple links within same comment.
Here is the sample pipe code
#Pipe({
name: 'comment'
})
export class CommentPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer){}
transform(value: any, args?: any): any {
const replacedValue = this.linkify(value);
return this.sanitizer.bypassSecurityTrustHtml(replacedValue)
}
// https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links#21925491
// this method is taken from above answer
linkify(inputText: string) {
var replacedText, replacePattern1, replacePattern2, replacePattern3;
//URLs starting with http://, https://, or ftp://
replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, '$1');
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, '$1$2');
//Change email addresses to mailto:: links.
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+#[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
replacedText = replacedText.replace(replacePattern3, '$1');
return replacedText;
}
}
Here is the completed stackblitz
If you want links within Input itself you might want to try different approach

Angular data binding sending strings?

I'm new to angular, and I would like to know if there's is a way to send a string to the Html file with a variable inside?
test.ts
test: string = "Display this {{testText}}";
testText: string = "Success";
test.html
<p>{{test}}</p>
What I want to achieve is that it displays this: Display this Success.
I'm just curious if this is possible, perhaps I can retrieve from an API chunks of HTML string and display them like that.
**
It is basic Javascript string operation. For this, there is nothing special with Angular at your TypeScript file.
Without handling updates on test
On Typescript file you have two options to merge strings:
First Way:
testText: string = "Success";
test: string = `Display this ${this.testText}`;
Second Way:
testText: string = "Success";
test: string = "Display this " + this.testText;
Of course you can see a problem with both of them. What will happen when you update your test? Based on these ways, the testText just initializing when the component instance is created, so if you want to fetch changes on your test variable you should use the way from one of following
**
First Way:
test.html
<p>Display is {{testText}}</p>
<p>{{'Display is ' + testText}}
Socond Way:
Specifically you can create a custom Pipe. You should check documentation about how are them work. For only this case you don't need to use this way. Pipes are generally for more generic or more complex operations.
Third way:
(more bad than others. Because change detector of Angular will not understand when your content should update the paragraph. You should use others.)
test.ts
getTestText() { return 'Display is ' + this.testText }
test.html
<p>{{ getTestText() }}</p>
**
Binding Dynamic Html Content
For binding any dynamic HTML template you need to use innerHTML attribute like
<div [innerHTML]="htmlVariable"></div>
but this is not a trusted way because there is nothing to check is the html is trusted or is it valid etc. Or if the html contains the selector of any component, it won 't render as expected. You should use more complex ways to do it.

A component variable not being displayed on the page (Angular6)

So the thing is: The html template of an angular component doesn't seem to be aware of the variable i set. Basically even if I set a variable in .ts class and then try to display it in a component, the view behaves as if there was no variable.
I've also tried creating a variable inside a tag but it just wouldn't work
TS
The ts part of a component
export class <%component_name%> {
bar = "test";
constructor(){...}
}
HTML
The html part of a component
<div>
<div>{{bar}}</div>
</div>
The result is:
Just in case: if I type a text inside the div without trying to display a variable, it works
HTML
Result
Oh, just in case: logging the hello variable in constructor function does display it in console: click
besides, if I add an *ngIf referencing some boolean from the TS file like that:
TS
export class <%component_name%> {
foo = true;
bar = "test";
constructor(){...}
}
HTML
<div *ngIf="foo">
<button>{{bar}}</button>
</div>
..then nothing at all is displayed.
At the end it's supposed to be a button with an icon and text on it, but i only get an icon on the button (Which is referenced inside the .html file)
If you have any idea of what might be going on, pls tell

Angular2 Html Templates

Is there a way to call a component selector from another html template. For instance:
#Component({
selector: 'reports',
templateUrl: 'reports.html'
})
Can I call that selector "reports" from within another templateUrl? I'm trying to split out my html into separate files in order to make it more manageable. I know how to set it up like <reports></reports> in the html. I'm not sure how I would set this up or call it per say from within the modules.
When you specify a selector, you are basically defining a custom HTML element. So you can use it in any other template in the application as you've shown: <reports></reports>.
Angular modules provide the "template resolution environment". So you need to ensure that the component containing the "reports" selector is declared in the same component as any template that uses it, or is "pulled in" by way of an Angular module import.
I have an example here: https://github.com/DeborahK/Angular2-GettingStarted/tree/master/APM%20-%20Final Check out the star.component.ts.
selector: "[reports]"
selector: "[reports]"
I think your split your html page sub pages, it's very easy.
<body>
<div clss="ui container">
<reports></reports> //the reports page view render hear,just add
this component class in NgModules, it will works
</div>
<div clss="ui container">
<reports-list></reports-list> //you can add another file also same
</div>
</body>

Is it possible to allow user to edit and save html template in angularjs application

I have an traditional asp.net application which reads HTML template and renders it inside div control. Using bootstrap xeditable user can edit certain parts of the template (only text). This template is later used to send emails. This functionality is working fine. Now I am rewriting this application using AngularJs and WebApi. I am using angular route to route to different pages (plain html) of the application. I am able to load the template using directive. now I want to allow user to edit the text and save the complete template so that it can be used later for sending email.
MyTemplate.html
<p>this is some text</p>
<p>this is some more text</p>
<p>this is some another text</p>
Directive
myapp.directive("customDirective", function () {
return {
templateUrl: 'MyTemplate.html'
};
});
Notify.html
<div>
<h2>{{message}}</h2>
<input type="button" ng-click="Redirect()" value="Report" />
</div>
<custom-directive></custom-directive>
I want that user should be able to edit the text in MyTemplate.html and save it as complete template for later use. Is this achievable?
Do not store it in file. Store the template in your database. Provide a default value there, so something shows if the user has not modified it yet.
In you directive, load the template from your database through your API. After you do that, append the template to the contents of your directive inside your link callback function and compile the directive (if needed).
myapp.directive("customDirective", ($compile, yourService) => {
return {
link: (scope, elem) => {
yourService.fetchTemplate().then(template => {
elem.html(template);
$compile(elem.contents())(scope);
});
}
}
});
Please make sure to sanitise your data properly. It could be fairly dangerous injecting and compiling template created by the user.
I hope this points you in the right direction.
Edit
You might not event need the $compile step. It depends on what kind of template you have in mind. If it is just a simple element without any connection to angular, simply skip the $compile line.
Edit 2 - Display the template on click
Please note the following is just a very simplified version, but it should point you in the right direction.
In your parent controller
$scope.state = {
displayTemplate: false
};
In your template
<my-template-directive ng-if="state.displayTemplate"></my-template-directive>
<button ng-click="state.displayTemplate = true">Show Template</button>