Angular does not allow the transfer of property values from one to another

As of late, I have delved into learning Angular but encountered 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 comprises the properties: title (string), authorId (number), and content (string).

Additionally, I implemented the solution from this post and created 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;
    }
}

Furthermore, I have a view for the Component:

<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>

However, I am facing an issue with the "contentToShow" property appearing as undefined, which is puzzling to me. Strangely, when I use "data.content" instead, it displays the data perfectly. Any insights into why this might be happening? I have been trying to solve it for the past 2 hours, and it seems like it might be something blatantly obvious that I'm missing. Appreciate any help! Thank you!

Answer №1

When you assign the value of data.content to contentToShow, the property is not set, so it appears that contentToShow is not set. It's important to understand that Javascript operates with "call-by-reference", but this only applies to Arrays or Objects, not Primitives (such as strings or numbers).

Because of this, you may see some value when using data.content. This is because you are accessing the property directly from the object and the changed value is displayed in the html.

For a better understanding of this concept, you can refer to Is JavaScript a pass-by-reference or pass-by-value language?.

There are two ways to solve this issue: you can use data.content, or you can notify your component that the values have changed, such as by using an Observable.

The latter approach can be implemented like this:

@Injectable()
export class SharedService{
    notifySubject: 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);
    }
}

In your component, you would subscribe to the Subject like this:

this._sharedService.notifySubject.subscribe(data => {
    // Here you can set `contentToShow` with the data or _sharedServices.sharedData
});

Answer №2

After reading this informative answer, it became clear that a similar solution was utilized. However, the issue with the original poster's code lies in this line:

this.contentToShow = this.data.content;

This line fails to function properly as it does not make reference to the object data as shown in the example above. this.contentToShow is a string and does not respond to changes made to the data by the _sharedService, unlike when using it like this:

this.data = this._sharedService.sharedData
. By using both this.data and this._sharedService.sharedData, they both point to the same object.

However, utilizing "data.content" works perfectly fine and displays the data as expected.

This working behavior is due to the component using a reference to the same object, which gets updated when changes occur.

Therefore, the solution involves using this.data in the component.ts file and in the 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>

Answer №3

The issue arises from the fact that "contentToShow" is a primitive type (string) and it gets initialized only in ngOnInit. When sharedData is updated, the connection between contentToShow and this.data.content is lost as it was never established.

Therefore, the following won't function as expected:

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

Instead, you need to maintain the reference to the object from your dataService in this manner:

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

UPDATE: If you still wish to customize your output using "contentToShow," you must be notified of changes. One approach is to incorporate a Subject in your "dataService" like this:

dataChanged = new Subject<any>();

Once this subject is established, update your "insertData" method with the following as the final line:

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

In your "CurrentArticleComponent" (ngOnInit), you should subscribe to these notifications and update "contentToShow" in this way:

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

StackBlitz

Answer №4

http requests operate in an asynchronous manner!

Allow me to elaborate. Initially, the value of contentToShow is set to "", which is an empty string and suitable for Angular. However, in the ngOnInit() method, you assign

this.contentToShow = this.data.content

The this._sharedService.sharedData may not be set or could be awaiting a response from the server, causing it to be undefined. Additionally, the initial value of data: IData; is undefined, resulting in this.data.content being undefined after the ngOnInit() call.

To rectify this issue, you can assign this.contentToShow in the init method only if this.data.content is available, as shown in the code snippet below.

Initialize your data and content:

 data: IData = { content: '' };

Alternatively, you can set it to '' in the initialization call.

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to retrieve data for the initial keystroke in jQuery autocomplete

I'm currently working on setting up jQuery autocomplete with a specific method involving a separate source function and an intermediary variable for data storage. My main goal right now is to successfully pass the data to the source part of the autoCo ...

Incorporating redux-offline seamlessly into your Angular 5 project

I'm currently grappling with the decision of how to develop an Angular web application that can function seamlessly under offline conditions. While researching possible solutions, I came across react-offline which seems to be a reliable choice for ha ...

problem with the video pathway in the javascript document

I'm currently in the process of putting together a Video gallery playlist using HTML, CSS, and JavaScript. So far, I've set up the html and css files along with two js files. The first js file contains all the video information as shown here: ...

