observable subscription does not work when written in ngOnit() in IE - angular5

I have defined a behavior subject:
component.ts
bsub1: BehaviorSubject<Array<any>> = new BehaviorSubject(null);
And called next() on it:
html:
<button (click) = getData()>Data</button>
component.ts:
getData() {
getSomething(value, bsub1);
}
service.ts
getSomething(value, _observable) {
//do something
_observable.next(retun)
somevariable.map(p => {
return { "a": value1, "b": value2 }
});
}
And made a subscription:
component.ts
ngOnInit() {
bsub1.subscribe(list=>{
console.log("subscribed to bsub1")
});
}
But my code does not reach upto subscription after call to getSomething().
It reaches there when component loads but when next() is called after that it does not subscribe.
this happen only in IE 11 whereas in edge, chrome and firefox it is working.
Can someone explain me this behavior?

Related

How to update same fixture file within the same BeforEeach hook and get the updated details as the response in Cypress [duplicate]

I am trying to write a test with the new cypress 6 interceptor method (Cypress API Intercept). For the test I am writing I need to change the reponse of one endpoint after some action was performed.
Expectation:
I am calling cy.intercept again with another fixture and expect it to change all upcomming calls to reponse with this new fixture.
Actual Behaviour:
Cypress still response with the first fixture set for the call.
Test Data:
In a test project I have recreated the problem:
test.spec.js
describe('testing cypress', () => {
it("multiple responses", () => {
cy.intercept('http://localhost:4200/testcall', { fixture: 'example.json' });
// when visiting the page it makes one request to http://localhost:4200/testcall
cy.visit('http://localhost:4200');
cy.get('.output').should('contain.text', '111');
// now before the button is clicked and the call is made again
// cypress should change the response to the other fixture
cy.intercept('http://localhost:4200/testcall', { fixture: 'example2.json' });
cy.get('.button').click();
cy.get('.output').should('contain.text', '222');
});
});
example.json
{
"text": "111"
}
example2.json
{
"text": "222"
}
app.component.ts
import { HttpClient } from '#angular/common/http';
import { AfterViewInit, Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
public text: string;
public constructor(private httpClient: HttpClient) { }
public ngAfterViewInit(): void {
this.loadData();
}
public loadData(): void {
const loadDataSubscription = this.httpClient.get<any>('http://localhost:4200/testcall').subscribe(response => {
this.text = response.body;
loadDataSubscription.unsubscribe();
});
}
}
app.component.html
<button class="button" (click)="loadData()">click</button>
<p class="output" [innerHTML]="text"></p>
Slightly clumsy, but you can use one cy.intercept() with a Function routeHandler, and count the calls.
Something like,
let interceptCount = 0;
cy.intercept('http://localhost:4200/testcall', (req) => {
req.reply(res => {
if (interceptCount === 0 ) {
interceptCount += 1;
res.send({ fixture: 'example.json' })
} else {
res.send({ fixture: 'example2.json' })
}
});
});
Otherwise, everything looks good in your code so I guess over-riding an intercept is not a feature at this time.
As of Cypress v7.0.0 released 04/05/2021, cy.intercept() allows over-riding.
We introduced several breaking changes to cy.intercept().
Request handlers supplied to cy.intercept() are now matched starting with the most recently defined request interceptor. This allows users to override request handlers by calling cy.intercept() again.
So your example code above now works
cy.intercept('http://localhost:4200/testcall', { fixture: 'example.json' });
// when visiting the page it makes one request to http://localhost:4200/testcall
cy.visit('http://localhost:4200');
cy.get('.output').should('contain.text', '111');
// now cypress should change the response to the other fixture
cy.intercept('http://localhost:4200/testcall', { fixture: 'example2.json' });
cy.get('.button').click();
cy.get('.output').should('contain.text', '222');
Cypress command cy.intercept has the
times parameter that you can use to create intercepts that only are used N times. In your case it would be
cy.intercept('http://localhost:4200/testcall', {
fixture: 'example.json',
times: 1
});
...
cy.intercept('http://localhost:4200/testcall', {
fixture: 'example2.json',
times: 1
});
See the cy.intercept example in the Cypress recipes repo https://github.com/cypress-io/cypress-example-recipes#network-stubbing-and-spying
const requestsCache = {};
export function reIntercept(type: 'GET' | 'POST' | 'PUT' | 'DELETE', url, options: StaticResponse) {
requestsCache[type + url] = options;
cy.intercept(type, url, req => req.reply(res => {
console.log(url, ' => ', requestsCache[type + url].fixture);
return res.send(requestsCache[type + url]);
}));
}
Make sure to clean requestsCache when needed.

Supabase client doesn't fetch anything

