I have two components. The first component is a table component --> selector: 'table-component'. This component implements a standard filtering on my table.
My second component is a component for a page that displays the table.
I have multiple pages that use this table component by using the tag.
But some pages need some custom filtering. So how can I add custom filtering in the table component like this?
<table-component>
<custom-filtering>
<div>
Here has to come the filtering stuff
</div>
</custom-filtering>
</table-component>
In your customFiltering directive you must add this
return {
transclude: true,
/* your code */
...
...
}
and then you need to add this to your template
<ng-transclude></ng-transclude>
This mean when you write custom html between tags
<custom-filtering>
// html code here
</custom-filtering>
html code between tags will replaced where your
<ng-transclude></ng-transclude>
in your template..
Is that what you are looking for? Hope this help you
Other way to archive what you want is to extend your customFiltering directive and pass custom filter
Related
I am trying to create dynamic print template in Angular 12. I would like to fill the html body text dynamically based on the parameter passed on the selector used in the main component view. Print layout component is in Shared module and both the html templates are in app folder.
My requirements are:
I want to read the html file in the print layout component and
display it. I don't want to create component and render the
component in the print layout
I don't want to save the html file in the backend and trigger api to
fetch the html
print-layout.component.html
<div [innerHtml]="myTemplate"></div>
print-layout.component.ts
#Component({
selector: 'app-print-layout',
templateUrl: './print-layout.component.html',
styleUrls: ['./print-layout.component.scss']
})
export class PrintLayoutComponent implements OnInit{
#Input() modalID;
myTemplate;
ngOnInit() {
if(modalID == "1")
template = '../../app/tools/test/test-message1.html';
else (modalID == "2")
template = '../../app/tools/test/test-message2.html';
}
}
Also, I have gone through this solution Read HTML File into template in Angular 2? where they are creating component dynamically from main component file.
Is there any way to display html template file in the main component without creating component. Also, please suggest is it the right way to display html content without creating component in Angular...?
You can try to use iframe tag inside the print-layout.component.html like this
<iframe [src]="myTemplate"></iframe>
Obviously on ngOnInit() you need to assign path to myTemplate as same name to use in html
Right now I am creating a library (my-custom-library) and a project in which we'll use that library (called my-Project)
The requirement is, that within my-project I have to use my-custom-library, extended with templates, like this (my-project's app.component.html):
<my-custom-library>
<ng-template #myTemplate>
<div>Some static content for now</div>
</ng-template>
</my-custom-library>
The reason for this is, that in my-custom-library they want to have template-able components, where the template is given from the outside (in this case from my-project).
Within my-custom-library I'm supposed to access the given template(s) and pass them to the corresponding components. This I'm trying to achieve (my-custom-project's app.component.ts)
#ContentChild("myTemplate") myTemplateRef?: TemplateRef<any>;
(my-custom-project's app.component.html)
<ng-container [ngTemplateOutlet]="myTemplateRef"></ng-container>
My problem is, that the contentChild is always empty, the template never renders. The structure itself I think is working, since when I'm moving this same structure within just one project and use it there everything works fine, the contentChild gets its value and "my template" is rendered.
One more information, I don't know if its useful but my-custom-library is created like this (my-custom-library's app.module.ts):
export class AppModule {
constructor(private injector: Injector) {
const customElement = createCustomElement(AppComponent, { injector: this.injector });
customElements.define('my-custom-library', customElement);
}
}
What could cause this issue? Is it even possible to achieve this?
I had the same issue, Apparently ngTemplateOutlet does not work with angular elements. but you can try content projection without ngTemplateOutlet and it works fine.
e.g
<my-custom-library>
<div placeholder1></div>
</my-custom-library>
and you can define placeholder1 within your angular element (my-custom-library)
e.g
<div>
/*your angular my-custom-library code here */
/* the content you want to inject from outside of your angular elements*/
<ng-content select="[placeholder1]"></ng-content>
</div>
Note: you can also do nesting of your angular elements as well using this, but with this approach you have to make sure that ng-content is not affect by any ngif condition, because your angular element can project the content from outside but it can not regenerate projection based on your conditions. those conditions should be added from where you are projecting content.
In an Angular app, I've developed a reusable component whose responsibility to take care of rendering a wrapper around content provided by the parent component. The wrapper provides some layout and functionality that is required by multiple components (the parents). To accomplish this, I've adopted the "Component Composition" technique outlined in the following article which relies on the use of NgTemplateOutlet to enable the parent component to provide the content rendered inside the wrapper.
https://blog.bitsrc.io/component-reusability-techniques-with-angular-727a6c603ad2
This approach has been working well in a variety of situations but now I've come across a new situation where one of the parent components needs to use a content query to get an element inside the template outlet. I've been unsuccessful in using either the ViewChild or ContentChild decorators to get a handle on the element.
The following pseudo code outlines the basic approach I've attempted to take to date.
Reusable Element:
<div class="card">
<ng-container *ngTemplateOutlet="chart"></ng-container>
</div>
...
#ContentChild('chart', {static: false})
chart: TemplateRef<any>;
Parent Component:
<app-shared-component>
<ng-template #chart>
<div #top10Graph></div>
</ng-template>
</app-shared-component>
...
#ViewChild('top10Graph', { static: false }) private top10GraphContainer: ElementRef;
ngAfterViewInit(): void {
console.log(this.top10GraphContainer); // undefined
}
Is there any solution for using a content query for obtaining an element inside a template outlet such as I'm using here, or is another approach required?
The end goal (not demonstrated here) is to enable the parent component to render a data driven graph inside the template outlet.
I'm using Angular 10.
I think you get undefined because using ng-template is not yet rendered.
This code can work for you
<app-shared-component>
<ng-template #chart>
<div #top10Graph></div>
</ng-template>
<ng-container *ngTemplateOutlet="chart"></ng-container>
</app-shared-component>
Hope useful
Currently building a web page in Vue, and have hit a bit of an issue parsing and then rendering the <slot>'s child components.
I need to be able to take the slot, parse the components into an array, and then render those components for the end-user.
What I've Tried
I've tried many variations of things, most starting with this: this.$slots.default
This is the last version I tried
let slotComponents = [];
this.$slots.default.forEach(vNode => {
slotComponents.push(vNode);
});
But I've also tried selecting the elements within the vNode and using things like $childeren to select the components. No luck so far.
Potential Issues
The cause could be any number of things, but here is what I thought was going on (in order)
I'm not getting the components into the array properly
I'm not rendering them properly or missed something about how they render
Vue isn't supposed to do this?
Edit - Context
Seems like it would be easier if I gave you the full context of my specific problem.
Goal
To create a dynamic tab component. Should look like this.
// Example of component use
<tab-container>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<trash>
<!-- This one won't show up -->
</trash>
</tab-container>
In order to parse through this content, I needed to get the slot data out.
// Inside the <tabs-container> component
computed: {
tabs: function() {
let tabs = []
this.$slots.default.forEach(vNode => {
tabs.push(vNode);
});
return tabs;
}
}
// Inside the <tabs-container> template
<div>
{{tabs[currentTab]}}
</div>
You shouldn't be using template and computed properties if you want to programmatically render out <tab> inside <tab-container>. {{}} in templates are designed to perform basic operations of JS. The most appropriate way will be to use render function.
Render functions - Vue docs
Here is a working example that takes in few tabs components and shows only active tab component: https://jsfiddle.net/ajitid/eywraw8t/403667/
I am trying to assign *ngIf directive from angular code to the template during runtime. Not been able to figure out a way to do it. Is view/templateref an option to do it or is there a different way and an easier one. Is it possible in the first place?
Update:
The code is a little messy and jumbled, so avoided it. But here is the DOM code how it approximately looks and why I need to add inbuilt structural directives dynamically.
<div>
<input type="text" [value]="userProvidedValue">
<textarea [value]="someDynamicDOMCodefromWYSIWYG">
<!-- user provided provided code or dynamic code -->
</textarea>
</div>
<div>
<select *ngIf="fetchArraywithHttpFromuserProvidedValue">
<option *ngFor="let val of fetchArraywithHttpFrom-userProvidedValue" value=""></option>
</select>
</div>
<div>
<ng-template>
<!-- Some User provided code or dynamic code which might need to use *ngIf OR *ngFor -->
<!-- The *ngIf OR *ngFor will be added dynamically based on a manipulator function which is decided from the value of fetchArraywithHttpFromuserProvidedValue -->
</ng-template>
</div>
Update
I am doing a fetch request based on userProvidedValue value and the result of the fetch request decides the fetchArraywithHttpFromuserProvidedValue array. Second, based on the value of fetchArraywithHttpFromuserProvidedValue derived from fetch request the decision is made whether to show the user provided template or a predecided set of templates in switch option. (only part of user provided template needs the *ngIf directive. The user template is parsed in JS to get the needed part). The use case is similar to a tool that creates a HTML design/page which fetches structure from a user. This is exactly for a similar tool, just that I am not making a tool that creates a user defined HTML page.
Please help me out with this. If this is not possible, then please recommend an alternative that will allow me to assign functionality similarly or get me a workaround in this situation.
Update 2
Like pointed out in one of the answers below, all of the following templates failed to be added with proper parsing/compilation with elementref or using ViewContainerRef + TemplateRef:
<input [value]="someVariableFromClass"/>
<input value="{{someVariableFromClass}}"/>
<div *ngFor="let item of items">{{item}}</div>
The following works though, if the template is in the DOM before the application is being built and loaded (not dynamic addition):
<ng-template #tpl>
<!-- Add the template to #vcr using ViewContainerRef from the class -->
<div *ngFor="let item of items">{{item}}</div>
</ng-template>
<div #vcr>
<!-- Add the template from #tpl using ViewContainerRef from the class -->
</div>
Currently, I am trying out the compiler API in Angular and checking if compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>): Promise<ModuleWithComponentFactories<T>> can help me in this use case. The issue seems like I will have a create a completely new component by assigning the html as a template to the component, then create a dynamic module, and then compile the whole before inserting into the view (Logic I am trying out currently - not working yet). After this (if I succeed), I will try adding the component template with a directive and see if that compiles right. Any help is welcome. It seems like I might end up by adding static directives to manual placeholders and adding [innerHTML]= / safeHTML / Sanitize API, if I dont succeed. Not ideal though. Please help with alternatives if you can.
I am using this example, though it's plunkr currently not working.
How can I use/create dynamic template to compile dynamic Component with Angular 2.0?
http://plnkr.co/edit/wh4VJG?p=preview
You don't call a fetch method inside an *ngIf. The ... inside *ngIf="..." gets executed every time angular decides to do change-detection and that might be dozens of times per second. You don't want to deploy a DDOS for your own backend.
This is why you should put a field like isUserProvidedValueValid there and update that field in the subscription of your HttpClient-call:
userProvidedValue: any;
isUserProvidedValueValid: boolean = false;
constructor(private httpClient: HttpClient) {}
doFetch() { // called by a button-click for example
this.isUserProvidedValueValid = false;
this.httpClient
.get<any>(SOME_URL)
.subscribe(res => {
if (res) { // you might have a complex check here, not just not-undefined
this.isUserProvidedValueValid = true;
}
// you might consider putting this in the if-clause and in the *ngIf only check for userProvidedValue being not null
this.userProvidedValue = res;
});
}
Now for the code that your user provides: first of all, you need to sanitize it. You can do it with a pipe inside a directive (you don't need to use ng-template, you use innerHtml of a normal tag for it) like in this example: https://stackoverflow.com/a/39858064/4125622
template: `<div [innerHtml]="html | safeHtml"></div>`,
Or before the .subscribe() in the code above, you can do
// domSanitizer needs to be injected in constructor as well
.map(res => this.domSanitizer.bypassSecurityTrustHtml(res));
If you need to transform this code, you can add another .map()-RXJS-mapper or another custom pipe - it's up to you which kind of transformer you prefer. In the transformer you can use VanillaJS for manipulation of the user-code. Perhaps an HTML-parser-plugin or a JSON-toHTML-parser-plugin or something similar might be useful to you - depends on the data type your user provides.
No you can't add structure directives dynamically, you need to approach it by thinking what is your app expecting later on.
For example, if you plan on looping through an array which I'm guessing is what you intend to do having looked at your code, you could do the following:
hotels: any;
<div *ngIf="hotels.length > 0">
<div *ngFor="let item of hotels">{{item.name}}</div>
</div>