Angular 2 modify view html after processing - javascript

I have below angular 2 code
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
template: 'Waiting on port ',
})
export class AppComponent {
}
As an example I would like to append text "3000" to the template output dynamically. How can this be achieved?
So, final output must be "Waiting on port 3000"
EDIT: I should have been a bit more specific. I was expecting answer something like a response object where I could modify the html before it is sent to "frontend" rendering. So, Angular 2 would process binding all the details in the template and then I get the modify the html.

#Component({
selector: 'app-root',
template: 'Waiting on port {{port}}',
})
export class AppComponent {
port:number;
someMethod() {
this.port = 3000;
}
}

Further to Günter Zöchbauer's answer, if you wanted the method to fire when the component's initialized you could use ngOnInit as your method, "called after data-bound properties of a directive are initialized" (from the docs).
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-root',
template: 'Waiting on port {{port}}',
})
export class AppComponent implements OnInit {
port:number;
ngOnInit(): void {
this.port = 3000;
};
}
OnInit must be included in your import.

Related

getting ExpressionChangedAfterItHasBeenCheckedError Angular 4

Im aware similar questions exist but none of those have provided me with an answer that works..
Basically I have a site with some services that inject data dynamically
In my app.component.ts I have two headers.. one when your on the home page and one for when your on any other page
app.component.html
<app-header *ngIf="router.url !== '/'"></app-header>
<app-header-home *ngIf="router.url != '/'"></app-header-home>
<router-outlet></router-outlet>
<app-footer></app-footer>
app.component.ts
import { Component } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'app';
router: string;
constructor(
private _router: Router
) {
this.router = _router.url;
}
}
now I also have a service that dynamically injects the title of the header
headerTitle.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class HeaderTitleService {
title = new BehaviorSubject('');
constructor() { }
setTitle(title: any) {
this.title.next(title);
}
}
then In my home component for example I set the title
home.component.ts
import { Component, OnInit, AfterViewInit } from '#angular/core';
import { HeaderTitleService } from '../../services/headerTitle.service';
import { HeaderImageService } from '../../services/headerImage.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor(
private headerTitleService: HeaderTitleService,
private headerImageService: HeaderImageService
) { }
ngOnInit() {
}
ngAfterViewInit() {
this.headerTitleService.setTitle(`
We strive to create things
<br> that are engaging, progressive
<br> & above all
<span class="highlight">
<em>innovative.</em>
</span>
`);
}
}
now basically it was all working until I put in the if statements on the two headers
now Im getting this error
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ''. Current value: '
We strive to create things
<br> that are engaging, progressive
<br> & above all
<span class="highlight">
<em>innovative.</em>
</span>
'.
not sure how I can fix this.. I tried setting the values in ngAfterViewInit but it did nothing
or does anyone know another way I could accomplish this??
Thanks
You can try using a setTimeOut method instead and set the values
inside of that
setTimeout(this.headerTitleService.setTitle(`
We strive to create things
<br> that are engaging, progressive
<br> & above all
<span class="highlight">
<em>innovative.</em>
</span>
`), 0);
note this is a work around and not a full proff solution to the problem .
To know why this error occurs in Angular change detection you need to know how the change detection works in Angular for this you can refer to this blog by Maxim NgWizard K
I know i fixed this in mine.
here is a great post
everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror
i have forced the change detection
export class AppComponent {
name = 'I am A component';
text = 'A message for the child component';
constructor(private cd: ChangeDetectorRef) {
}
ngAfterViewInit() {
this.cd.detectChanges();
}

Angular 2 Failed to compile

i created a new component in angular 2 with this:
ng g component todos
So it created the new component, I went to the component and I noted that I had a new folder with the files:
todos.component.css, todos.component.html, todos.component.spec.ts, todos.component.ts
Then I openened todos.component.ts and it had:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.css']
})
export class TodosComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
Then I put the new second line because I am learning with a tutorial:
import { Component, OnInit } from '#angular/core';
import { TodosComponent } from './todos/todos.component';
#Component({
selector: 'app-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.css']
})
export class TodosComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
When I did that and I ran the server it showed me this:
Failed to compile.
C:/angular2/proyecto/src/app/todos/todos.component.ts (2,10): Individual declarations in merged declaration 'TodosComponent' must be all exported or all local.
I'd like to know what is it bad? why does it show that error?
Thanks!
You are importing the class into it's own file.
No need to import your own component, you should import it in other files, where you use it.

How to send data from one component to another using a shared service

