How to input a component into another component in Angular? - javascript

In React, you can pass entire components into other components, and then easily display those components in the respective div. This referring not to imports, but rather function parameters so what is displayed will vary.
In Angular, you use #Input() and #Output() to do the same thing with values and functions, but what about components? How do you do this with components? I'm not talking about imports handled by the module file or at the top of the file; I'm talking about parameters that will vary based on the runtime of your program.
I.e, I want to convert the following React code into Angular, where children is another React component passed in via ReactNode:
const ReactComponent = (props) => {
return (
<div>
{props.children}
</div>
);
};
Also I apologize if any of my terminology is incorrect; I'm new to Angular and I'm coming from a (limited) React background.
I tried using #Input() with a parameter of type "any" but this doesn't seem right.

Dynamically, like React, you can use ng-content. This is the opposite of Reacts { props.children }
Use it so (Component - Code Behind)
#Component({
selector: 'btn',
template: `
<button class='btn' (click)="do()">
<ng-content></ng-content>
</button>
`
})
export class ButtonComponent{
do(){
console.log("Ok")
}
}
And the HTML
<div>
<btn>
This is a dynamic Button
</btn>
</div>
This is a dynamic Button can be a component, too. So like this:
<div>
<btn>
<div><span>Hello! </span><button>I'm a button inside a button!?</button></div>
<app-my-custom-component [data]="bindAnything"></app-my-custom-component>
</btn>
</div>
The Result can be look like this:
And here you can play with this on Stackblitz.
Read all about it in the official documentation here.
Content projection
This topic describes how to use content projection to create flexible, reusable components.
To view or download the example code used in this topic, see the live example / download example.
Content projection is a pattern in which you insert, or project, the content you want to use inside another component. For example, you could have a Card component that accepts content provided by another component.

parent.component.html
<div>
<child-component></child-component>
</div>
child.component.ts
#Component({ selector: 'child-component' })
export class ChildComponent {}
This is a very quick way to show how Angular components can be nested. In this example you have a parent component template file (parent.component.html) and inside this file you can include any other component by their selector. In the example I include 'child-component' and show child component ts file.
If you want to make child component dynamic (if I understand you correctly - to pass data from parent to child) and for example pass string value into it, you can:
parent.component.html
<div>
<child-component name="Andrei"></child-component>
</div>
child.component.ts
#Component({ selector: 'child-component' })
export class ChildComponent {
#Input() name: String;
}
More on Angular components communication: https://angular.io/guide/component-interaction

Related

How to read HTML file into component in Angular

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

Angular > How to use a content query to get an element inside an template outlet

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

Import a local module as component with sub components

