Create a master table component and add child columns dynamically in angular - html

Good morning everyone !
So have to create an application that displays many tables that all look the same (except their columns of course and the objects inside).
I have a master table component which can be summarized by:
<master-table>
<!-- here I define the master table style, pagination, etc etc
which is the same everywhere -->
</master-table>
In the master table component TS file I have basically options that are relevant for every page where this kind of table should be displayed, such as filterDate, clearSelection etc etc etc nothing very special.
The point is, I don't want to repeat this code every where because it is not necessary, so my idea was to create several component that would extend the master table component.
It works fine for the typescript values, but I am stuck with the HTML.
In my master table HTML I would like at some point some kind of placeholder something like this:
<master-table>
<standard-for-all-table></standard-for-all-table>
<!-- insert here child columns -->
</master-table>
In the components that extends my master table I was imagining then something like:
<!-- child component -->
<master-table></master-table>
<child-column>column definition</child-column>
Doing this would allow me to define only the columns in the child components HTML and they would be added automatically to the parent HTML at runtime...
Any idea how to do this ?
Cheers and thanks !

Basically you have to create your main master-table component and your generic list chid-column component and insert it in your parent html template structure.
I've edit the final part hope in a better understanding way...
Then you can structure your child component to contain all the properties you need and thanks to *ngIf show only the properties you return from your provider methods i.e. getClients(), getUsers(), getHouses(), also thanks to the #Input decorator you can inject this data directly from the parent to the child and create many components you want with just a change of the data.
So in your parent you can have something like
import { Component } from '#angular/core';
import { MY_DATA_FROMP_ROVIDER } from './mydata/provider';
#Component({
selector: 'master-table',
template: `
<ul>
<child-column *ngFor="let item of items"
[item]="item">
</app-hero-child>
</ul>
`
})
export class MasterTableComponent {
items:any
constructor(public data: MYDATAFROMPROVIDER) {
this.items = MYDATAFROMPROVIDER.myGetMethod();
}
And in your child
import { Component, Input } from '#angular/core';
import { INTERFACE } from './mydata/interface';
#Component({
selector: 'child-column',
template: `
<div *ngIf="item.id">{{item.id}}</div>
<div *ngIf="item.name">{{item.name}}</div>
<div *ngIf="item.address">{{item.address}}</div>
<div *ngIf="item.phone">{{item.phone}}</div>
`
})
export class ChildColumnComponent {
#Input() item: INTERFACE;
}
If you want to go deeper: Component Interaction

This guide is from official angular page Here
Here is the live sample of it
Not sure if these links could help.
But I actually worked on a project where we want to dynamically loading Child component into a Grid(Parent component).
And later on we can pass any component with different view inside the Grid
Guess that pretty close to your goal.

Related

Trigger child to update

Hi I'm new to Angular and any help would be great. The parent of my component has a method to generate different rules for each picker, but the rules also change dynamically from the parent so I need to make my picker component call the disableoption method and updating its own Options when the parent has been updated.
<picker-component *ngFor="let parameter of product.parameters" [parameter]="parameter" [disabledOptions]="disabledOptions(parameter)"></picker-component>
It's possible to inject the parent in the child (I'm not telling this is the best approach to your case, I'm just answering your question):
<parent> <!-- ParentComponent typescript class -->
<child></child> <!-- ChildComponent typescript class -->
</parent>
export class ChildComponent {
constructor(private _parentComponent: ParentComponent) {
this._parentComponent.disabledOptions(parameter);
}
}
In cases like this, a service injected in both ParentComponent and ChildComponent usually is a better and easy-to-implement approach though.

Use styling for ng-content from template component

I've created a template case component which I'm intending to use for multiple cases. To use the component as a template, I used ng-content select="".
It works fine, but not completely as desired. For example:
I have a div with a background image, its style is being configured inside of the template component:
<div class="front-image min-vh-100 min-vw-100" [style.transform]="'scale(' + scale + ')'">
</div>
To make this usable as a template, I replaced the given code with: <ng-content select=".front-image"></ng-content> and used the template inside of another component like this:
<app-case-template *ngIf="cases[3] as case">
<div class="front-image min-vh-100 min-vw-100" [ngStyle]="{ 'background-image': 'url(' + case.image + ')'}"
[style.transform]="'scale(' + scale + ')'">
</div>
</app-case-template>
How can I achieve the template to always get its styling from the template component - right now I had to declare its styling inside of the new component to get it working. Additionally [style.transform] stopped working.
Is there something like a bypass?
You may be going about this the wrong way. I'll try to outline a good way to do it. First, have a directory for your templates:
templates
- template.component.css (generic styling)
- template.component.html (your markup)
- template.component.ts (your ts/js)
Set up your template.ts for use:
import {Component} from '#angular/core';
#Component({
selector: 'template-component',
templateUrl: 'template.component.html'
styleUrls: ['./template.component.css']
})
export class TemplateComponent {
}
Register it in your component.module.ts (or whatever you may have called it):
...
import {TemplateComonent} from './templates/template.component';
...
const exportedComponents = [
SvgIconComponent
]
If needed, have your shared.module.ts export it:
import {ComponentsModule} from './components/components.module';
...
exports: [ComponentsModule]
Now you can use it like it's a custom HTML tag (your selector):
<template-component class="template"> </template-component>
Now it will always grab the css styling from itself, but you can stick a custom class in that tag as I have done, or call it like this from your main component's style page:
template-component {
margin: 10px;
background-color: grey;
}
There's a lot you can do including setting #Input() tags in your template that can be called from your HTML tag. You can also use Angular bindings to dynamically enter values or text into it. Many options here.
Note: instead of calling them templates, consider calling them shared components, or shared modals (if so). Have a directory called shared > components and have a custom-name directory with the like in there for organization.