First of all, sorry if I make english mistakes
I'm making this side-project for fun using Vue3 & Vite and it's my first time using Supabase
I created the 'players' table in the Supabase dashboard and added two rows for testing purposes.
I used createClient to initialize the client :
// client.js
import { createClient } from "#supabase/supabase-js"
const supabaseInfos = {
url: "https://example.supabase.co",
key: "example"
}
export const client = createClient(supabaseInfos.url, supabaseInfos.key)
Then I used the client in order to make a Player Controller :
// Player.js
import { client } from "../../lib/client";
export default class Player {
static async index() {
return await client.from('players').select('*')
}
static async show(id) {
return await client.from('players').select('*').match({ id })
}
static async create(data) {
return await client.from('players').insert(data)
}
static async update(id, data) {
return await client.from('players').update(data).match({ id })
}
static async delete(id) {
return await client.from('players').delete().match({ id })
}
}
Please take note that I'm using the official documentation
The main problem is, it doesn't work. The client isn't fetching any data, what it all do is returning an empty array.
// App.vue
<template>
<ul v-if="players.length > 0">
<li v-for="player in players" :key="player.id">{{ player.name }}</li>
</ul>
<p v-else>No player found.</p>
</template>
<script>
import Player from './assets/controllers/Player'
export default {
data() {
return {
players: []
}
},
methods: {
async getAllPlayers() {
const { data } = await Player.index()
this.players = data
console.log(data) // returns an empty array
console.log(this.players) // returns an empty Proxy object
}
},
mounted() {
this.getAllPlayers()
},
}
</script>
I really don't understand why the client never returns anything. I got the api key and url right and my database is public. It returns a 200 code but there's nothing to use when the Promise comes
Instead of using createClient, I instanciated a SupabaseClient, which changed nothing at all. I also tried to use client.query() but it didn't work either...

Use nuxt.js dynamic routing to pass parameters