Creating an array of objects using Constructors in Typescript

Utilizing TypeScript for coding in Angular2, I am dealing with this object: export class Vehicle{ name: String; door: { position: String; id: Number; }; } To initialize the object, I have followed these steps: constructor() { ...

Having trouble changing the state within React's useEffect() when using an empty dependencies array? Socket.io is the cause

I have a question regarding the access of allUserMessages from my state within useEffect without anything in its dependency array. Let me provide more details below. Thank you. My goal is to append data to an array in my state using useEffect similar to c ...

Problem with repetitive looping of Jquery click event within an Angular controller

Currently, I am in the process of creating an app using onsen UI (angular based) + phonegap. One issue I am facing is related to a JQuery click event inside an angular controller. The problem arises when I navigate back through the page stack and then ret ...

Tips on deobfuscating Next.js HTML from online sources

I am faced with the task of reconstructing a website that I scraped from the internet using wget. It seems to be built on next js, based on the presence of the _next folder. Even though I have no experience with nextjs and do not understand its inner worki ...

TimePicker Component for ASP.Net MVC with Razor Syntax

I'm currently working on implementing a TimePicker using Razor and JQueryUI in my bootstrap website. While I have successfully created a DatePicker, I am facing difficulties in creating a separate TimePicker using two different TextBoxes instead of se ...

Sharing data between two components in Angular 7

The Address object values are not being retrieved as expected when requesting from the credit card component to a function called getAddress() in a config service that holds the value. Instead of the updated values, I am getting the initial values. Below i ...

Is it possible for multiple queries executed within a websql transaction to be run concurrently?

An informative tutorial online demonstrates the following transaction: db.transaction(function (tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)'); tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")&ap ...

Creating elements with HTML using .createElement()

My goal is to use .createElement() from the wp.element to insert HTML, however it appears as text instead. Here's an example: wp.element.createElement( Meta, { className: 'TEST', title: 'TEST& ...

Is it possible to store a JWT token in local storage when working with Next.js?

We are considering using Next.js for our application, with a focus on client-side rendering for data fetching. The API we will be interacting with is external and requires authentication to access specific user dashboard content. While the homepage will ...

What is the comparable alternative to promise<void> in observables?

I've been working with Angular using TypeScript and I'm attempting to return a promise from an observable. What is the correct way to accomplish this? So far, I have tried the following: of(EMPTY).toPromise() // error: Promise<Observable<n ...

JavaScript function to close mobile menu when menu item is clicked

https://i.sstatic.net/GfKem.png I have a dilemma with my HTML code. I am trying to figure out how to collapse the menu when clicking on a menu item using JavaScript. I have been stuck on this for two days now. Can anyone provide a solution with an explanat ...

The tip display script is unable to revert back to its original content

I managed to show a block of text in a td after a mouseover event, but I want to revert back to the original content on mouseout/mouseleave. The code I used is below. Please help. I am getting an undefined error when running the code. I suspect the issue l ...

The Modal Textarea refreshes each time it is clicked

Whenever I try to type on the modal with 2 textareas, it stops and exits the textarea. The issue seems to be with onChange={event => setTitle(event.target.value)}, but I'm not sure how to fix it. <Modal.Body> <form onSub ...

What is the best way to manage a new Error in Node.js while utilizing ES6 Symbols?

In my node.js application, I am implementing an endpoint using ES6 Symbols. Here is an example: // ES6 Symbol Method const taskCreationMethod = { [Symbol.taskMethod]() { return { storeCheckFunc: async function(storeId, employeeId) ...

Enhanced hierarchical organization of trees

I came across this code snippet: class Category { constructor( readonly _title: string, ) { } get title() { return this._title } } const categories = { get pets() { const pets = new Category('Pets') return { ge ...

Tips for including a sequelize getter in a model instance?

I'm currently struggling to add a getter to the name field of the Company model object in my project. Despite trying various approaches, I haven't had any success so far. Unfortunately, I also couldn't find a suitable example to guide me thr ...

A guide on utilizing the index column for multiple tables using just one statement in the datatable js library

I've incorporated the datatable js for managing two tables on a single page. HTML <!-- Table#1 --> <table class="dataTable"> <thead> <tr> <td>#</td> <td>col1</td> </tr> &l ...