Vue rerender from data by async method - vue.js

I have an icon
that is displaying a favorite/not favorite state with a red or white heart.
html
<img :src="favoriteStatus ? '/icons/liked#3x.png' : '/icons/like#3x.png'">
I call the api to get the status
try {
const res = await .......... ;
if (res.cmd_data.code === 0) {
this.favoriteStatus = res.cmd_data.msg;
this.$forceUpdate();
} else {
....
}
} catch ......
res.cmd_data.msg is a Boolean.
My vue devtools are showing that favoriteStatus can be correctly changed (true or false).
But the heart icon not changing and $forceUpdate() is not working neither.

You could maybe try this form
<img :src="`url(${require('/icons/${favoriteStatus ? liked : like}#3x.png')})`
EDIT: At the end, it was just some lifecycle issue.
The OP fixed it by fiddling when fetching the API.

Related

Clicking on button in iframe in Cypress

Ran into the issue, where the test code should click the button Process in the iframe. Used npm i cypress-iframe lib, but came up to nothing. Cypress could not find the button.
Tried cy.iframe('[class="resp-iframe"]').find('resp-iframe[id="submit"]')
HTML of the problem
Tried the other ways to click on iframe button:
cy.get('iframe[class="resp-iframe"]').then($element => {
const $body = $element.contents().find('body')
cy.wrap($body).find('resp-iframe[class="btn btn-block btn-primary"]').eq(0).click();
})
also
cy.get('[class="resp-iframe"]').then($element => {
const $body = $element.contents().find('body')
let stripe = cy.wrap($body)
stripe.find('[class="resp-iframe"]').click(150,150)
})
and
cy.iframe('#resp-iframe').find('[name="submitButton"]')
Error
Error 2
Updated FYI:
The first part of code - clicking the Google button in bottom-right:
const getIframeBody = () => {
// get the iframe > document > body
// and retry until the body element is not empty
return cy
.get('[id="popup-contentIframe"]')
.its('0.contentDocument.body')
// wraps "body" DOM element to allow
// chaining more Cypress commands, like ".find(...)"
// https://on.cypress.io/wrap
.then(cy.wrap)
}
getIframeBody().find('[id="payWithout3DS"]').click()
Then, waiting for secure payment preloader to finish up:
cy.wait(20000)
Then, trying to catch the Process button by suggestions:
cy.iframe('[name="AcsFrame"]').find('#submit').click()
or
cy.iframe('[class="resp-iframe"]').find('[id="submit"]')
whole code part looks:
const getIframeBody = () => {
// get the iframe > document > body
// and retry until the body element is not empty
return cy
.get('[id="popup-contentIframe"]')
.its('0.contentDocument.body')
// wraps "body" DOM element to allow
// chaining more Cypress commands, like ".find(...)"
// https://on.cypress.io/wrap
.then(cy.wrap)
}
getIframeBody().find('[id="payWithout3DS"]').click()
cy.wait(20000)
cy.iframe('[name="AcsFrame"]').find('#submit').click()
But still, getting:
Maybe anyone had something like that?
Thanks.
How about you try this:
cy.iframe('[name="AcsFrame"]').find('#submit').click()
You don't need to repeat the resp-iframe inside the .find().
The selector .find('resp-iframe[id="submit"]') means look for HTML like this: <resp-iframe id="submit"> but the element you want is <input id="submit">.
Everything else looks ok
cy.iframe('[class="resp-iframe"]').find('[id="submit"]')

$refs are null after route change