I wanted to send data using subject to another component (for a earning purpose). I am not able to fetch back the data. Here is my code:
app.component.ts
import { Component } from '#angular/core';
import { shareService } from './share.service';
#Component({
selector: 'my-app',
template: `
<hello></hello>
<button (click)="passData()">
Start
</button>
`,
styleUrls: [ './app.component.css' ],
providers:[shareService]
})
export class AppComponent {
constructor(private service : shareService){}
passData(){
this.service.send("hello");
}
}
hello.component.ts
import { Component, Input } from '#angular/core';
import { shareService } from './share.service';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'hello',
template: `<h1>Hello!</h1>`,
styles: [`h1 { font-family: Lato; }`],
providers:[shareService]
})
export class HelloComponent {
subscription: Subscription;
constructor(private share : shareService){
this.subscription = share.subj$.subscribe(val=>{
console.log(val);
})
}
}
share.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class shareService{
private sub = new Subject();
subj$ = this.sub.asObservable();
send(value: string) {
this.sub.next(value);
}
}
I am not getting the value in console.
Here is the working Demo : DEMO
By putting:
#Component({
.....
providers: [sharedService]
})
in both components, you are creating two distinct instances of the shared service.
Each instance is not 'aware' of the data from each component.
Provide it at module level and create a singleton service:
#NgModule({
....
providers: [sharedService]
})
This way, you inject the service as a single instance in the both components, so they can share it as they will share the data.
Or using the Angular's preferred new way :
Beginning with Angular 6.0, the preferred way to create a singleton
service is to specify on the service that it should be provided in the
application root. This is done by setting providedIn to root on the
service's #Injectable decorator:
#Injectable({
providedIn: 'root',
})
Demo
See also
I dont know why sub$ is used but you dont need that
// just push data to subject. you can use BehavourSubject to initiatte a value.
#Injectable()
export class shareService{
private sub = new Subject();
confirmMission(astronaut: string) {
this.sub.next(astronaut);
}
}
And then in your 2nd component sub scribe it
#Component({
selector: 'hello',
template: `<h1>Hello!</h1>`,
styles: [`h1 { font-family: Lato; }`],
providers:[shareService] // this can be shared in module lebel or componenet level
})
export class HelloComponent {
subscription: Subscription;
constructor(private share : shareService){
this.subscription = share.subj.subscribe(val=>{
console.log(val);
})
}
}
make sure to provide your service in module level or provide it in both the component.

500 when calling setTitle using angular 2 titleService and server side rendering

I have a problem with server side rendering using angular 2 and titleService.
My code looks like this
import { Component } from '#angular/core';
import { BrowserModule, Title } from '#angular/platform-browser';
import { Router } from '#angular/router';
#Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [Title]
})
export class AppComponent {
constructor(private titleService: Title) {
titleService.setTitle("Setting the title...");
}
}
This works fine using client side rendering but when reloading the page I get this error:
Exception: Call to Node module failed with error: TypeError: Cannot create property 'title' on string ''
Any ideas why this occurs?
With angular universal there should be no need to provide any external service as this is built in. (as echonax stated in the comments.)
Working example with this angular-universal fork. I guess it should be the same for your version of angular-universal.
app.component.ts
import { Component, OnInit } from '#angular/core';
import { Router, NavigationEnd } from '#angular/router';
import { Meta, Title } from '#angular/platform-browser';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
constructor(private _router: Router, private _meta: Meta, private _title: Title) { }
ngOnInit() {
this._router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
switch (event.urlAfterRedirects) {
case '/':
this._title.setTitle('title goes here');
this._meta.updateTag({ name: 'description', content: 'same goes for meta content' });
break;
case '/another-route':
this._title.setTitle('Another title');
this._meta.updateTag({ name: 'description', content: 'You get the idea' });
break;
}
}
});
}
}
NavigationEnd takes care of setting a new title each time I navigate to a new route.
Hope it helps.
I guess this might be expected since the titleService interacts with element only present in the browser. When reading the "Universal Gotchas" it clearly status that you need to check if you are on the client or in the browser when doing this. I expected the titleService to handle such things though. However checking if client solved the problem.
Se: https://github.com/angular/universal

Using input/output events to trigger methods in a parent component in Angular 2

How can a child service notify a parent component of a change? I used to do this in angular 1 by $watching a variable in the child service. Unfortunately, this is no longer possible.
I tried injecting the service back into the component, but this fails, probably due to circular dependencies. Based on what I could find in current documentation, I came up with the code below:
AppComponent
|
SomeComponent
|
SomeService
AppComponent
#Component({
selector: '[app-component]',
templateUrl: 'partials/app.html',
directives: [
SomeComponent
],
providers: [
SomeService
]
})
export class AppComponent {
constructor() { }
}
bootstrap(AppComponent);
SomeComponent
import {Component, Input} from 'angular2/core'
import {SomeService} from '../services/some.service'
#Component({
selector: 'foo',
templateUrl: 'partials/foo.html'
})
export class SomeComponent {
constructor() {}
#Input set someEvent(value) {
console.log(value);
}
}
SomeService
import {EventEmitter, Output} from 'angular2/core'
export class CoreService {
constructor() {
this.someEvent = new EventEmitter();
}
#Output() someEvent: EventEmitter<any>;
public foo() {
this.someEvent.emit(true); // Or next(true)?
}
}
#Output must be used for components only not in services. At this level you can register on this event using the (...) syntax.
From the angular.io documentation (https://angular.io/docs/ts/latest/api/core/Output-var.html):
Declares an event-bound output property.
When an output property emits an event, an event handler attached to that event the template is invoked.
For a service you need to explicitly subscribe on this event, as described below:
import {Component, Input} from 'angular2/core'
import {SomeService} from '../services/some.service'
#Component({
selector: 'foo',
templateUrl: 'partials/foo.html'
})
export class SomeComponent {
constructor(service:CoreService) {
service.someEvent.subscribe((val) => {
console.log(value);
});
}
}

Categories