Angular 5 auto update string variable on another variable change - string

Is there a way to update a string variable when another variable changes? I have a string that is built by using various variables. I display that string in the component's html file using interpolation. However, if a variable changes that the string was using to build itself, the string will never change because they not mutable. Only way would be to reset the string again when one of the other variables change.
Sample ts code:
import { Component} from '#angular/core';
#Component({
selector: 'app-test-component',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent {
myString = '';
amount = 0;
constructor() {
this.myString = `Hello, you have ${this.amount} dollars.`;
setTimeout(() => {
this.amount = 40;
}, 5000);
}
}
Sample html code:
<p>{{ myString }}</p>
Of course, after five seconds, the string stays the same because I never reinitialized it. Is there a way to automatically detect if any of the variables being used inside myString change, then update myString to use the values of the changed variables?
Thank you, any information is appreciated!

You can do that using BehaviorSubject, which has a notion of "the current value". It stores the latest value emitted to its consumers, and whenever a new Observer subscribes, it will immediately receive the "current value" from the BehaviorSubject.
Create separate service for amount data.
service.ts
//set the initial value here
amount=new BehaviorSubject(0);
component.ts
ChangeDetectorRef
What it means if there is a case where any thing inside your model (
your class ) has changed but it hasn't reflected the view, you might
need to notify Angular to detect those changes ( detect local changes)
and update the view.
constructor(private ref: ChangeDetectorRef,subj:SubjectService) {
subj.amount.subscribe((data)=>{
this.amount=data;
this.myString = `Hello, you have ${this.amount} dollars.`;
//check for changes then it will update the view
this.ref.markForCheck();
})
setTimeout(() => {
//change amount data after 1 second
subj.amount.next(40);
}, 1000);
}
example here: https://stackblitz.com/edit/changedetection-using-subject?file=src%2Fapp%2Fapp.component.ts

Related

React Typescript take part of props

I'm relatively new to React & TypeScript. I'm trying to extend an existing component by making a wrapper around it, but I am having issues trying to add my own values to the properties.
I want it so that the default properties (in a predefined type "TextFieldProps" from the MUI library) carry over, and I can add my own values to it. I'm doing this by making my own type as such:
type PinnableTextFieldProps = TextFieldProps & {
pinned: boolean;
onPin: (newValue: boolean) => void;
};
I then use it as follows:
export function PinnableTextField(props: PinnableTextFieldProps) {
return (
<TextField
{...props}
InputProps={{}}
/>
);
}
This works fine, except that the "pinned" and "onPin" values are copied over to the TextField while they shouldn't be (TextField doesn't know what they are, and an error is printed to the console because of it)
I tried to cast it using ...(props as TextFieldProps) but it still included the properties in the spread.
How would I properly split up the props spread to only include all values inside of the TextFieldProps type, so excluding the 2 values I added?
I hope someone can point me in the right direction!
Many thanks!
You could do something like this:
export function PinnableTextField(props: PinnableTextFieldProps) {
const { pinned, onPin, ...rest } = props; // <= Splitting props into pinned, onPin and all other properties into rest
// Use pinned, onPin here
// Pass the rest of the props down to the TextField
return (
<TextField
{...rest}
InputProps={{}}
/>
);
}

React state data changing on its own

Hi I'm working on a simple address book app made with ReactJS.
There's one boolean key in state called 'show_personal'. By default it is set to false. When it is true then the component will render personal address book.
For some reason this boolean value keeps changing on page reload. I've put some logging statements which shows value of it.
I noticed that almost in a sec when the component gets rendered, its value changes due to which my component get re-rendered and this breaks my app UI.
I'm using proper way to update state using this.setState method. While logging/debug I've disabled every possible function which toggles this value. Still facing this issue
Is there anyway to trace which component of code has changed it's value ?
class Home extends React.Component{
state = {
show_personal : false
}
render(){
const checkFlag = ()=>{
if(this.state.show_personal){
return <div>Show personal records</div>
}
}
else{
return <div>Show business records</div>
}
return({
{checkFlag}
})
}
}
Hi your render syntax is a little unusual, you return your checkFlag function without calling it, I don't known what that could do...
Maybe you could try it like this to see if it behaves better
class Home extends React.Component{
state = {
show_personal : false
}
render() {
return this.state.show_personal ? <div>Show personal records</div> :
<div>Show business records</div>
}
}