I have a React component that works 'standalone'. It allows child components to be editable (dynamically) with an 'editable' state (editable = true makes children editable, editable = false does not)
import Editable from './editable'
<Editable>
<div>edit me!</div>
</Editable>
Next to this component I have some 'sub components' that could be used as children for the 'main component' (Editable). The can be used to specify other behavior for their children, when Editable's state is set to 'editable = true'. I don't want to import these sub components all separately. I know some ways to achieve this, I will specify them below the question.
But what I'm looking for is syntax like this:
import Editable from './editable'
<Editable>
<div>edit me!</div>
<Editable.Hide>
<div>don't show me when editable</div>
</Editable.Hide>
<Editable.Not>
<div>don't make me editable when editable</div>
</Editable.Not>
</Editable>
So the export needs to be structured so that the usage of the default export will result in the main component, but (somehow) the sub components can also be accessed through that same default export.
Why? Mostly my curiosity into the possibilities and I would love to use a syntax like the above.
So is it possible to structure an export to be able to use a syntax like that?
These are the ways I already know how to import components with sub components:
import Editable from './editable'
<Editable.MainComponent>
<div>edit me!</div>
<Editable.Hide>
<div>don't show me when editable</div>
</Editable.Hide>
<Editable.Not>
<div>don't make me editable when editable</div>
</Editable.Not>
</Editable.MainComponent>
If I would only want the main components, I could do this with some filestructure in the editable folder and import like this:
import EditableMainComponent from './editable/mainComponent'
<EditableMainComponent>
<div>edit me!</div>
</EditableMainComponent>
Or create a named export with only the main component in the same file.
import {EditableMainComponent} from './editable'
<EditableMainComponent>
<div>edit me!</div>
</EditableMainComponent>
Another way to go is to keep only the main component as the default export and the sub components as named exports.
I am trying to avoid usage like this:
import Editable, {EditableHide, EditableNot} from './editable'
<Editable>
<div>edit me!</div>
<EditableHide>
<div>don't show me when editable</div>
</EditableHide>
<EditableNot>
<div>don't make me editable when editable</div>
</EditableNot>
</Editable>
Because I don't want the user to have to specify all the different sub components in the import. So that could also be achieved like this:
import { * as Editable } from './editable'
<Editable.default>
<div>edit me!</div>
<Editable.Hide>
<div>don't show me when editable</div>
</Editable.Hide>
<Editable.Not>
<div>Don't make me editable when editable</div>
</Editable.Not>
</Editable.default>
If components are self-sufficient and can be used separately, it's preferable to consider them of the same value and treat all of them as named exports:
import {Editable, EditableHide, EditableNot} from './editable'
If some components aren't supposed to be used apart from main component, they can be namespaced with it.
For class components:
class Not extends Component {...}
export default class Editable extends Component {
static Not = Not;
...
}
For functional components:
const Not = props => ...;
const Editable = props => ...;
Editable.Not = Not;
export default Editable;
The advantage of the last approach is that this improves testability by mocking or spying secondary components in tests, as long as they are referred as Editable.Not and not Not inside Editable main component.
The disadvantage of the last approach is that secondary components cannot be tree-shaken, this shouldn't be done in case their footprint is large and main component can be often used without them.

Difference between HTML component and a normal component in Aurelia?

I'm starting with the standard TypeScript skeleton for my Aurelia development.
I wanted to add some code to the "nav-bar" component, so I decided to convert it from a simple HTML-only component to a regular component. To this end, I modified the require tag in my app.html from:
<require from="nav-bar.html"></require>
to
<require from="nav-bar"></require>
Next, I created a nav-bar.ts file, which contained the following code:
import {autoinject} from 'aurelia-framework';
import {customElement, bindable} from 'aurelia-framework';
// import {HttpClient} from 'aurelia-fetch-client';
#autoinject
#customElement('nav-bar')
export class NavBarClass {
public attached() {
console.log("TEST");
}
}
}
I left the nav-bar.html exactly as is. Now the program runs and the console contains the TEST value from the instantiated NavBarClass, BUT the menu that used to be displayed when nav-bar was HTML-only is now missing.
How do I get the menu back? What am I doing wrong? How does a regular component differ from an HTML-only component?
Thanks for your help,
-Greg
In a standard custom element the bindable properties are defined in the view-model:
nav-bar.js:
export class NavBar {
#bindable router;
...
...
}
In an html-only custom element, there is no view-model so the bindable properties are listed on the <template> element's bindable attribute instead:
nav-bar.html:
<template bindable="router">
...
...
</template>
Either way the nav-bar element usage is the same:
<nav-bar router.bind="router"></nav-bar>

How to use an AngularJS 2 component multiple times in the same page?

After declaring a component with selector 'some-comp', using <some-comp></some-comp> in a page will only work once. I would like to use it multiple times any help, here is an example code:
#Component({ selector: 'some-comp' })
#View({ template: 'component template' })
class SomeComponent { }
bootstrap(SomeComponent);
The bootstrapped Component is really to be looked at as an app, if you want to use the same component multiple times, you will want to make it into a directive and then include that directive in your app which you bootstrap.
Look at this menu component as an example, you will see the <aria-menuitem> components used multiple times as well as the <aria-menu> components.
https://github.com/dylanb/Axponents/tree/master/angular2

Categories