Angular object array value throw error of undefined - javascript

I am working on Angular 6 application. I have behaviour variable of Input, once I received data, I map to surveyInfo object. I have surveyInfoDataModel class as shown below; followed by I am trying to display this data by reading surveyInfo object in template but go error
error
ERROR TypeError: Cannot read property 'surveyId' of undefined
component
export class MyComponent implements OnInit {
#Input() surveySelectedToGetInfo: BehaviorSubject<any>;
ngOnInit() {
this.surveySelectedToGetInfo.subscribe(surveyDataItem=>{
debugger;
if(surveyDataItem!=null){
this.loadSurveyInformation(surveyDataItem);
}
});
}
private loadSurveyInformation(selectedSurveyObject:any):any{
var mappedObject = this.mapSurveyInfo(selectedSurveyObject);
}
private mapSurveyInfo(survey:any):SurveyInfoDataModel{
if(survey!=null){
this.surveyInfo = new SurveyInfoDataModel(
survey.consultationId,
survey.surveyId,
survey.surveyIdNum,
survey.surveyName
);
}
return this.surveyInfo;
}
Survey Info DataModel class
export class SurveyInfoDataModel{
surveyId:string;
surveyIdNum:string;
surveyName:string;
consultationId:string;
constructor(consultationId, surveyId, surveyIdNum, surveyName ){
this.consultationId =consultationId;
this.surveyId = surveyId;
this.surveyIdNum = surveyIdNum;
this.surveyName = surveyName;
}
}
html template
<div class="surveyListInfoBlock">
<div *ngIf="surveyInfo">
{{surveyInfo.surveyId}}
</div>
</div>

Try to change if(survey!=null) to if(!survey) return;. Looks like you going to return undefined if there is no survey cause return statement is outside the brackets. If it will work, you'll need to check all props of this object on undefined. Also you need to add typing to this object.
private mapSurveyInfo(survey:any):SurveyInfoDataModel{
if (!survey) return;
this.surveyInfo = new SurveyInfoDataModel(
survey.consultationId,
survey.surveyId,
survey.surveyIdNum,
survey.surveyName
);
return this.surveyInfo;
}

Survey in your case is undefined. Instead of testing if survey is null you can test for both null & undefined with this:
if(!!survey){
this.surveyInfo = new SurveyInfoDataModel(
survey.consultationId,
survey.surveyId,
survey.surveyIdNum,
survey.surveyName
);
}

Related

Add an optional parameter to a constructor that already has optional parameters in JavaScript

I'm currently working on a node.js project that supports es6 classes and I need to add an optional parameter to a class constructor that is being called several times, but the problem is that the class constructor already has one optional parameter.
So my question is, is there a way I can add another optional parameter without having to refactor all places where the class is instantiated and still have a clear syntax?
The code I have so far looks like this:
// Error class
class MyError extends Error {
constructor( message = "default message") {
super(message)
}
}
...
// Being called as
throw new MyError()
And I would like to add another optional parameter:
class MyError extends Error {
constructor( message = "default message", code = 500) {
super(message);
this.code = code;
}
}
...
// But if I do it like this, then I always have to inform message when I instantiate MyError:
throw new MyError(undefined, 400)
Is there an approach to adding an optional parameter that would allow me to avoid refactoring previous calls and still allow me to omit the undefined when I create a new instance of MyError with a custom code?
I know for example that I could use object destructuring to have named parameters:
class MyError extends Error {
constructor( { message = "default message", code = 500 } = {} ) {
super(message);
this.code = code;
}
}
...
// But then I would have to refactor all places that call MyError with a message to
throw new MyError({ message: "OldMessage"})
As commented by others, it is best to keep with the parameter structure of Error from which this class inherits, and not be disturbed by new MyError(undefined, 400).
If however you really want to go ahead with the object parameter, then you could make it backwards compatible like this:
class MyError extends Error {
constructor(options = { message: "default message", code: 500 }) {
super(typeof options === "string" ? options : options?.message);
if (typeof options === "string") options = { message: options, code: 500 };
this.message = options?.message;
this.code = options?.code;
// rest of your code
}
}
This would allow old-style code like new MyError("OldMessage") to continue to work, while newer code can do any of these:
new MyError({ message: "NewMessage" });
new MyError({ code: 404 });
new MyError({ message: "NewMessage", code: 404 });
This gives you time to refactor the code at your convenience.
Given that you have already had to refactor this once, the best approach is going to be doing the work now so that you don't have to in the future.
You should go the route of object destructuring. If you are against refectoring everything now, you can add this as a new constructor and refactor at a later point, but it is highly recommended that you perform all this at once.

Global Function/Service to detect and replace undefined object properties | Angular 2 -TS

I searched for solutions to the problem of
core.umd.js:3523 ORIGINAL EXCEPTION: Cannot read property 'fullName' of undefined
The Exception came from Template on getting specific property:
{{project.collaborators["0"]["fullName"]}}
I found some useful answer too.
but I am looking for a way to define a global service which will check each object and replace missing/empty property with default value i.e -
instead of checking in each template's each property and will make code less buggy.
// undefined-obj.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class UndefinedObjectsGlobalService {
private charecterToReplace: string = '-'; // set defualt value
replaceDefaultCharecter(object: any, charecterToReplace: string): any {
this.charecterToReplace = charecterToReplace;
// create instance vars to store keys and final output
let keyArr: any[] = Object.keys(object),
var dataArr: any[];
// loop through the object,
// pushing values to the return array
keyArr.forEach((key: any) => {
// if key is null at any iteration then replace is with given text
if (key == null){
dataArr.push(object[key] = this.charecterToReplace);
// else push
}else{
dataArr.push(object[key]);
}
});
// return the resulting array
// need to convert is back to object any idea ?
return dataArr;
}
}
as I am new to angular so the class undefinedObjectsGlobalService may be buggy
please help.
You seem to be trying to access a property of an object that does not exist.
Angular already provides the elvis operator to handle this kind of thing.
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
{{collaborators["0"]?.fullname}}
</div>
`,
})
export class App {
name:string;
collaborators: [{fullname: string}];
constructor() {
this.name = 'Angular2'
this.collaborators = [];
}
}
You can see an example here.

Angular2 Calling exposed method from outside app and loses change binding

I have a public method that I exposed to window. This method talks to a Component and modifies a variable I am watching in my template. But when I change the value, the *ngIf() does not get triggered.
app.component
constructor(private _public: PublicService,) {
window.angular = {methods: this._public};
}
PublicService
export class PublicService {
constructor(
private _viewManager: ViewManagerComponent,
) {}
CallMe(){
this._viewManager.renderView('page1')
}
}
LayoutManagerComponent
#Component({
selector: 'view-manager',
template: `<page *ngIf="view == 'page1'"></page>`
})
export class ViewManagerComponent {
//This is the variable being watched
view = "page";
renderView = function(type){
console.log(type)
this.view = type;
console.log(this.view)
};
}
So the idea is that when the view initially loads, the view is blank. Then when I type angular.methods.CallMe() it modifies the view variable to page1 which should then show the html for the Component. If I console renderView function it is successfully getting called, just the view does not change.
----Update - Still not working -------
export class ViewManagerComponent {
constructor(private zone:NgZone,private cdRef:ChangeDetectorRef) {
}
view = "page";
#Output() renderView(type){
// type is 'page'
console.log(this.view)
this.zone.run(() => {
// type is 'page'
console.log(this.view)
this.view = type;
// type is 'page1'
console.log(this.view)
});
// type is 'page1'
console.log(this.view)
//cdRef errors:
//view-manager.component.ts:36 Uncaught TypeError: this.cdRef.detectChanges is not a function(…)
this.cdRef.detectChanges();
};
}
In this case Angular2 doesn't know that it needs to run change detection because the change is caused by code that runs outside Angulars zone.
Run change detection explicitely
contructor(private cdRef:ChangeDetectorRef) {}
someMethodCalledFromOutside() {
// code that changes properties in this component
this.cdRef.detectChanges();
}
Run the code that modifies the components properties inside Angulars zone explicitely
contructor(private zone:NgZone) {}
someMethodCalledFromOutside() {
this.zone.run(() => {
// code that changes properties in this component
});
}
The zone method is a better fit when // code that changes properties in this component not only changes properties of the current component, but also causes changes to other components (like this.router.navigate(), call method references of methods of other components) because zone.run() executes the code inside Angulars zone, and you don't need to explicitely take care of change detection in every component where a change might happen because of this call.
If you use function(...) instead of () => it's likely you'll get unexpected behavior with this in code inside the Angular component.
See also my answer to this similar question for more details Angular 2 - communication of typescript functions with external js libraries
update
export class ViewManagerComponent {
constructor(private zone:NgZone,private cdRef:ChangeDetectorRef) {
self = this;
}
view = "page";
#Output() renderView(type){
// type is 'page'
console.log(self.view)
self.zone.run(() => {
// type is 'page'
console.log(self.view)
self.view = type;
// type is 'page1'
console.log(self.view)
});
// type is 'page1'
console.log(self.view)
self.cdRef.detectChanges();
};
}

Aurelia - Custom element property binding resolution

I have created a custom element with an #bindable property. In the element's constructor another property is set using the value of the bound property. This is the code for the custom element (file name custom-element.ts):
import {bindable} from 'aurelia-framework';
export class CustomElement{
#bindable value: any = null;
message: any;
constructor(){
this.message = this.generateMessage();
}
generateMessage(){
if (this.value != null){
// logic to generate and return message
} else {
return "Someting went wrong";
}
}
}
This model has a the simple related view (file name custom-element.html):
<template>
The value id is: ${value.id}, and the message is: ${message}
</template>
I use this element in a view elsewhere, whose model has access to the value object:
<require from="resources/custom-element"></require>
<custom-element value.bind="value"></custom-element>
While ${value.id} displays correctly, the ${message} is always Something went wrong.
When is value being set, and how can I use its value to set message?
The constructor is called when your custom element is initally created however the data-binding will happen later in its lifecycle.
In order to get access to the bound properties you need to use the bind lifecycle callback (or attached depending on your actual needs) to set your message:
export class CustomElement{
#bindable value: any = null;
message: any;
bind() {
this.message = this.generateMessage();
}
generateMessage(){
if (this.value != null){
// logic to generate and return message
} else {
return "Someting went wrong";
}
}
}
As an alternative solution you can subscribe on your value property changed event with implementing a method with the naming convention: yourPropertyChanged (so in your case valueChanged) and do the message generation there:
valueChanged(newValue) {
this.message = this.generateMessage(newValue);
}
generateMessage(newValue){
if (newValue != null){
// logic to generate and return message
} else {
return "Someting went wrong";
}
}

QT using addToJavaScriptWindowObject()

I am trying to use void QWebFrame::addToJavaScriptWindowObject(const QString & name, QObject * object). My problem is when I try and call the function in the JavaScript
TypeError: Result of expression 'screen.valueChanged' [undefined] is not a function.
TimeSliceScreen::TimeSliceScreen(QWidget* parent)
:
QWidget( parent )
{
QVBoxLayout* layout = new QVBoxLayout( this );
_timeSlice = new QWebView( this );
_timeSlice->setMinimumSize( 200,200);
QSizePolicy policy = _timeSlice->sizePolicy();
policy.setVerticalStretch(3);
_timeSlice->setSizePolicy(policy);
_timeSlice->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
_timeSlice->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
layout->addWidget( _timeSlice );
layout->addStretch();
layout->addSpacing( 20 );
_timeSlice->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
interface = new WebPageInterface();
connect( _timeSlice->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
this, SLOT(populateJavaScriptWindowObject()) );
}
void TimeSliceScreen::populateJavaScriptWindowObject(){
_timeSlice->page()->mainFrame()->addToJavaScriptWindowObject(QString("screen"),
interface);
}
WebPageInterface is a very simple class that extends QObject and has one slot called valueChanged that is the function I am trying to call.
My JavaScript is:
function call() {
screen.valueChanged();
}
which gets called from
<input type="hidden" id="maxhid" name="maxhid" value="{maxSlider}" onchange="call()"/>
Everything I have read says that this is the way to do it, but it's not working for me.
I think screen is a reserved name in the js. Try changing the name to something else. Otherwise looks like it should work.

Categories