Ionic 3 Passing objects using navparams to 3 pages

I am an absolute beginner and am self taught using Ionic3
I have a problem that is driving me wild that I hope someone can assist on.
I have a master-detail-detail setup where the following happens:
Master page has a list of reports (taken from a JSON file), click on that report and it takes to a details page, click on that and it opens up another page with even more details, without having to define all the individual parts.
All I want to do is simply pass the whole object from the second page to the third page so that I can use its parameters again
master page (Report),
Second page (Reportmenu),
Third page (GenOverview)
So passing between master and second page is fine and works as it should using navparams (not shown here) but I want to use that same object and pass all the data again to the 3rd page.
I thought it would be just as simple as assigning it to a new variable and then passing that again using navparams but I get undefined
export class ReportmenuPage {
name: any;
overallscore: any;
reportdate: any;
coach: any;
age: any;
TechOverall: any;
TactOverall: any;
PhysOverall: any;
PsychOverall: any;
Logo: any;
data2: any;
constructor(public navCtrl: NavController, public navParams: NavParams,
public postsService: Posts, private toastCtrl: ToastController) {
this.overallscore = this.navParams.get('OverallScore');
this.reportdate = this.navParams.get('ReportDate');
this.name = this.navParams.get('Name');
this.coach = this.navParams.get('Coach');
this.age = this.navParams.get('Age');
this.TechOverall = this.navParams.get('TechOverall');
this.TactOverall = this.navParams.get('TactOverall');
this.PhysOverall = this.navParams.get('PhysOverall');
this.PsychOverall = this.navParams.get('PsychOverall');
this.Logo = this.navParams.get('Logo');
console.log(this.navParams.data);
this.data2 = this.navParams.data;
}
myClickHandlerOverview(data2) {
this.navCtrl.push(GenOverviewPage, data2);
So this is all fine and gives an expected output
So all I want to do now is get this to the GenOverviewPage
Here is the Reportmenu.html
<ion-item (click)="myClickHandlerOverview(data2)" color="primary">
<ion-icon name="arrow-dropright" item-end></ion-icon>
GENERAL OVERVIEW
</ion-item>
And Finally the bit that doesn't work
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { ReportmenuPage } from '../reportmenu/reportmenu';
#IonicPage()
#Component({
selector: 'page-gen-overview',
templateUrl: 'gen-overview.html',
})
export class GenOverviewPage {
constructor(public navCtrl: NavController, public navParams: NavParams) {
let data3 = this.navParams.data;
console.log(data3); //this shows as {}
}
ionViewDidLoad() {
console.log('ionViewDidLoad GenOverviewPage');
}
}
I am pretty certain I am going to get ridiculed as I think this relates to my lack of understanding of objects/ arrays and how they are passed, but I have searched high and low and cannot grasp what I am doing wrong.
and why console.log(data3) shows the second empty array but as you can see I can transfer it fine between page 1 and 2
Many thanks
In your second page, you are saving the data in a local variable that is contained within your constructor.
let data2 = this.navParams.data;
You need to save it in the class variable to be passed to your myClickHandlerOverview. Change it to:
this.data2 = this.navParams.data;
Also may need to change datatype from array to object or any.
data2: any;
A better approach seems to be to store it in a common provider.
Very simple in the end thanks to Suraj
I was missing a this!
myClickHandlerOverview(data2) {
this.navCtrl.push(GenOverviewPage, this.data2);

Angular2 object property typed as "number" changes to string

I have a very simple Angular2 app running locally. I'm using a service to send an instance of an object to a webservice API. The API validates JSON against a schema, and ID's must be numbers (i.e. NOT quoted in the JSON).
My problem is that when I try to send the object to the webservice, my ID field has quotes around it, even though it's typed to be a number in Typescript.
The behaviour is only observed when the nameproperty of the object contains "special characters".
I've tested and found that it doesn't seem to be the JSON.stringify I use — please see code below.
The app is written in Typescript.
Unit class:
export class Unit {
id: number; // This is the problem child
name: string; // This is the string that can contain special characters
short: string;
triggers_plural: number;
is_headline: boolean;
}
Method to save:
My code for saving an instance of Unit to the webservice:
updateUnit(unit: Unit): Promise<Unit>
{
var objSend = {unit_data: [unit]}; // Webservice expects an array of units
console.log(objSend.unit_data[0].id === 2); // Yields false when ID is 2
console.log(objToReturn); // Logs ID to verify it is 2 when testing
// Code for actual request
return this.http.put(`${this.unitUrl}/${unit.id}`, JSON.stringify(objSend),{headers:this.headers})
.toPromise()
.then(()=> unit)
.catch(this.handleError);
}
When running the code and calling the method, the console will log that the ID is NOT equal when the Unit object's name property contains special characters.
Example without special characters (no problem, id is a number):
Example WITH special characters (eek! Id is a string!):
The updateUnit method is called from my unit-detail component where you can edit a unit:
export class UnitDetailComponent implements OnInit {
unit: Unit; // this.unit later on
constructor(
private unitService: UnitService,
private route: ActivatedRoute
){}
ngOnInit(): void
{
this.route.params.forEach((params: Params) => {
let id = +params['id']; // The routing will give id to look for
this.unitService.getUnit(id)
.then(unit => this.unit = unit); // Here the unit is instanciated in the first place
});
}
save(): void
{
this.unitService.updateUnit(this.unit).then(this.goBack); // Here is the call to updateUnit method
}
}
It's bound to an input in the template:
<div *ngIf="unit">
<div>
<label>Edit unit</label>
<div>
<input type="text" [(ngModel)]="unit.name" />
</div>
</div>
<button class="btn btn-default" type="button" (click)="save()">Save</button>
</div>
Maybe the problem arises already when the two-way data binding is filling in the name property when you write something in the <input> but I don't understand how the type of the id can change?
Link to github repos of the whole project: https://github.com/djoike/ng2-cookbook/tree/master-so
A small solution is to cast the field to any , and then convert it to a number using parseInt(), I faced a similar problem just today
for more info about casting, check the Type assertions section here

Using WinJS with Angular2

Trying to use a property to configure a WinJS control from within Angular2, so far I couldn't find a solution, e.g. this code below is throwing 'Can't bind to 'dataWinOptions' since it isn't a known property of the '' element'.
#View({
template: `<div id="rating" data-win-control='WinJS.UI.Rating' [data-win-options]='jsonRating'></div>`
})
class MyRating {
rating: number;
get jsonRating() {
return '{averageRating: ' + this.rating + '}';
}
constructor() {
this.rating = 1.5;
}
}
Any hint?
#ericdes about your last comment I think this would be the best option. Assuming you have Nth WinJS controls
Consider the following code. I'm specifying differents values for the averageRating property in options.
<winjs-control [options]="{averageRating: '1.5', someMoreOptions : 'x'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.4', differentOptionsForThisOne :'Z'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.3'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.2'}"></winjs-control>
<winjs-control [options]="{averageRating: '1.1'}"></winjs-control>
// more and more...
The component will read this options property and will pass it to the view. Forget about the directive, it isn't necessary after all.
We pass options through attr.data-win-options since it isn't a property of div but an attribute.
#Component({
selector : 'winjs-control',
properties : ['options']
})
#View({
template : `<div data-win-control="WinJS.UI.Rating" [attr.data-win-options]="jsonRating"></div>`,
})
class WinJSComponent implements OnInit, AfterViewInit {
constructor() {}
// We specify onInit so we make sure 'options' exist, at constructor time it would be undefined
// And we stringify it or otherwise it will pass an object, we need to convert it to a string
onInit() {
this.jsonRating = JSON.stringify(this.options);
}
// We process WinJS after view has been initialized
// this is necessary or 'data-win-options' won't be fully processed
// and it will fail silently...
afterViewInit() {
WinJS.UI.processAll();
}
}
Here's a plnkr for this case.
That's one option and IMHO I think this is the easiest one. Another one, having the same HTML content, would be to communicate the parent with its children and I haven't tested your case with that approach.

Resources