Material Design Lite tool tip issue in Angular 2 - html

I've researched and read everything I can find on the issue I'm having, but I can't seem to get any of the proposed solutions to work. I'm building an application using Angular 2 and MDL that takes customer orders and displays them on MDL cards. Sometimes orders have errors, which I want to display on hover utilizing the MDL tooltip, but it won't work for me. The application is set up to make an API call to fetch incoming orders and then display them using the MDL card template using *ngFor. All that works great.
Here's the portion of my template that contains the MDL tooltip:
<div id="id{{order.order_id}}" class="error-icon-div">
<div class="icon material-icons error-icon" *ngIf="order.error">
error
</div>
</div>
<div class="mdl-tooltip mdl-tooltip--large" attr.data-mdl-
for="id{{order.order_id}}">
{{order.error}}
</div>
Since you can't use the same ID more than once, I'm generating a unique ID for each card/order by taking the string 'id' and adding the order ID. I know this works because I can see it when I inspect the elements in the browser's dev tools.
I read that I should set each component's encapsulation to none like this:
encapsulation: ViewEncapsulation.none
I've done that. I also read that I should use the componentHandler to upgrade the DOM, which I've tried two different ways.
First I tried it like this:
ngAfterViewInit() {
componentHandler.upgradeDom();
};
I also tried it like this:
ngAfterViewInit() {
componentHandler.upgradeAllRegistered();
};
None of this has worked.
I discovered that if I simply set the id equal to a string instead of doing it dynamically with Angular that it works, but then I encounter the issue of how to generate a unique ID for every order/card.
I'm wondering if this is some sort of timing issue, and that even though when inspecting the element in dev tools I see the id rendered the way I expect it to be, MDL doesn't see it that way. I tried using setTimeout to delay ngAfterViewInit() from being called a second or two, but to no avail.
Any help would be greatly appreciated.

I've gotten MDL to work with Angular2. I had to set it in the ngAfterViewChecked event.
In App.Component.ts:
import $ from 'jquery';
import { Component, AfterViewChecked, ViewEncapsulation, Inject } from '#angular/core';
import { AccountService } from '../services/account.service';
import { User } from '../models/user.model';
import 'material';
#Component({
selector: 'my-app',
templateUrl: 'app/components/app.component.html',
styleUrls: ['...', 'dist/vendors.min.css'],
encapsulation: ViewEncapsulation.None //for ui framework to work normally
})
export class AppComponent implements AfterViewChecked {
constructor(
private accountService: AccountService,
#Inject('User') public user : User ) {
}
ngAfterViewChecked() { //need to start ui frameworks quite late.
componentHandler.upgradeAllRegistered();
}
}

Related

Angular - Create HTML Elements and Angular Components dynamically

I am currently working on an Angular 10 project where I have to add native HTML-elements like div, table, p, h1 to the DOM (this could be achieved in Angular with the DomSanitizer) from the TypeScript class. But I also want to create an Angular component inside an created div.
Because creating just one component is easy with the ComponentFactoryResolver.
For example, this should be created from Typescript dynamically:
<div>
<my-component></my-component>
</div>
How can I achieve this?
Kind regards,
Steve
I think is this what you're looking for:
import {Component} from '#angular/core';
#Component({
selector: 'example',
templateUrl: '<my-component></my-component>',
styleUrls: ['./example.component.scss']
})
export class ExampleComponent {
}
You don't need <div> unless you use him for style or other thing.

Why are the curly braces in my Angular app's html code not compiling what is inside of them?

I've just started my adventure with Angular and I've faced a problem. I know that you can create a property in app.components.ts and display it in html code by putting its name into curly braces and it should output the property on the wesite In my case it just doesn't work and I'a actually clueless. That is my app.components.ts:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'AngularCV';
}
There is a property named "title" that I want to use in my html code. My app.component.html:
<div>
{{title}}
</div>
After running live server the browser displays exactly what I typed without compiling it, so
"{{title}}" is what I see on the webpage, instead of "AngularCV" I searched through similar problems, but nothing worked. Maybe it is trivial, but I am stumped right now.
All right, I just created a brand new angular app, and modified my current html files to match those created in the brand new app. Everything works as it should. As far as I undestand this, the src/index.html should contain just head and empty body with just mark. And all the stuff but without the head should be put into app.component.html. Hope that helps someone in the future.

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

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.

How to dispaly diffrent background color each page with Angular 5?

I am new to Angular, I need to change the background-color of the body tag. I created a project in Angular with multiple pages.
Ex:
Login / Home / About / Service / Contact
I need to display a different background-color on each page.
But here actually, the body tag is common for all pages so I'm unable to change the `background-color in CSS?
you could try changing it during the Component ngOnInit lifecycle,
something like this
ngOnInit(){
document.body.style.backgroundColor = 'red';
}
inside your component code
Or you could also try changing ViewEncapsulation, but it will change it for all the component styles, so it could lend you to unexpected things.
Something like this
#Component({
selector: "app-theme",
templateUrl: "./theme.component.html",
encapsulation: ViewEncapsulation.None,
styleUrls: ['./theme.component.scss']
})
As you asked in your comment, you want to edit body classes as well, so I think you're going for the first option I wrote.
It could be different based on how many classes you want to handle. I'll write down the example for just one class
ngOnInit(){
const body = document.body;
body.className = '';
body.classList.add('classname');
}
If you need to handle more classes, you will need to edit your code, to remove just the classes you don't need, instead of all of them

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