I am going to query the database to return the result after receiving a parameter in the dynamic route, and find that the console reports an error.
TypeError: Cannot read property 'title' of null
When I went to see the request, I found that the first request returned the data, and then sent the same request but the spliced parameter was null and reported the error.
This is the second request 304
This is my page code.
`
<templat>
<div class="wrapper qa-content">
<div class="qa-title">
<div class="fl title">
<h2>{{problem.title}}</h2>
<p>
<span
>{{labes(index)}}</span>
<span>{{timeago(problem.createtime)}}</span>
</p>
</div>
import "~/assets/css/page-sj-qa-detail.css";
import axios from "axios";
import problemApi from "#/api/problem";
import replyApi from "#/api/reply";
import labelApi from "#/api/label";
export default {
asyncData({ params }) {
return axios
.all([
problemApi.findById(params.id),
replyApi.findByProId(params.id),
problemApi.findPL(params.id)
])
.then(
axios.spread(function(pojo, replyList, labelList) {
return {
problemId: params.id,
replyList: replyList.data.data,
problem: pojo.data.data,
labelList: labelList.data.data
};
})
);
},
data() {
return {
CurrentreplyId: "",
commentList: [],
labelName: [],
textarea: "",
dialogVisible: false,
content: "",
editorOption: {
// some quill options
modules: {
toolbar: [
[{ size: ["small", false, "large"] }],
["bold", "italic"],
[{ list: "ordered" }, { list: "bullet" }],
["link", "image"],
["blockquote", "code-block"]
]
}
}
};
},
mounted() {
console.log("app init, my quill insrance object is:", this.myQuillEditor);
},
methods: {
labes(index) {
console.log(this.labelList);
labelApi.findOne(this.labelList[index].labelid).then(res => {
this.labelName.push(res.data.data.labelname);
console.log(this.labelName);
});
},
check(id) {
console.log(id);
replyApi.findByParentid(id).then(res => {
this.commentList = res.data.data;
});
},
shows(item) {
console.log(item.id);
if (item.content === null || item.content === "" || item.content === "") {
return false;
} else {
return true;
}
}
`
This page is dynamically routed from the previous page.
<nuxt-link :to="'/qa/items/'+item.id" target="_blank">{{item.title}}</nuxt-link>
Ok. I think that helps. If I understand you correctly you do the following:
You are on a previous page
You click on the nuxt-link with the item id in the route
Your new route loads and you get an error: Because your page fetches the data from the API twice (but you did not reload the page) is that correct?
If so, I am not sure why your asyncData is executed twice, but you could probably solve it like this:
asyncData({ params }) {
if (params.id) {
return axios
.all([...
}
This would make sure that your request is only made ifan ID is present and it would not send null.
Honestly I don't know why the request is resent...
I also don't really understand how your API looks like. I see that it somehow returns promises, and that you can call methods on it... Something I haven't seen in such a context, but OK :).
Besides that you seem to execute further API calls in your methods.
Maybe that is the problem:
<span>{{labes(index)}}</span>
I don't see what would be passed into this method. This index is not defined...
When then calling the
labes() (did you mean labels?) method, you execute
labelApi.findOne(this.labelList[index].labelid)
but as index is undefined, I think this.labelList[index] will not return something useful and you make a request there?
(Depending on what your api.findOne() method does. Could it be that itself sends a request to an actual remote API?)
Cheers

Onsen + VueJS: Call back from child component (using onsNavigatorProps)

Per documentation here
If page A pushes page B, it can send a function as a prop or data that modifies page A’s context. This way, whenever we want to send anything to page A from page B, the latter just needs to call the function and pass some arguments:
// Page A
this.$emit('push-page', {
extends: pageB,
onsNavigatorProps: {
passDataBack(data) {
this.dataFromPageB = data;
}
}
});
I am following this idea. Doing something similar with this.$store.commit
I want to push AddItemPage and get the returned value copied to this.items
//Parent.vue
pushAddItemPage() {
this.$store.commit('navigator/push', {
extends: AddItemPage,
data() {
return {
toolbarInfo: {
backLabel: this.$t('Page'),
title: this.$t('Add Item')
}
}
},
onsNavigatorProps: {
passDataBack(data) {
this.items = data.splice() //***this*** is undefined here
}
}
})
},
//AddItemPage.vue
...
submitChanges()
{
this.$attrs.passDataBack(this, ['abc', 'xyz']) // passDataBack() is called, no issues.
},
...
Only problem is this is not available inside callback function.
So i can't do this.items = data.splice()
current context is available with arrow operator.
Correct version:
onsNavigatorProps: {
passDataBack: (data) => {
this.items = data.splice()
}
}

IE not calling lifecycle hooks or filling #Input until change detected?

I'm using beta.0 because this outstanding bug prevents angular 2 from working in IE in beta.1 and beta.2.
Relevant code from SearchBar.ts
#Component({
selector : 'search-bar',
templateUrl: 'views/searchbar.html'
})
export class SearchBar {
private history: SearchHistoryEntry[] = [];
#Output() onHistory = new EventEmitter();
constructor() {
this.history = JSON.parse(localStorage.getItem('SearchHistory')) || [];
}
ngOnInit() {
// The constructor doesn't have #Outputs initialized yet. Emit from this
// life cycle hook instead to be sure they're received on the other side
debugger;
this.onHistory.emit(this.history);
}
}
Relevant code from home.html
<search-bar (onHistory)="SearchBarHistory($event)"></search-bar>
Relevant code from home.ts
SearchBarHistory(history: SearchHistoryEntry[]) {
debugger;
this.history = history;
}
In Chrome this works just fine. The SearchBar's constructor correctly reads from localStorage, in ngOnInit it emits to my Home component who receives it, it's stored locally and the UI bindings tied to history update to show the information as it all should.
In IE 11 this does not work. ngOnInit won't run until I click inside my search bar. It seems that any #Input or lifecycle hook (specifically I've tested ngOnInit, ngAfterContentInit, and ngAfterViewInit and they all behave the same) doesn't run until the component's change detection is triggered. If I refresh the page then it runs exactly like Chrome where no interaction is required for #Inputs or lifecycle hooks to be called and my history goes through and gets bound like it should.
I think this is a bug of the beta but in the mean time is there anything I can do to make it work the first time without an interaction or page refresh?
I am having the same issue I tried it to resolve by forcing detectChanges like:
import {Injectable,ApplicationRef, NgZone} from '#angular/core';
#Injectable()
export class IeHackService {
constructor(
private _appRef: ApplicationRef,
private _zone: NgZone) {}
private isIe() {
let ua = window.navigator.userAgent;
let msie = ua.indexOf('MSIE ');
if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) // If Internet Explorer, return version number
return true;
return false;
}
onComponentLoadInIe() {
if (this.isIe()) {
this._zone.run(() => setTimeout(() => this._appRef.tick(), 5));
}
}
}
Then in Every Route component that uses Lifecycle Hooks I called
constructor(private dialogService: ModalDialogService,ieHackService: IeHackService) {
ieHackService.onComponentLoadInIe();
}
I had this issue as well, I used a workaround to automatically refresh the page if the bug occurs, hoping that the bug will eventually be solved.
It's very ugly, but for now it works at least.
declare var Modernizr: any;
#Component({
selector : 'search-bar',
templateUrl: 'views/searchbar.html'
})
export class SearchBar {
private history: SearchHistoryEntry[] = [];
#Output() onHistory = new EventEmitter();
ie11hack: boolean = true;
constructor() {
this.history = JSON.parse(localStorage.getItem('SearchHistory')) || [];
if (!Modernizr.es6collections || navigator.userAgent.toLowerCase().indexOf("safari") !== -1) {
setTimeout(() => {
if (this.ie11hack) {
window.location.reload();
}
}, 500);
}
}
ngOnInit() {
ie11hack = false;
// The constructor doesn't have #Outputs initialized yet. Emit from this
// life cycle hook instead to be sure they're received on the other side
debugger;
this.onHistory.emit(this.history);
}
}
Edit, a less ugly partial fix:
The issues is (I think) caused by using a direct url rather then using the angular router (javascript).
If you want your website to work if people enter their url manually then you still need the above hack, but otherwise you may do what I did below (you may want to do that anyway).
I changed this:
<!-- html -->
<a href="#/objects/objectthingy/{{myObject.id}}" class="my-object-class">
To this:
<!-- html -->
<a href="javascript:void(0)" (click)="openMyObject(myObject.id)" class="my-object-class">
// typescript
openMyObject(objectId: number) {
this.router.navigate(['/Objects', 'ObjectThingy', { id: objectId}]);
}
and ngAfterViewInit method was called again.