Get details of object from unrelated element in angular

I am following the basic 'Tour of Heros' tutorial and sort of adding my own needed elements as I go (bootstrap, ng-bootstrap etc) and I want to grab the 'selected hero' from hero details when I reach it and put the name of the hero in a navbar component.
Like so, but obviously with a way to access the selected hero
<div *ngIf="selectedHero">
<li class="nav-item">
<div class="nav-link" routerLink="/detail" routerLinkActive="active">{{selectedHero.name}}</div>
</li>
</div>
My navbar is called by app.component.html above the routing outlet
<app-navbar></app-navbar>
<div class="container">
<router-outlet></router-outlet>
</div>
I have already looked up several questions related to this sort of thing but havnt really found anything made sense or worked when I tried it (I am assuming I am not doing them correctly or a similar issue)
I am new to angular and I feel like this sort of access is something I should know asap
I have seen 'emitters' and 'parent-child relationship' etc but not sure how to go about that with my navbar and the selected hero. The tutorial im following (that has all the code that im working with) is: https://angular.io/tutorial
Edit Ive also considered just calling the 'navbar' component within every other main component (as in, within 'hero-detail.component.html' above the actual information) but I think that goes against standards/repeating code?
The hero details component and the navbar component have no relationship, so to share data between them you simply need to create a shared service between them that can pass data back and forth like this:
selected-hero.service.ts
import {Injectable} from '#angular/core';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
#Injectable()
export class SelectedHeroService {
selectedHero = new BehaviorSubject<string>('Default Name Of Hero To Be Shown Goes Here');
selectedHeroObservable = this.selectedHero.asObservable();
changeSelectedHero(newHero:string):void{
this.selectedHero.next(newHero)
}
}
and then in your navbar component, you can read the selected hero like this:
navbar.component.ts
constructor(private sh: SelectedHeroService ) {
this.sh.selectedHeroObservable
.subscribe((hero) => {
//add your logic here!! for now I'm just gonna console log the selected hero
console.log(hero);
});
}
To set a new hero in you heroes details component you call this method:
hero-details.component.ts
changeSelectedHero(){
this.sh.changeSelectedHero('My New Selected Hero');
}
and don't forget to add the service in the provides arrays and both of the components in the declarations array of the same module so you don't get any errors. Also, don't forget to unsubscribe from the selectedHeroObservable to avoid memory leaks.
Component interaction in Angular could be simplified as i use angular at least to three ways: #ViewChild, EventEmitter (Output) or Input.
Viewchild is as it sound when you have a child component and you could set variable declaration (#) in the template to directly have access to methods on the child component.
Eventemitter is used in the child component when you want to notify the parent.
Input is used to set a property in a child.
A part from these three ways to communicate within components there is also services. I use this aproach when the components are too far from each other.
And to answer your question i would go with the service aproach. Have a look at subjects. Check out this plunker!!
export class MessageService {
private subject = new Subject<any>();
sendMessage(message: string) {
this.subject.next({ text: message });
}
clearMessage() {
this.subject.next();
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
http://plnkr.co/edit/FHIPt1?p=preview

Angular2/4 best way to change html content periodically

Unfortunately I have to change a html element's content in every X second because I have to show more data in such a little space... I didn't find any good example of change html elements periodically, maybe angular2 animations is a great deal for this but how should I solve the content change in every X time period?
Btw I have to change a button's content from a div to an other one with different style, elements...
Angular is a framework which binds your model to view in a declarative way using templates. All you have to do is update your model periodically and your template will automatically be updated for you.
For example, we can create a dummy observable which will emit every second, and then use the async pipe in the template to update it regularly.
#Component({
selector: 'my-app',
template: `Data: {{ data$ | async }}`,
})
export class AppComponent {
data$ = Observable.interval(400).mapTo(1).scan((a, b) => a + b, 0)
}
Here's a live demo.
Of course, details depend on the way you're receving your data and the way you want to display it, but the above example shows that it's very simple to change the HTML content periodically which was your question.

Pass data from nested Components Angular 2

I have a component where with a *ngFor print some boxes defined in an other component like this:
<div>
<commponentB *ngFor="let a of list"></componentB>
</div>
where "list" is a list ok object like this:
this.list=[{"id":"3","nome":"app3","posizione":3},{"id":"1","nome":"app1","posizione":1},{"id":"2","nome":"app2","posizione":2}];
I have to populate all the component. How can I transfer data from this two component?
TY
EDIT1:
the problem is that the list is splitted into 2 list for the repeat in 2 different bootstrap columns so the situation is this:
<div>
<commponentB *ngFor="let a of list1"></componentB>
</div>
<div>
<commponentB *ngFor="let a of list2"></componentB>
</div>
and the component is like that:
<div>
<span>{{name}}</span>
</div>
if I pass all the list I can't know how to populate the component at the right position (sorry if I don't explain the problem very well)
Since your components are directly linked, you can use the #Input() component interaction to send data from the parent to the child.
Here is a link from the official documentation :
https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding
If you can't do that model because of a router interaction, or components are too "far" away so it's too much work to setup several inputs, you can use a shared service to share the data from one to another component.
https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service
if you want to pass your list to componentB you can define Input() property in componentB and then pass your list
<div>
<commponentB [list]="list"></componentB>
</div>
export class ComponentB{
#Input() list: any[];
}
Update Ok maybe I'm not getting your issue, even if you have to pass different inputs to same component you can do that. Check out this plunker: