In previous angular version we had $scope.apply to detect changes , So i below code i have data from detailService that is printed now i am pushing data to object its throwing error object property is undefined , what is correct approach in new angular version to push data to array and bind it to the dom ?
app.component.ts
import { Component, OnInit,Pipe, PipeTransform, EventEmitter, Output } from '#angular/core';
import { DetailService } from '../detail.service';
import { StreamService } from '../stream.service';
import { MatTableDataSource } from '#angular/material';
import {GtConfig} from '#angular-generic-table/core';
import { GenericTableComponent} from '#angular-generic-table/core';
import * as io from 'socket.io-client';
export interface Element {
ticketNum: number;
ticketOpened: number;
eventType: string;
riskIndex: string;
riskValue: number;
severity: string;
lastModifiedDate: number;
assetID: string;
}
#Component({
selector: 'app-detail',
templateUrl: './detail.component.html',
styleUrls: ['./detail.component.css'],
})
export class DetailComponent{
messageArray: any[];
message1:Object = {};
public secondConfigObject: GtConfig<any>;
constructor(private detailService: DetailService) {
this.secondConfigObject = {
settings: this.getBaseSettings(),
fields: this.getBaseFields(),
data: []
};
};
ngOnInit() {
this.detailService.currentMessage1.subscribe(message1 => {
console.log('eventINDetailComp',message1);
this.secondConfigObject.data.push(message1);
});
}
}
app.component.html
<div class="table-responsive">
<generic-table [gtClasses]="'table-hover'" #myCustomTable [gtSettings]="secondConfigObject.settings" [gtFields]="secondConfigObject.fields" [gtData]="secondConfigObject.data"></generic-table>
</div>
You should move the code from the constructor to the start of the ngOnInit() function so the data gets set once the page has been created, not during.
As for data binding, variables on the screen/html will automatically update when they are changed in the code behind
Related
My Problem: I created a CardComponent acts like a card view with css and html and has a constructor.
I want to use it in a cards array (of its type). I use service to store the cards' data.
home comp. is using the service and loop over with ngFor, This is the code.. and below is the error I get...
Is there another way of using this so it will work?
card.component.ts:
import { Component } from '#angular/core';
#Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.css']
})
export class CardComponent{
imageSrc : string;
title : string;
constructor(imgSrc : string, title : string) {
this.imageSrc = imgSrc;
this.title = title;
}
ngOnInit(): void {
}
cards.service.ts:
import { CardComponent } from './card/card.component';
export class CardsService{
ctgryCards : CardComponent[] = [
new CardComponent("https://cdn3.iconfinder.com/data/icons/outline-amenities-icon-set/64/Beauty_Saloon-512.png", "Beauty"),
new CardComponent("https://www.pinclipart.com/picdir/middle/391-3917890_get-business-value-from-sustainable-data-electronics-icon.png", "Electronics")
];
getAllCtgryCards(){
return this.ctgryCards.slice();
}
}
home.component.ts:
import { Component, OnInit } from '#angular/core';
import { CardComponent } from '../card/card.component';
import { CardsService } from '../cards.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
categoryCards : CardComponent[] = [];
constructor(private cardsServ : CardsService) { }
ngOnInit(): void {
this.categoryCards = this.cardsServ.getAllCtgryCards();
}
}
home.component.html:
<app-card *ngFor = "let card of categoryCards"></app-card>
Error NG2003:
ERROR in src/app/card/card.component.ts:12:15 - error NG2003: No
suitable injection token for parameter 'imgSrc' of class
'CardComponent'. Found string
constructor(imgSrc : string, title : string) {
In card.component.ts:
Update the constructor to be
//import { Inject } from '#angular/core';
constructor(#Inject(String) private imgSrc : string, title : string)
OR
//import { Inject } from '#angular/core';
constructor(#Inject('imgSrc') private imgSrc : string, title : string)
According to https://angular-2-training-book.rangle.io/di/angular2/inject_and_injectable ; #Inject() is a manual mechanism for letting Angular know that a parameter must be injected. #Inject decorator is only needed for injecting primitives.
The primitive types are number, string, boolean, bigint, symbol, null, undefined.
I have a application with a table of cars:
This is my code:
Carcomponent.html
<tbody>
<tr *ngFor="let car of allCars; index as carId" \>
<td [routerLink]="['/cars', carId]">{{car.carId}}</td>
<td>{{car.brand}}</td>
<td>{{car.model}}</td>
<td>{{car.color}}</td>
<td>{{car.topSpeed }}</td>
</tr>
</tbody>
I have register the route like this:
{ path: 'cars/:carId', component: CardetailsComponent }
And this is my CarDetails.ts file:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { CarVM } from '../viewmodels/car-vm';
import { CarService } from '../services/car.service';
#Component({
selector: 'app-cardetails',
templateUrl: './cardetails.component.html',
styleUrls: ['./cardetails.component.css']
})
export class CardetailsComponent implements OnInit {
car: any;
carList: any;
constructor(private route: ActivatedRoute, private carservice: CarService) { }
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.car = params.get('carId');
});
}
getCarList() {
this.carList = new CarVM();
this.carservice.getCarById(this.carList.carId).subscribe((res: any) => {
this.carList = res.data;
console.log(this.carList)
})
}
}
And on my Cardetails.html I want to show the selected car like this:
<h2>Car Details</h2>
<div *ngIf="car">
<h3>{{ car.brand }}</h3>
<h4>{{ car.model }}</h4>
<p>{{ car.color }}</p>
</div>
The routing is working fine and fetching the cars is working. Now I want to select one car and see the brand, model, color on the next page. I use a viewmodel for this:
export class CarVM {
CarId: number;
Brand: string;
Model: string;
Color: string;
TopSpeed: number;
}
How can I see the selected car on the next page?
I have followed this tutorial:
https://angular.io/start/routing
Ok, you seem to be bit confused. In cardetails component you want to process carId from route parameters and use it to get car details. You can either get them from server, or have the service return already loaded details of all cars.
Let's say we are trying to make it happen getting the first way, it might look like this:
import { map, switchMap } from 'rxjs/operators';
ngOnInit() {
this.getCar();
}
private getCar(): void {
this.route.paramMap.pipe(
map(params => params.get('carId')),
switchMap(carId => {
return this.carservice.getCarById(carId);
})
).subscribe(
res => {
this.car = res;
console.log('#My car:', this.car);
}
);
}
First, you'll get the carId from route.paramMap, map it using rxjs map, then use switchMap to call you carservice.getCarById(carId) and have it return Observable to which you can subscribe. This should do the trick. Don't forget to properly map it/create CarVM object from it.
The problem is, you don't have CarVM object properly on CardetailsComponent. You are only getting carId into CarVM here: this.car = CarVM[+params.get('carId')];
First you need to create CarVM properly with your class variables. And the you can call your index.
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { CarVM } from '../viewmodels/car-vm';
#Component({
selector: 'app-cardetails',
templateUrl: './cardetails.component.html',
styleUrls: ['./cardetails.component.css']
})
export class CardetailsComponent implements OnInit {
car: any;
carList: any;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.car = params.get('carId');
});
}
getCarList(){
this.carList = new CarVM();
//call your service here to fill your carList variable and once you get car list, you will be able to access variable using with your index (this.car).
}
}
So, What I am trying to do seems like it would be trivial. And it probably is. But I can't figure it out. My question is:How can I pass a variable from #Input to a service in an Angular2 component? (Code has been simplified)
My component is as follows:
import { Component, Input } from '#angular/core';
import { CMSService } from '../cms.service';
#Component({
selector: 'cmstext',
templateUrl: './cmstext.component.html',
styleUrls: ['./cmstext.component.css']
})
export class CMSTextComponent {
constructor(private cms: CMSService) { }
#Input() id : string;
content = this.cms.getContent(this.id); // this.id is NULL so content is NULL
}
And then my service:
import { Injectable } from '#angular/core';
#Injectable()
export class CMSService {
constructor() { }
getContent(textId:string) : string {
this.text = textId; // textId is NULL so this.text returns NULL
return this.text;
}
}
My component template:
<p>id: {{id}}</p>
<p>Content: {{content}}</p>
When <cmstext id="4"></cmstext> is added to another component template the output is:
id: 4
content:
I'm just diving into Angular2 any help or suggestions would be greatly appreciated!
Just make it a setter and put the code there:
#Input()
set id(value : string) {
this.content = this.cms.getContent(value);
}
As pointed out by #Kris Hollenbeck,ngOnInit() was the answer. My final code looked like this. The component now passed the variable to the service.
import { Component, Input, OnInit } from '#angular/core';
import { CMSService } from '../cms.service';
#Component({
selector: 'cmstext',
templateUrl: './cmstext.component.html',
styleUrls: ['./cmstext.component.css']
})
export class CMSTextComponent implements OnInit {
public content : string;
#Input() id : string;
constructor(private cms: CMSService) { }
ngOnInit() {
this.content = this.cms.getContent(this.id);
}
}
This assigned the data from the service to the variable "content" and the id passed from the element attribute to the variable "id". Both variables were then accessible to the template!
I have two page components that use the same methods with the exception of using two different type classes. The two components are called Services and Users. Both components use templates that are very similar with the exception of the class property info it displays. It seems to be inefficient to repeat methods on both controllers, is there a way to combine/share controllers.
Services.ts
import { Component } from '#angular/core';
import { CORE_DIRECTIVES } from '#angular/common';
const template = require('./service.component.html');
const style = require('./service.component.css');
interface Service {
id: number;
name: string;
summary: string;
path: string;
};
#Component({
selector: 'admin-services',
directives: [ CORE_DIRECTIVES],
template: template,
styles: [ style ]
})
export class ServiceComponent {
services = Services;
selectedService:Service ;
constructor() {
}
onselect(service:Service){
this.selectedService = service ;
}
onEdit(service:Service){
console.log("Edit: "+service);
}
onDelete(service:Service){
console.log("Delete: "+service);
}
onView(service:Service){
console.log("View: "+service);
}
onAdd(){
this.selectedService = <Service>{};
}
}
User.ts
import { Component } from '#angular/core';
import { CORE_DIRECTIVES } from '#angular/common';
const template = require('./users.component.html');
const style = require('./users.component.css');
interface User {
id: number;
image: string;
name: string;
email: string;
role: string;
};
#Component({
selector: 'admin-users',
directives: [ CORE_DIRECTIVES],
template: template,
styles: [ style ]
})
export class UsersComponent {
users = Users;
selectedUser:User ;
constructor() {
}
onselect(user:User){
this.selectedUser = user ;
}
onEdit(user:User){
console.log("Edit: "+user);
}
onDelete(user:User){
console.log("Delete: "+user);
}
onView(user:User){
console.log("View: "+user);
}
onAdd(){
this.selectedUser = <User>{};
}
}
Yep, this is where Angular's component-driven design and Typescripts's class-driven design really shine:
Having defined a ServicesComponent as you have above, you can simply extend that class and attach different component metadata to it:
#Component({
selector: 'admin-users',
directives: [ CORE_DIRECTIVES],
template: template,
styles: [ style ]
})
export class UsersComponent extends ServicesComponent {
constructor(){
super();
}
//override whatever methods/fields in the parent class you need to (and only those)
}
I believe you can create a service with a single set of methods and pass in an object. Then cast the object to the desired class and use it in the method.
I would like to pass a string parameter to my component. Depending on passing parameter i will pass different parameters for services in my component. I do next: In index.html call my component, passing parameter.
<top [mode]="tree">Loading...</top>
In my component i include Input from angular2/core
import {Input, Component, OnInit} from 'angular2/core';
In my component`s class i declare an input
#Input() mode: string;
And with console.log() i try to catch my passing parameter of 'tree', but it`s undefined.
console.log(this, this.mode);
The full code of a component file:
import {Http, HTTP_PROVIDERS} from 'angular2/http';
import {Input, Component, OnInit} from 'angular2/core';
import {ParticipantService} from '../services/participant.service';
import {orderBy} from '../pipes/orderby.pipe';
#Component({
selector: 'top',
templateUrl: 'dev/templates/top.html',
pipes: [orderBy],
providers: [HTTP_PROVIDERS, ParticipantService]
})
export class AppTopComponent implements OnInit {
constructor (private _participantService: ParticipantService) {}
errorMessage: string;
participants: any[];
#Input() mode: string;
ngOnInit() {
console.log(this, this.mode);
this.getParticipants('top3');
var self = this;
setInterval(function() {
self.getParticipants('top3');
}, 3000);
}
getParticipants(public mode: string) {
this._participantService.getParticipants(mode)
.then(
participants => this.participants = participants,
error => this.errorMessage = <any>error
);
}
}
When you use [...], the value you provide corresponds to an expression that can be evaluated.
So tree must be something that exists in the parent component and correspond to a string.
If you want to use the string tree, use this:
<top mode="tree">Loading...</top>
You can notice that such parameters can't be used for root component. See this question for more details:
Angular 2 input parameters on root directive
As a workaround for the limitation Thierry explained you can use
constructor(private _participantService: ParticipantService,
elRef:ElementRef) {
this.mode=elRef.nativeElement.getAttribute('mode');
}
you need to wait until template is bound to DOM. in order to do this, you have to implement AfterViewInit
export class AppTopComponent implements AfterViewInit{
public ngAfterViewInit() {
console.log(this, this.mode);
this.getParticipants('top3');
var self = this;
setInterval(function() {
self.getParticipants('top3');
}, 3000);
}
}