ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: '' - angular5

I am writing a component which takes one #Input parameter and display it in the html bit I am getting below error.
SuggestionsComponent.html:54 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: '<p>so string value</p>
'.
at viewDebugError (core.js:9801)
at expressionChangedAfterItHasBeenCheckedError (core.js:9779)
at checkBindingNoChanges (core.js:9948)
at checkNoChangesNodeInline (core.js:14002)
at checkNoChangesNode (core.js:13976)
at debugCheckNoChangesNode (core.js:14805)
at debugCheckDirectivesFn (core.js:14707)
at Object.eval [as updateDirectives] (SuggestionsComponent.html:54)
at Object.debugUpdateDirectives [as updateDirectives] (core.js:14689)
at checkNoChangesView (core.js:13814)
Here is the component.
export class SuggestionsComponent implements OnInit, AfterViewInit {
#Input() suggestions: Array<Suggestions>;
#Output() approveSuggestion = new EventEmitter<Object>();
constructor(
private elementRef: ElementRef,
) {
}
ngOnInit() {
}
ngAfterViewInit() {
if (this.suggestions && this.suggestions.length > 0) {
this.suggestions
.map((value, index) => {
this.suggestions[index].newSuggestion = value.descriptionSuggestion;
});
}
}

The problem is changing component #Input variable value in ngAfterViewInit(). At that time angular changes view and making some change in bind value in upsets angular.
So moving the #input() value to ngOnInit solves the problem as its the method which executes during component bootstrap.
ngOnInit() {
// This is placed here in the in after view init is because it will throw exception as view will be change at that time
this.suggestions
.map((value, index) => {
this.suggestions[index].newSuggestion = value.descriptionSuggestion;
});
}
ngAfterViewInit() {
if (this.suggestions && this.suggestions.length > 0) {
this.suggestions
.map((value, index) => {
this.elementRef.nativeElement.querySelector('.class' + index).style.display = 'none';
});
}
}

Related

How to pass a computed as a prop to a component?

I have 1 component to which I pass a computed as a prop in this way:
<Datatable :extraParams="extraParams" />
the computed is in the attached image.
I'm having trouble with the value of this property: coverageSelected:coverageData
Coverage data is filled by a select multiple
The problem I have is that when selecting an element of the select, first the component function is executed, then the coverageSelected property is empty, then the computed is executed and until this moment the coverageSelected array is filled, then until the second attempt It already has a full array.
This is my computed
props: [
"status_selected",
"rows",
"totals",
"dateRangeValue",
"coverageSelected",
"coverageList",
"showAll",
"dateFilterSelected",
],
computed(){
extraParams() {
let coverageData = this.coverageList.filter((m) => this.coverageSelected.includes(m.variable));
return {
status: this.status_selected,
dateRange: this.dateRangeValue,
dateFilterSelected: this.dateFilterSelected,
coverageSelected: coverageData, //This is the property that is not late.
showAll: this.showAll,
};
},
}
Another detail to mention that this.coverageSelected is a prop
The method that is executed first in the computed is this:
async getList(params) {
this.loading = true;
try {
if (params) {
this.query = { ...this.query, ...params, ...this.extraParams, filters: this.filters };
} else {
this.query = { ...this.query, ...this.extraParams, filters: this.filters };
}
const { data } = await this.$axios.post(`${this.$config.routePrefix}${this.action}`, this.query);
if (data.code == 200) {
this.rows = data.rows;
this.total = data.total;
this.$emit("listed", data);
}
} finally {
this.loading = false;
}
},

Delete item from pinia state

I am new to vue and I have just started using pinia. I wanna delete an item from array but it does not work
here is my store
import {defineStore} from 'pinia'
export interface ObjectDto {
input: string,
}
interface ObjectDtoInterface {
objects: Array<ObjectDto>
}
export const useSearchHistoryStore = defineStore('objectsStore', {
state: (): ObjectDtoInterface => {
return {
objects: [] as ObjectDto[]
}
},
actions: {
add(dto: ObjectDto) {
if (this.objects
.filter(shd => dto.input === shd.input)
.length === 0) {
this.objects.unshift(dto)
}
},
delete(obj: ObjectDto) {
this.objects = this.objects.filter(e => !(e.input === obj.input))
}
}
})
and here is the function from different .ts file
function delete(obj: ObjectDto) {
objectsStore.delete(obj)
}
add action works perfect, it adds item to the state but when I try to delete an item, nothing happens. The data I pass to delete method is 100% good because I checked this many times
Filter does not mutate the original object, you need to reasing
delete(obj: ObjectDto) {
this.objects = this.objects.filter(e => !(e.input === obj.input))
}
more info https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Angular 7 application, I want to do an if() condition that checks the Input() value passed from parent

I am passing data from parent to child. In the HTML, i can see the value of the Input() variable. However, on my TS file, when I try to do a conditional to check the value of Input() it is always an empty string. Here is my code for the child:
#Input() checkDbStatus = '';
ngOnInit() {
this.initForm();
this.dbStatusCheck();
}
// disables all controls in a form group
disableControl(group: FormGroup){
Object.keys(group.controls).forEach((key: string) => {
const abstractControl = group.get(key);
abstractControl.disable();
})
}
// disable form controls if dbStatus !== update
dbStatusCheck() {
if(this.checkDbStatus !== 'update') {
this.disableControl(this.demographicsSectionOne);
this.disableControl(this.demographicsSectionTwo);
this.disableControl(this.demographicsSectionThree);
this.disableControl(this.demographicsSectionFour);
this.disableControl(this.demographicsSectionFive);
}
}
I think you need to use the ngChange lifecycle.
https://angular.io/api/core/OnChanges
export class YourComponent implements OnChanges
ngOnChanges(changes: SimpleChanges) {
if (changes.checkDbStatus.currentValue !== changes.checkDbStatus.previousValue) {
this.doStatusCheck();
}
}
Try set and get input() function
https://angular.io/guide/component-interaction
_checkDbStatus: any;
#Input() set checkDbStatus(data: any) {
this._checkDbStatus = data;
this.dbStatusCheck(data)
}
get checkDbStatus(){return this._checkDbStatus }