I have a keyboard navigation system. When you press ArrowUp or ArrowDown, an event is emitted FROM app.js (best place I found to listen to these keypresses since they need to be system-wide) TO the mounted() in the component.
The Event.$on() INSIDE the mounted() part of the component then calls a function that uses $refs to identify the currently selected item and, when ENTER is pressed, show it's modal.
app.js code (listen to the keypresses):
else if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Enter') {
event.preventDefault()
switch (this.$router.currentRoute.path) {
case "/pedidos":
Event.$emit('navegarSetasPedidos', event.key)
break;
case "/clientes":
Event.$emit('navegarSetasClientes', event.key)
break;
}
}
mounted() section of the component in question:
mounted() {
Event.$on('navegarSetasPedidos', (key) => {this.navegarSetas(key)})
}
function responsible for the navigation (sorry for bad formating, haven't figured how stackoverflow's codeblock thing works yet):
navegarSetas(key) {
if (this.navegacaoSetasAtiva == false) {
this.navegacaoSetasAtiva = true
this.navegacaoAtual = 0
} else if (this.modalAtivado == false && this.navegacaoSetasAtiva == true) {
if (key == 'ArrowDown' && this.navegacaoAtual < this.pedidos.length - 1) {
this.navegacaoAtual++
let elementoSelecionado = this.$refs['pedido'+this.navegacaoAtual][0].$el
let boundaries = elementoSelecionado.getBoundingClientRect()
if (boundaries.top < 0 || boundaries.top > (window.innerHeight || document.documentElement.clientHeight)){
elementoSelecionado.scrollIntoView({behavior: 'smooth'})
}
} else if (key == 'ArrowUp' && this.navegacaoAtual <= this.pedidos.length && this.navegacaoAtual > 0) {
this.navegacaoAtual--
let elementoSelecionado = this.$refs['pedido'+this.navegacaoAtual][0].$el
let boundaries = elementoSelecionado.getBoundingClientRect()
if (boundaries.top < 0 || boundaries.top > (window.innerHeight || document.documentElement.clientHeight)){
elementoSelecionado.scrollIntoView({behavior: 'smooth'})
}
} else if (key == 'Enter') {
let pedidoSelecionado = this.pedidos[this.navegacaoAtual].id
Event.$emit('changeShow', pedidoSelecionado)
}
}
This works very well the first time it is acessed. The problem is, if I change the current route to show another component and then return to the previous component, I get a lot of "this.$refs['pedido'+this.navegacaoAtual][0].$el is undefined" errors, but the system still works normally, albeit erratically.
The funny thing is: if I console log "this.$refs['pedido'+this.navegacaoAtual][0].$el is undefined", I'll get an EMPTY log before the errors, then ANOTHER one right below it, this time, not empty.
Everywhere else I've searched this says that the problem is due to how Vue re-renders things, and that I'm calling this event BEFORE it's rendered, which shouldn't be possible since I'm calling it inside mounted().
Any help is greatly appreciated, thank you!
Turns out, after a LOT of searching, the Event.$on event setters also work as the normal JavaScript ones (which makes a lot of sense now that I'm thinking about it)—meaning that you have to destroy them whenever your component is unmounted (aka Destroyed).
Even though VUE Dev Tools was picking only one event after the re-route, it was still firing two (seen through console.log() returning one empty value, a bunch of errors, and another value with filled array AFTER the errors).
The solution to this was simply adding Event.$off('eventName') on the destroyed() function of the component.

iview drawer(transfer=false, inner=true ) show in tag outside rather than inside in IE10

Situation is OK in Chrome but also the IE11
With "transfer"(false) and "inner"(true) set, Drawer work as follow link:
https://run.iviewui.com/prdkRwyB
normally effect
Problem occur when using IE10
The drawer show in tag outside rather than inside.
abnormally effect
And The html code of drawer has been place out of its parent tag
If you use F12 developer tools to check the HTML and CSS, you can see that the drawer is outside the iview card body, it seems that this issue is related to iView, you could contact them and feedback this issue.
The screenshot in IE 11:
The screenshot in IE 10:
I found the problem solution.(iview 3.2.2)
iview/src/directives/tansfer-dom.js
This js file handle the DOM transfer job, which lead to drawer panel transfer out of the parent DOM.
inserted (el, { value }, vnode) {
if ( el.dataset && el.dataset.transfer !== 'true') return false;
el.className = el.className ? el.className + ' v-transfer-dom' : 'v-transfer-dom';
const parentNode = el.parentNode;
if (!parentNode) return;
const home = document.createComment('');
let hasMovedOut = false;
if ( value !== false) {
parentNode.replaceChild(home, el); // moving out, el is no longer in the document
getTarget(value).appendChild(el); // moving into new place
hasMovedOut = true
}
if (!el.__transferDomData) {
el.__transferDomData = {
parentNode: parentNode,
home: home,
target: getTarget(value),
hasMovedOut: hasMovedOut
}
}
},
As file show
if ( value !== false)
The judgment on Line 9 is unappropriated.
After replacing code as below and rebuild the iview by running 'npm run dist',
if( value && value !== false )
drawer show well in IE10

Passing State in React-Navigation from TabsNavigation to Child StackNavigation

UPDATE: Now with a Snack Demo
I've created a demo on snack so you can see the issue first hand and help me demonstrate a solution in actual code.
Steps to duplicate
launch app
Tap "GO TO EVENTTABS" button
Tap each tab, noticing that the eventId is in scope for the first three tabs
Tap "More" tab
Tap "TEAM MEMBERS", noticing that eventId is no longer in scope. This is where the problem lies. How do I pass along eventId?
_____________________________
My App has the following navigation hierarchy, where every instance of <> is just a regular component
App <StackNavigator> {
EventList <>
EventTabs <BottomTabNavigator> {
Quests <>
Leaderboard <>
Gallery <>
More <StackNavigator> {
MoreList <>
TeamMembers <>
}
}
}
Upon entering the app, the user's first screen is EventList. They click a button to navigate into EventTabs, so I'm able to use the navigation.navigate() to transition while passing state like so...
EventList.navigation.navigate(EventTabs, passedParams);
To this point, everything makes sense. But TeamMembers also needs access to the passedParams. I'm confused how to pass those along. Hence my question...how do access passedParams from the TeamMembers component? They seem to be scoped just to the EventTabs.
If the answer is to use navigate.setParams(), then I'm not sure where I'd do that.
If the answer is to use NavigationActions.setParams(), then I'm also not sure where I'd do that.
Unfortunately we don't have good support for this, but you could use a function like this to recursively walk your navigation parents in search of the correct param.
function getParam(navigation, paramName) {
const { getParam, dangerouslyGetParent } = navigation;
let parent = dangerouslyGetParent();
let val = getParam(paramName);
while (val === undefined && parent && parent.getParam) {
val = parent.getParam(paramName);
parent = parent.dangerouslyGetParent();
}
return val;
}
The problem seems to continue for version 5.x of react navigation.
For me this works.
function getParentParam(navigation, paramName) {
const { dangerouslyGetParent } = navigation;
let paramValue = null;
const parent = dangerouslyGetParent();
const routes = parent.dangerouslyGetState().routes;
routes.some((r) => {
paramValue = r.params ? r.params[paramName] : null;
return paramValue !== null;
});
return(paramValue);
}
You can iterate over parent.dangerouslyGetParent() according to the depth level you have

Riot JS unmount all tags in a page and then mount only one tag is not working

I am using Riot JS and in my index.html, I have 3 custom tags - header, login-panel and candidates-panel inside my body. In my main app.js, in the callback function of $(document).ready, I execute the current route and also register a route change handler function. In my switchView, I unmount all custom tags and then try to mount only the tag pertaining to the current view being switched. Here is my code. If I do unmount, then nothing is displayed on the page
index.html
<body>
<header label="Hire Zen" icon="img/user-8-32.png"></header>
<login-panel class="viewTag" id="loginView"></login-panel>
<candidates-panel id="candidatesView" class="viewTag"></candidates-panel>
<script src="js/bundle.js"></script>
</body>
app.js
function switchView(view) {
if(!view || view === '') {
view = 'login'
}
//unmount all other panels and mount only the panel that is required
//TODO: unmount all view panels and mounting only required panel is not working
//riot.unmount('.viewTag')
riot.mount(view+'-panel')
$('.viewTag').hide()
$(view+'-panel').show()
}
$(document).ready(function () {
RiotControl.addStore(new AuthStore())
RiotControl.addStore(new CandidatesStore())
riot.mount('header')
//register route change handler
riot.route(function (collection, id, action) {
switchView(collection)
})
riot.route.exec(function (collection, id, action) {
switchView(collection)
})
})
Answer for riot.js v2.1.0:
The function
riot.unmount(...)
is not available as far as I know. However, you can unmount saved tags.
mytag.unmount(true)
Source
The trick is to remember the mounted tags to be able to unmount them later:
var viewTag = riot.mount(document.getElementById('viewTag'))
viewTag.unmount(true)
You can store all those view tags in an object and loop them to unmount all and mount only the active one.
Source
Answer for 2.3.18
Based on the previous answer and this tutorial I have created following concept:
app.currentPage = null;
var goTo = function(page){
if (app.currentPage) {
app.currentPage.unmount(true); //unmount and keep parent tag
}
app.currentPage = riot.mount(page)[0]; //remember current page
};
riot.route(function() {
console.info("this page is not defined");
//do nothing (alternatively go to 404 page or home)
});
riot.route('/inventory', function(){
goTo('inventory');
});
riot.route('/options', function() {
goTo('options');
});
I think you are looking for riot.util.tags.unmountAll(tags)
How to achieve the goal?
index.html
var tags = [];
some.tag.html
var some = this;
tags.push(some);
unmountAllTags.js
riot.util.tags.unmountAll(tags);