0

Recently I started learning Angular but I faced an issue today. I have the following Component:

import { Component, OnInit } from '@angular/core';
import { SharedService } from '../home/shared.service';
import { IData } from '../data/IData';

@Component({
  selector: 'app-current-article',
  templateUrl: './current-article.component.html',
  styleUrls: ['./current-article.component.css']
})
export class CurrentArticleComponent implements OnInit {

  data: IData;
  symbols: number = 150;
  showReadMore = true;
  contentToShow: string = "";

  constructor(private _sharedService: SharedService) { }

  ngOnInit() {
    this.data = this._sharedService.sharedData;
    this.contentToShow = this.data.content;
  }

  readMore() {
    //this.symbols += 100;
  }
}

The "data" property is an object with the following properties: title (string), authorId (number) and content (string).

I have also followed the solution of this post and made this service:

import { Injectable } from '@angular/core';
import { IData } from '../data/IData';

@Injectable()
export class SharedService{

    sharedData: IData = {
        title: "",
        authorID: 0,
        content: ""
    };

    insertData(data: IData){
        this.sharedData.title = data.title;
        this.sharedData.authorID = data.authorID;
        this.sharedData.content = data.content;
    }
}

And I have view for the Component as well:

<div *ngIf="data.title">
    <h1>{{ data.title }}</h1>
    <h5>Author ID: {{ data.authorID }}</h5>
    <div>
        <span>{{ contentToShow }} </span>
        <span>
            <a href="#" *ngIf="showReadMore" (click)="readMore()">Read More &#8618;</a>
        </span>
    </div>
</div>

Here comes my problem. The "contentToShow" property seems to be undefined and I find this strange... However if I try to use "data.content" it works just fine and displays my data. What is happening? I am tring to resolve it for 2 hours and I am starting to think it must be something really obvious but I do not know what... Thank you!

4 Answers4

3

At the point you assign the value of data.content to contentToShow the property isn't set, so contentToShow seems not to be set. You need to know that Javascript works with "call-by-reference", but it works only with Arrays or Objects and not with Primitives (like string, number, etc).

Because of that you see some value, when you're using data.content. Because you access the property directly from the object and the changed value is displayed in the html.

For better understand this post Is JavaScript a pass-by-reference or pass-by-value language? can help.

Two ways to solve your problem can be: You can use data.content or you can notify your component that the values have changed, e.g. by using an Observable.

The last can look something like this:

@Injectable()
export class SharedService{
    notfiySubject: Subject< IData > = new Subject();

    ...
    insertData(data: IData){
        this.sharedData.title = data.title;
        this.sharedData.authorID = data.authorID;
        this.sharedData.content = data.content;
        // Emits the subject
        this.notifySubject.next(this.sharedData);
    }
}

And in you component you would subscribe to that Subject:

this._sharedService.notifySubject.subscribe(data => {
    // Here you can set `contentToShow` with data or _sharedServices.sharedData
});
Batajus
  • 5,831
  • 3
  • 25
  • 38
0

The problem is that your "contentToShow" is a primitive type (string) and it is initialized in ngOnInit (and only there). Once you update sharedData it is updated correctly but the connection between contentToShow and this.data.content is lost. Actually it wasn't there.

So this will never work:

<span>{{ contentToShow }} </span>

Instead you must keep the reference to the object from your dataService like this:

<span>{{ data.content }} </span>

UPDATE: If you still want to customize your output using "contentToShow" then you must get notified about changes. One way to do it is to have a Subject in your "dataService" like this:

dataChanged = new Subject<any>();

Once this subject is in place you can update your "insertData" method with this as a last line:

    this.sharedData.content = data.content;
    this.dataChanged.next(); // <-- this will send the notification
}

Inside your "CurrentArticleComponent" (ngOnInit) you need to subscribe to this notifications and update "contentToShow" like this:

this._sharedService.dataChanged.subscribe(() => {
  this.contentToShow = '??? ' + this.sharedService.sharedData.content + ' ???';
})

StackBlitz

robert
  • 5,742
  • 7
  • 28
  • 37
  • Thanks for the answer! So how can I make a "copy" then? What I need to do is to make some manipulations of that "contentToShow" and still save the original value. How can I do that? Thanks again! – Svetoslav Nedelchev Mar 03 '19 at 20:26
0

Based on this answer it uses similar solution, but the problem with OP's code is that this:

this.contentToShow = this.data.content;

doesn't work because it doesn't reference to the object data like in the example above. this.contentToShow is a string and each time the data will be changed by the _sharedService it doesn't know anything about those changes, unlike in the case of using it like this this.data = this._sharedService.sharedData. Now both this.data and this._sharedService.sharedData point to the same object.

However if I try to use "data.content" it works just fine and displays my data.

It works because component uses the reference to the same object and get updated when changes occurred.

So the solution is using this.data in component.ts and in template:

// component
ngOnInit() {
  this.data = this._sharedService.sharedData;
}


// template
<div *ngIf="data.title">
<h1>{{ data.title }}</h1>
<h5>Author ID: {{ data.authorID }}</h5>
<div>
    <span>{{ data.content }} </span>
    <span>
        <a href="#" *ngIf="showReadMore" (click)="readMore()">Read More &#8618;</a>
    </span>
  </div>
</div>
Vadi
  • 3,279
  • 2
  • 13
  • 30
-1

http calls are asyn in nature!

Let me explain. Initially, you set the value of contentToShow to "" which is a blank string and fine for angular but in the ngOnInit() call, you set this.contentToShow = this.data.content

The this._sharedService.sharedData might be not set or waiting server to response for which it might be undefined also the initial value of data: IData; in undefined, so this.data.content will be undefined after ngOnInit() call.

To fix this error you can set an this.contentToShow in init method only if the this.data.content is available by below code.

set your initial data and content:

 data: IData = { content: '' };

or else you can set to '' in the init call.

ngOnInit() {
    this.data = this._sharedService.sharedData;
    this.contentToShow = this.data && this.data.content || '';
}
Aniruddha Das
  • 20,520
  • 23
  • 96
  • 132