Angular 5 - Event emitter (Property 'update' does not exist on type ....)

I've got a component that I want to update when a person's name changes by emitting an event. My problem is the code doesn't compile because of an error. This is my code
ApplicationFormComponent
#Output() nameChange = new EventEmitter();
closeAccordion(isComplete: string, accordionToClose: string, accordion: NgbAccordion) {
if (accordionToClose === 'personal-details-panel') {
this.applicationStatusFlags.personalDetailsStatus = (isComplete === 'true');
this.nameChange.emit({ personId: this.personId });
}
}
ApplicationFormComponent.html
<name-display
[personId]="personId"
[placeHolderText]="'Hello'"
(nameChange)="update($event)">
</name-display>
NameDisplayComponent
import { Component, Input, OnChanges, SimpleChanges } from '#angular/core';
import { PersonService } from "../../../service/person.service";
#Component({
selector: 'name-display',
templateUrl: './NameDisplay.component.html',
providers: [PersonService]
})
export class NameDisplayComponent implements OnChanges {
constructor(private readonly personService: PersonService) { }
#Input() personId;
#Input() placeHolderText: string = "";
forename: string = "";
ngOnChanges(changes: SimpleChanges): void {
if (changes["personId"]) {
this.personService.getPersonDetails(this.personId).subscribe((res: IPersonDetails) => {
this.forename = res.forenames;
});
}
};
update(personId: number) {
alert("update name");
this.personService.getPersonDetails(personId).subscribe((res: IPersonDetails) => {
this.forename = res.forenames;
});
}
}
My problem is basically when I use angular cli with the command ng server --aot, it doesn't compile because of this error:
ERROR in src\app\component\ApplicationForm\ApplicationForm.component.html(42,9): : Property 'update' does not exist on type 'ApplicationFormComponent'.
I've written a similar component that uses an event emitter which doesn't have this problem, so I'm stuck with how to fix the error.
Any ideas?
It is because you are passing $event to method.
(nameChange)="update($event)"
But it accepts number.
update(personId: number) {
alert("update name");
}
Please change the method as below.
update(event:any) {
const personId = event as number
alert("update name");
}

Vue js : _this.$emit is not a function

I have created a Vue component call imageUpload and pass property as v-model
<image-upload v-model="form.image"></image-upload>
and within imgeUpload component
I have this code
<input type="file" accept="images/*" class="file-input" #change="upload">
upload:(e)=>{
const files = e.target.files;
if(files && files.length > 0){
console.log(files[0])
this.$emit('input',files[0])
}
}
and I received
Uncaught TypeError: _this.$emit is not a function
Thanks
Do not define your method with a fat arrow. Use:
upload: function(e){
const files = e.target.files;
if(files && files.length > 0){
console.log(files[0])
this.$emit('input',files[0])
}
}
When you define your method with a fat arrow, you capture the lexical scope, which means this will be pointing to the containing scope (often window, or undefined), and not Vue.
This error surfaces if $emit is not on the current context/reference of this, perhaps when you're in the then or catch methods of a promise. In that case, capture a reference to this outside of the promise to then use so the call to $emit is successful.
<script type="text/javascript">
var Actions = Vue.component('action-history-component', {
template: '#action-history-component',
props: ['accrual'],
methods: {
deleteAction: function(accrualActionId) {
var self = this;
axios.post('/graphql',
{
query:
"mutation($accrualId: ID!, $accrualActionId: String!) { deleteAccrualAction(accrualId: $accrualId, accrualActionId: $accrualActionId) { accrualId accrualRate name startingDate lastModified hourlyRate isHeart isArchived minHours maxHours rows { rowId currentAccrual accrualDate hoursUsed actions { actionDate amount note dateCreated } } actions {accrualActionId accrualAction actionDate amount note dateCreated }} }",
variables: {
accrualId: this.accrual.accrualId,
accrualActionId: accrualActionId
}
}).then(function(res) {
if (res.data.errors) {
console.log(res);
alert('errors');
} else {
self.$emit('accrualUpdated', res.data.data.deleteAccrualAction);
}
}).catch(function(err) {
console.log(err);
});
}
}
});
You can write the method in short using upload(e) { instead of upload:(e)=>{ to make this point to the component.
Here is the full example
watch: {
upload(e) {
const files = e.target.files;
if(files && files.length > 0) {
console.log(files[0]);
this.$emit('input',files[0]);
}
}
}