I have a really weird problem with my application. In order for the real time database to display, I have to interact with the application. 2 months ago I did not have this problem, the information appeared normally, but now, I have to click on a button or something in order for the view to update.
I made a quick GIF here:
If you pause just as I click the <- back button you'll see what I mean.. https://i.gyazo.com/44b450aa502dc7f37e5a5feea19824c6.mp4
I don't know what I did to make this happen..
Here is a code example from my application:
.ts:
if(fromClick) { this.subscription.off(); }
this.postFeed = new Array();
this.subscription = this.database.database.ref('/posts/'+this.locationId)
.orderByChild('score')
.limitToLast(10);
this.subscription.on('child_added', (snapshot) => {
this.postFeed.push(snapshot.val())
});
.html:
<ng-container *ngFor="let post of postFeed.reverse(); let i = index">
<post [postData]="post"></post>
</ng-container>
So this code above used to work perfectly, but now I have to interact with my app for it to display on the screen, even though the postFeed console logs perfectly fine. The data shows up in the console exactly when I request it, but it doesn't show on screen.
Any ideas? As I said, it used to work fine. Any help would be great! Thank you
For anyone else who has this problem, NgZone was the answer for me.
I imported it:
import { NgZone } from '#angular/core';
added it to the constructor:
public zone: NgZone,
and then wrapped it around my firebase code:
this.zone = new NgZone({});
if(fromClick) { this.subscription.off(); }
this.postFeed = new Array();
this.subscription = this.database.database.ref('/posts/'+this.locationId)
.orderByChild('score')
.limitToLast(10);
this.subscription.on('child_added', (snapshot) => {
this.zone.run(() => {
this.postFeed.push(snapshot.val())
}):
});
and this worked wonderfully for me. I couldn't find the actual cause of the issue, but I just wrapped the NgZone code around all my firebase functions and it worked like it used to. If anyone else has more information that would be great.
Related
I'm following along a javascript tutorial, and suddenly localhost:3000 is not displaying anything but a white page after making some changes in my About.jsx file.
It started happening after I added:
import { urlFor, client } from '../../client';
and
const [abouts, setAbouts] = useState([]);
useEffect(() => {
const query = '*[_type == "abouts"]';
client.fetch(query)
.then((data) => {
setAbouts(data);
})
}, []);
If I undo the changes, hit save after adding one change at a time, it's fine. But once all of them are in place, Chrome doesn't display anything.
I've tried adding an error in my code to see what happens, and the browser immediately picks up on it displaying it in red, so apparently it's not NOT working.
Any idea what's happening? I've also tried deleting all cookies/browser history/caches with no luck.
EDIT:
Tested a bit more now, and it really seems like the import is the culprit here. I followed the tutorial to a t, but one thing that puzzled me was that I have no folder called client. It's a client.js file inside my src folder, but that's what the tutorial dude did, and so far in the video it still works for him, so... still confused.
My client.js file looks like this:
import sanityClient from '#sanity/client';
import imageUrlBuilder from '#sanity/image-url';
export const client = 'sanityClient'({
projectID: process.env.REACT_APP_SANITY_PROJECT_ID,
dataset: 'production',
apiVersion: '2022-02-01',
useCDN: true,
token: process.env.REACT_APP_SANITY_TOKEN
});
const builder = imageUrlBuilder(client);
export const urlFor = (source) => builder.image(source);
I found the error, and now I'm deciding between jumping for joy or just jumping into a lake, lol.
Apparently, it IS a big deal if you write 'projectID' when it should be 'projectId'..!
Who knew! Happy Saturday, everyone :)
EDIT: And I had to remove the '' from
export const client = sanityClient({
I thought about deleting this question since I figured it out myself, but maybe this will be to some help for anyone else sometime, so I'll leave it up.
Sorry for the novel, but I want to make sure I give enough info. I've read the Vue reactivity docs and I'm not adding elements by index or changing the length of the array, but I'm still having problems with my component not updating properly. I'm just doing a plain assignment with my array. I've made it so you can drag and drop the goals in order to have them in whatever order you want, save the order, and reload them in the correct order.
What happens is most of the time, it doesn't render at all. If I change a prop in the element with v-for in the template and save (like, switching ":key="goal.id" to :key="goal.title" or vice versa), forcing a refresh, it will render one time correctly and then disappear after I click on saveGoals(). saveGoals() still works correctly and saves the goals in the right order. Array is being repopulated after saveGoals() to make sure I'm getting the new order of the goals. No errors are appearing in the console. Reloading the page doesn't work, only changing the prop value.
It used to consistently render just fine, but unfortunately, I can't remember what code I changed to break it. The onMounted() function (using the composition API) is using DB functions, and after extensive testing, I verified that the DB functions are working correctly. I removed the draggable library for testing purposes and found that I encounter the same error. It seems to be working fine with a hardcoded list, however, I haven't tested any save function with the hardcoded list. I've tried it with different lifecycle hooks (onBeforeMounted, onCreated, etc.) and I got the same result.
It is being rendered using <component :is="GoalList"> because there's three components on the page (think tabs). Switching between the components doesn't update it, either, but switching between the components also doesn't make anything disappear.
I don't know why I have to force a refresh by changing a prop on an element, but that's the only thing that seems to work. I'd rather not write a hack to force the refresh (creating a key that updates with a counter function), but if that's the only fix I'll take it. As far as I can tell, it should be rendering the array just fine.
EDIT: A simplified version of the code can be found here:
Codesandox.io link. Everything works fine in the sandbox, so there's something in the original code. DB is returning the array just fine, and since everything was working I added in vue-draggable-next and Element+ to try to isolate the issue with no luck.
EDIT: I managed to clarify the issue. Sorry for the long posts.
Upon initially loading, even though there's data in the array, nothing is loaded on the page. If I change the template code in any way (adding a button, changing a prop, removing a button, etc.) and save the Vue file, the content appears.
Tried clearing out the cache on my browser in case there was something there causing the issue. Also tested on multiple browsers.
A hardcoded list works as expected: it renders upon page load just fine. To ensure that I wasn't trying to loop a blank array, I added a logging button that logs out the array to the console. The array is populated with the data.
What makes me think that I'm breaking the renderer somehow is that everything populates when the template code is updated and npm run serve refreshes. Everything works perfectly. It's just blank on the initial page load (and, of course, when reloaded).
I honestly don't even know where to start with debugging this issue, which is probably why I'm having a problem explaining it. Not expecting a magical fix, just a direction to head in.
Onto the code. If you think the DB code would be helpful, I am happy to add it, although it is ugly:
Template:
<template>
<h2>Your Goals</h2>
<el-form #submit.prevent>
<draggable v-model="goals">
<div v-for="goal in goals" :key="goal.id">
<el-card class="box-card">
<div class="goal_header">
<h3>{{ goal.title }}</h3>
</div>
<div class="goal_buttons">
<el-button #click="deleteGoal(goal.id)" type="danger"
>Delete Goal</el-button
>
<el-button #click="editGoal(goal.id)">Edit Goal</el-button>
</div>
</el-card>
</div>
<el-button #click="saveGoals">Save Goals</el-button>
</draggable>
</el-form>
</template>
onMounted():
onMounted(() => {
getAuth().onAuthStateChanged((user) => {
if (user) {
try {
getOrderOfGoals(user.uid).then((result) => {
if (result.goalOrder.length === 0) {
getListOfGoals(user.uid).then((result) => {
goals.value = result;
console.log(goals.value);
});
} else {
returnOrderedGoals(user.uid, result.goalOrder).then(
(result) => {
goals.value = result;
});
}
});
} catch (error) {
ElMessageBox.alert(
"An error has occurred attempting to render the component. " +
error,
"Error",
{
confirmButtonText: "OK",
}
);
}
} else {
router.push("/");
}
});
});
saveGoals():
function saveGoals() {
getAuth().onAuthStateChanged((user) => {
if (user) {
try {
saveGoalOrder(user.uid, goals.value)
.then(() => {
getOrderOfGoals(user.uid)
.then((result) => {
returnOrderedGoals(user.uid, result.goalOrder).then(
(response) => {
goals.value = response;
}
);
})
...(A bunch of catches for Promises being thrown about).
OK. I don't know why it works, but rewriting the code to the following worked:
getAuth().onAuthStateChanged((user) => {
if (user) {
try {
goalListener.getListOfGoals(user.uid).then((result) => {
const listOfGoals = result;
goalListener.getOrderGoalList(user.uid).then((result) => {
if (listOfGoals.length === result.goalOrder.length) {
goalListener
.returnOrderedListOfGoals(user.uid, result.goalOrder)
.then(() => {
goals.value = result.goalOrder;
});
} else {
goals.value = listOfGoals;
}
});
});
} catch...(bunch of error code here)
I created a listener (goalListener) and put all the promises in there with simple returns:
const goalListener = {
removeGoal: function (user, docId) {
return deleteGoalFromDB(user, docId);
},
saveGoals: function (user) {
return saveGoalOrder(user, goals.value);
},
getOrderGoalList: function (user) {
return getOrderOfGoals(user);
},
getListOfGoals: function (user) {
return getListOfGoals(user);
},
returnOrderedListOfGoals: function (user, listOfOrderedGoals) {
return returnOrderedGoals(user, listOfOrderedGoals)
},
};
I have no idea what the original bug was, but now that it's gone, I can rest easy. I also don't really expect anyone to come across the same issue, but I wanted to post the solution to be thorough.
I am using angular version 9. I have 2 components. 1 to search a car make, model and 2nd component to load the details of the car that is selected from component 1. I pass the image link for the car from the car that is selected from component 1 to component 2 and load that image in component 2. This works fine..
When I browse to some other page in my application and again go to the component 1 and select a different car and go to component 2, the image of the previously selected car shows for around 1-2 seconds and then my new image is shown. This looks like a browser caching problem. I have tried to load a loader image initially till my new image completes loading and then show the newly loaded image but it does not work. What I have tried is as below.
<img *ngIf="isLoading" [src]="loadingimage.png" />
<img [hidden]="isLoading" [src]="carModelImage" (load)="isLoading = false"/>
What I have noticed is, the moment component 2 is opened the (load) event on the img tag fires and changes the isLoading value to false even without waiting for the new carModelImage to load and thus the previous image shows for some time.
My component code looks like below.
#Component({
selector: 'app-request-summary',
templateUrl: './request-summary.component.html',
styleUrls: ['./request-summary.component.scss']
})
export class RequestSummaryComponent implements OnInit, OnDestroy {
public carModelImage: string = '';
public isLoading: boolean = true;
constructor() {
this.carModelImage = '';
this.isLoading = true;
}
ngOnInit() {
this.requestSummaryService.carData
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((data) => {
this.carModelImage = data[0].carModelImage ? data[0].carModelImage
:'http://nocarimage.png';
}
}
ngOnDestroy() {
this.isLoading = true;
this.carModelImage = '';
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
I am struglling due to this issue since quite a long. Any help will be highly appreciated.
Well, I don't have much to go on here, but I'll share a simple pattern with you.
When your image is no longer being displayed null it out.
For example somewhere in your code you probably write this:
carModelImage = "your-image";
When you are sure you no longer need this var anymore, null it out.
carModelImage = null;
Post more code if you want help specific to your issue.
How do you assign carmodelImage variable . The carmodelImage is responsible for showing the previous image, if you could nullify it on destroy may be it should help
Assuming you said ngondestroy is being called
I cant seem to figure out what the issue is,
I have a service that has a behavior subject like so...
popupSource = new BehaviorSubject<any>('');
popup(component) {
this.popupSource.next(component);
}
then In my header component
popupClick() {
this._service.popup('example');
}
then in my header.html
<button (click)="popupClick()"></button>
then In my app component
ngOnInit() {
this._service.popupSource.subscribe((result) => {
console.log(result);
})
}
so whats happening is the click is firing the this._service.popup('example'); but its never hitting the subscription...
I've put breakpoints on each function and It succesfully reaches this.popupSource.next(component) but then nothing?? Every Time I click the button I should be getting the console log.
Im not sure what Im doing wrong... Ive left out code for brevity sake so please let me know if you need more information
EDIT
Ive also tried doing
private popupSource = new BehaviorSubject<any>('');
getPopup = this.popupSource.asObservable();
popup(component) {
this.popupSource.next(component);
}
and the in my app component listend to the getPopup instead
ngOnInit() {
this._service.getPopup.subscribe((result) => {
console.log(result);
})
}
and that's not working either, I cant figure out what the problem is...
Thanks
Your issue here is that you provide your service in two different modules, that end up with two instances of your service. The simplest way if you are using Angular v6 is to use the providedIn flag in your service:
import { Injectable } from '#angular/core';
#Injectable({providedIn: 'root'})
class myService {}
With this way you don't need to provide your service in the providers array of any module, it will be automatically provided in the root injector.
Documentation about this can be found here : Angular providers.
If your are using Angular v5 you should only provide your service in your AppModule.
In your service, write a new method like this:
popupSource = new BehaviorSubject<any>('');
getPopupSource() {
return this.popupSource.asObservable();
}
And subscribe to that instead of subscribing directly to the subject itself. You could just add the 'asObservable()' part whenever you subscribe to it instead of creating an entirely new method, but I like the separate method so I can keep the Subject private, and I usually subscribe to something multiple times in different places throughout an app, so it cuts down on repetition.
In your component :
this._service.getPopupSource().subscribe( result => { etc...})
EDIT :
Demo recreation of your scenario - https://stackblitz.com/edit/angular-n6esd5
You may not have the same instance of service, my same problem was that I had #Injectable({ providedIn: 'root'}) for my service, but also I put this service in the component providers [] array, just remove the providers array, then it works
I am quite new to angular2 and I have a problem with the change detection.
At the loading of my page, I need to call some API in order to get the information to construct my web page. What I do is that when I receive this information (which is contained in an array) I want to iterate through it using *ngFor. This is my code for a course component.
import {Component,Input} from 'angular2/core';
import {courseCompDiagram, sepExInWeeks} from "../js/coursesTreatment.js";
import {getSampleWeeks} from "../js/courseMng.js";
#Component({
selector: 'course',
directives:[Exercises],
template: `
<div class="course">
<h2>{{aCourse.name}}</h2>
<div class='diag-container row'>
<div id="Completion{{aCourse.name}}"></div>
<div *ngFor="#week of weeks"> {{week.weekNb}} </div>
</div>
</div>`
})
export class Course{
//This is inputed from a parent component
#Input() aCourse;
this.weeks = [];
ngAfterViewInit(){
//I call this method and when the callbacks are finished,
//It does the following lines
courseCompDiagram(this.aCourse, function(concernedCourse){
//When my API call is finished, I treat the course, and store the results in weeks
this.weeks = sepExInWeeks(concernedCourse.course.exercises);
});
//This is not supposed to stay in my code,
//but is here to show that if I call it here,
//the weeks will effectively change
this.weeks = getSampleWeeks();
}
}
So first of all, I would like to know if it's normal that angular2 doesnt detect the fact that this.weeks changed.
Then I don't know if I should us the ngAfterViewInit function to do my work in. The problem is that I began doing that because in my courseCompDiagram I need to use jquery to find the div containing the id Completion[...] and modify it (using highcharts on it). But maybe I should do all this at some other point of the loading of the page ?
I tried using ngZone and ChangeDetectionStrategy as stated in this topic but I didn't manage to make it work on my case.
Any help is appreciated, even if it doesn't completely solves the problem.
export class Course{
//This is inputed from a parent component
#Input() aCourse;
this.weeks = [];
constructor(private _zone:NgZone) {}
ngAfterViewInit(){
//I call this method and when the callbacks are finished,
//It does the following lines
courseCompDiagram(this.aCourse, (concernedCourse) => {
//When my API call is finished, I treat the course, and store the results in weeks
this._zone.run(() => {
this.weeks = sepExInWeeks(concernedCourse.course.exercises);
});
});
//This is not supposed to stay in my code,
//but is here to show that if I call it here,
//the weeks will effectively change
this.weeks = getSampleWeeks();
}
}
You should use arrow functions to be able to use lexical this, as described below:
courseCompDiagram(this.aCourse, (concernedCourse) => {
// When my API call is finished, I treat the course,
// and store the results in weeks
this.weeks = sepExInWeeks(concernedCourse.course.exercises);
});
As a matter with raw callbacks, the this keyword doesn't correspond to your component instance.
See this link for more hints about the lexical this of arrow functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions.
Otherwise I have a sample comment regarding your code. You should leverage observables for your HTTP calls. It doesn't seem the case in your code as far as I can see...