I currently have this code in my app.component.ts
app.component.html
<div [ngClass]="myclass">
...rest of the content here
</div>
This I have the this:
<button (click)="changeClass('myFavClass')">Change Class to myFavClass</div>
app.component.ts
export class AppComponent {
myclass: string;
changeClass(myclass) {
this.myclass = myclass;
}
}
Now, all this works fine BUT I now want to put the triggering button on another component.
If I put this on another component:
<button (click)="changeClass('myFavClass')">Change Class to myFavClass</div>
How can I get it to change the class?
There are two ways you can do this you can use output with an EventEmit
Or you can set up a service that monitors the changes to a variable and use that as the control point for the change.
Personally, I use services for this instance as its easier to manage the code and its flow.
This answer has all the code in you need to look at.
Changing a value in two different components at the same time Angular 2
Hope that helps
There are at least two options. Subject and Observable or if this another component is a parent you can use #Input.
Subject and Observable method:
angular guide Highly recommended to read whole page.
Some component
export class SomeComponent {
constructor(private ClassService: ClassService) { }
private changeClass(class) {
this.ClassService.changeClass(class);
}
}
Another Component
export class AnotherComponent implements OnInit, OnDestroy {
constructor(private ClassService: ClassService) { }
private class: string = "";
private subscribtion: Subscribtion;
ngOnInit(): void {
this.Subscribtion = this.ClassService.someClass$.subscribe(
(class) => { this.class = class; }
)
}
ngOnDestroy(): void {
this.Subscribtion.unsubscribe();
}
}
Service
#Injectable();
export class ClassService{
constructor() { }
private someClassSource= new Subject<string>();
someClass$= this.someClassSource.asObservable();
changeClass(class) {
this.someClassSource.next(class);
}
}
taken from my answer
#Input method:
angular guide
This is very simple, when you click button changeClass method will change elClass which will be passed to another component by #Input decorator, every change of #Input will cause a detect changes which will detect that value has changed so class will change to myClass.
Parent component
parent.component.html
<another-component [elementClass]="elClass"></another-component>
<button (click)="changeClass('myClass')">change class<button>
parent.component.ts
export class ParentComponnet {
private elClass: string = "";
changeClass(class: string) {
elClass = class;
}
}
Another component (must be child component)
another.component.html
<div [ngClass]="elementClass">
another.component.ts
export class AnotherComponent {
#Input() elementClass: string;
}
There is also Child to Parent interaction via #Output (emitting event) angular guide
Related
I like to explore the feasibility with using component Input as Event output, similar to how react does it.
Parent component
parentHandler(message){
console.log(message)
}
Parent html
<child [myEvent]="parentHanlder"></child>
Child component
#Input myEvent
ngOnInit(){
this.myEvent('hello')
}
I am wondering if there are disadvantage or potential issue with this approach e.g memory/performance
thank you
This will work perfectly as expected.
The behavior will be similar to #Output except the context of execution.
The scope of execution will be with child component
export class AppComponent {
version: number = 7;
messageMe(param){
alert("Message Me.."+param+this.version);
}
}
<framework
[messageHandler]="messageMe"></framework>
export class FrameworkComponent implements OnInit {
#Input()
messageHandler: Function;
name="Jone";
ngOnInit() {
this.messageHandler(this.name);
}
}
You can use setter in this situation
Parent:
<child [myEvent]="parentHanlder"></child>
Child (ts):
#Input()
set myEvent(parentHandler: string) {
this.myEvent('hello');
}
I have an angular 6 application, which has a top bar and a content area below this. These are different component and I am currently developing the user profile page. Name of the user is also displayed in the top bar.
My problem is like whenever I have updated the user's name in EditUser page, it successfully saves, but the same is not updated on the top bar. In Vue.js, I can simply handle this with a Vuex store; but how can I handle this in Angular 6.
Any help would be much appreciated.
Next time post a bit of code. Since there isn't, I'll show you how to do it with an example.
Let's assume we have two component, A and B. And the changes will be reflected on both of two components.
Service :
export class YourService{
private data$ = new BehaviorSubject<string>(null);
data = this.data$.asObservable();
public setData(data: string){
this.data$.next(data);
}
}
Component A/B.html :
<div>{{something}}</div>
Component A/B.ts :
isAlive = true;
something: string;
constructor(
private service: YourService
) { }
ngOnInit(){
this.service.data
.takeWhile(() => this.isAlive)
.subscribe( res => {
if(!!res){
this.something = res;
}
});
}
ngOnDestroy(){
this.isAlive = false;
}
Component that change the status:
export class AnotherComponent{
constructor(
private service: YourService
) { }
private changeData(data: string){
this.service.setData(data);
}
}
Now everything is working fine. BehaviorSubject allow the communication between components. whenever the function changeData is fired, you will see the changes on both of your components.
takeWhile is for unsubscribing when the component die.
If you have more question, feel free to ask them to me and I will edit this answer.
You can create service to exchange data between components. It could be UserService that provide access to current user information.
#Injectable()
export class UserService {
user: UserInfo;
// define user here (load from backend or somehow else)
}
In user-profile.component.ts
export class UserProfileComponent {
constructor(public userService: UserService) { }
}
user-profile.component.html
<input type="text" [(ngModel)]="userService.user.name">
In header.component.ts
export class HeaderComponent {
constructor(public userService: UserService) { }
}
header.component.html
<span>{{ userService.user.name }}</span>
So the anggular DI will create a singleton UserService and injects the same object to both components. And when you change it in any of them the changes will be shown in other.
I'm trying to figure out how to bind a view child to a child component of a class inside of my view.
I have a models that emulates binary expression:
export interface IODataExpression{
}
export class ODataExpressionDescriptor implements IODataExpression{
property: ODataProperty;
selectedFunction: ODataFunctionDescriptor;
value: any;
isNegated: boolean = false;
}
export class ODataBinaryExpressionDescriptor implements IODataExpression{
left: IODataExpression;
right: IODataExpression;
operator: ODataBinaryOperators;
}
I have a component class which looks like so:
binaryExpression: ODataBinaryExpressionDescriptor = new ODataBinaryExpressionDescriptor();
binaryOperatorKeys: any;
binaryOperators = ODataBinaryOperators;
#ViewChild('left') leftExpression: OdataSimpleFilterComponent;
the left property points to a component which internally has a property:
odataExpression: ODataExpressionDescriptor = new ODataExpressionDescriptor();
How can I make it so that the binaryExpression.left always equals the view childs leftExpression.odataExpression?
Use an EventEmitter.
In OdataSimpleFilterComponent
#Output() odataExpressionChange = new EventEmitter<ODataExpressionDescriptor>();
Then, whenever tha value changes internally in the component, you do:
this.odataExpressionChange.emit(this.odataExpression);
In the main component, you'll have to do in ngAfterViewInit (or ngAfterViewChecked), to make sure that leftExpression is initialised:
ngAfterViewInit() {
leftExpression.odataExpressionChange.subscribe(data => {
this.binaryExpression.left = data;
}
}
This way, whenever the value changes in the child component, you'll receive a notification (via the subsrcription to the EventEmitter) and can react accordingly.
Of course some details might change, as I can't know all of the details of your implementation.
I have created Angular2 + Typescript project. I have there alot of tables so I want to have something like base component for that.
There is my base component:
export abstract class ManagementComponent<T> implements BaseComponent {
protected selectedItem: T;
protected items: Array<T>;
}
Now there is my child component. I would like to get all items from http and then assign it into base class
export class CompetencesComponent extends ManagementComponent<Competence> implements OnInit {
thisField: string;
constructor(private service: CompetencesService) {
super();
}
ngOnInit(): void {
this.getCompetences();
}
private getCompetences() {
this.service.getCompetences().subscribe(function (competences: Array<Competence>) {
this.thisField // ok
this.items // not ok
})
}
}
Any idea how I can access base fields from subscribe methods?
Currently I'd expect that you wouldn't be able to reference either thisField or items, because you should be losing the this context inside your subscription function.
You can switch to an arrow function to retain context:
this.service.getCompetences().subscribe((competences: Array<Competence>) => { ... }
You can set list of competencies to parent class as follow:
private getCompetences() {
var self = this;
this.service.getCompetences().subscribe(function (competences: Array<Competence>) {
this.thisField // ok
self.items = competences; // not ok
})
}
The reason you are unable to access items property through this binding is the scope. Inside callback this binding is bound to something else and you loose the context.
I'm using angular 2. I have a component with an input.
I want to be able to write some code when the input value changes.
The binding is working, and if the data is changed (from outside the component) I can see that there is change in the dom.
#Component({
selector: 'test'
})
#View({
template: `
<div>data.somevalue={{data.somevalue}}</div>`
})
export class MyComponent {
_data: Data;
#Input()
set data(value: Data) {
this.data = value;
}
get data() {
return this._data;
}
constructor() {
}
dataChagedListener(param) {
// listen to changes of _data object and do something...
}
}
You could use the lifecycle hook ngOnChanges:
export class MyComponent {
_data: Data;
#Input()
set data(value: Data) {
this.data = value;
}
get data() {
return this._data;
}
constructor() {
}
ngOnChanges([propName: string]: SimpleChange) {
// listen to changes of _data object and do something...
}
}
This hook is triggered when:
if any bindings have changed
See these links for more details:
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html
As mentioned in the comments of Thierry Templier's answer, ngOnChanges lifecycle hook can only detect changes to primitives. I found that by using ngDoCheck instead, you are able to check the state of the object manually to determine if the object's members have changed:
A full Plunker can be found here. But here's the important part:
import { Component, Input } from '#angular/core';
#Component({
selector: 'listener',
template: `
<div style="background-color:#f2f2f2">
<h3>Listener</h3>
<p>{{primitive}}</p>
<p>{{objectOne.foo}}</p>
<p>{{objectTwo.foo.bar}}</p>
<ul>
<li *ngFor="let item of log">{{item}}</li>
</ul>
</div>
`
})
export class ListenerComponent {
#Input() protected primitive;
#Input() protected objectOne;
#Input() protected objectTwo;
protected currentPrimitive;
protected currentObjectOne;
protected currentObjectTwo;
protected log = ['Started'];
ngOnInit() {
this.getCurrentObjectState();
}
getCurrentObjectState() {
this.currentPrimitive = this.primitive;
this.currentObjectOne = _.clone(this.objectOne);
this.currentObjectTwoJSON = JSON.stringify(this.objectTwo);
}
ngOnChanges() {
this.log.push('OnChages Fired.')
}
ngDoCheck() {
this.log.push('DoCheck Fired.');
if (!_.isEqual(this.currentPrimitive, this.primitive)){
this.log.push('A change in Primitive\'s state has occurred:');
this.log.push('Primitive\'s new value:' + this.primitive);
}
if(!_.isEqual(this.currentObjectOne, this.objectOne)){
this.log.push('A change in objectOne\'s state has occurred:');
this.log.push('objectOne.foo\'s new value:' + this.objectOne.foo);
}
if(this.currentObjectTwoJSON != JSON.stringify(this.objectTwo)){
this.log.push('A change in objectTwo\'s state has occurred:');
this.log.push('objectTwo.foo.bar\'s new value:' + this.objectTwo.foo.bar);
}
if(!_.isEqual(this.currentPrimitive, this.primitive) || !_.isEqual(this.currentObjectOne, this.objectOne) || this.currentObjectTwoJSON != JSON.stringify(this.objectTwo)) {
this.getCurrentObjectState();
}
}
It should be noted that the Angular documentation provides this caution about using ngDoCheck:
While the ngDoCheck hook can detect when the hero's name has changed,
it has a frightful cost. This hook is called with enormous frequency —
after every change detection cycle no matter where the change
occurred. It's called over twenty times in this example before the
user can do anything.
Most of these initial checks are triggered by Angular's first
rendering of unrelated data elsewhere on the page. Mere mousing into
another input box triggers a call. Relatively few calls reveal actual
changes to pertinent data. Clearly our implementation must be very
lightweight or the user experience will suffer.