Nativescript Vue: access class functions - vuejs2

I'm currently building an app with Nativescript and Vuejs.
I use the Material BottomNavigationBar (https://github.com/Akylas/nativescript-material-components/blob/master/packages/nativescript-material-bottomnavigationbar/README.md).
There are two methods included i need to use:
selectTab(index: number)
showBadge(index: number, value?: number)
Now I need to call these methods and there is the issue. How do I do that?
My code:
main.js
import BottomNavigationBar from 'nativescript-material-bottomnavigationbar/vue';
import BottomNavigationTab from 'nativescript-material-bottomnavigationbar/vue';
Vue.use(BottomNavigationBar);
Vue.use(BottomNavigationTab);
Footer.vue:
<BottomNavigationBar titleVisibility="Never" activeColor="#0285ff" inactiveColor="#5c687c"
backgroundColor="#f5f5f5" #tabSelected="onBottomNavigationTabSelected" row="1"
class="footer" ref="navBar" :selectedTab="2">
<BottomNavigationTab icon="~/assets/images/logo.png"/>
<BottomNavigationTab icon="~/assets/images/chat.png"/>
<BottomNavigationTab icon="~/assets/images/settings.png"/>
</BottomNavigationBar>
...
mounted() {
this.$refs.navBar.nativeView.selectTab(2)
}
This is not working and says that nativeView is undefined.
Is there a way to access these class methods?
Regards,
Tobias

Found the solution!
It is necessary to wait until the component is loaded.
My way now:
Component:
<BottomNavigationBar titleVisibility="Never" activeColor="#0285ff" inactiveColor="#5c687c"
backgroundColor="#f5f5f5" #tabPressed="pressedNavigation" #tabSelected="pressedNavigation"
row="1" class="footer" ref="navBar" #loaded="loaded">
<BottomNavigationTab icon="~/assets/images/logo.png"/>
<BottomNavigationTab icon="~/assets/images/chat.png"/>
<BottomNavigationTab icon="~/assets/images/settings.png"/>
</BottomNavigationBar>
Method:
loaded(args) {
this.loadedNavBar = true;
this.navBar = args.object
if (this.selectedTab !== null) this.navBar.selectTab(this.selectedTab)
},
I select the index and store it in a varaible. When the component is loaded I can adjust the selection.

Your component has yet to load when mounted gets triggered.
I'd suggest using a loaded event on your navbar component and then trigger your method.
<BottomNavigationBar titleVisibility="Never" activeColor="#0285ff" inactiveColor="#5c687c"
backgroundColor="#f5f5f5" #tabSelected="onBottomNavigationTabSelected" row="1" #loaded="onNavbarLoaded"
class="footer" ref="navBar" :selectedTab="2">
<BottomNavigationTab icon="~/assets/images/logo.png"/>
<BottomNavigationTab icon="~/assets/images/chat.png"/>
<BottomNavigationTab icon="~/assets/images/settings.png"/>
</BottomNavigationBar>
onNavbarLoaded(evt) {
this.$refs.navBar.nativeView.selectTab(2);
// I am not sure if you actually have to easy nativeView..
this.$refs.navBar.selectTab(2);
}

You can put a ref="mybar" in the tags and then find it under this.$refs.mybar inside your component script.
Just like what was made on this example.
See vue documentation for more details.

Related

Unable to add elements using the setAttribute

I am using the VUE JS code and trying to add the setAttribute to some of the tags.
Here is the code I am using :
changetab() {
const demoClasses = document.querySelectorAll(".delCon__select");
demoClasses.forEach(button => {
button.setAttribute("tabindex", "0");
});
return true;
},
but when I view in the code inspector, It does not show added to it, I have added the above function in computed.
template is like this :
<template>
<el-container class="orders"></el-download>
</template>
You need to make this type of request in Vue's Lifecycles, like: created or mounted.
Something like:
mounted() {
this.changetab()
}
Computed would not be the most appropriate place for this type of action.

How to test my tooltip component using Vue.js and Jest?

I'm trying to test my tooltip component, but it seems it does not exist :cry:
My .html
<div>
<boxComponent>
Some text
<tooltipComponent
#mouseover.native="handleHover(true)"
#mouseleave.native="handleHover(false)"
>This text appears on Hover</tooltipComponent>
</boxComponent>
<switchComponent button-type="button" :label="false" #change="activeFun" />
</div>
My .js
methods: {
handleHover (s) {
this.onHoverTooltip = s
},
}
My .spec.js
const localVue = createLocalVue()
localVue.use(Vuex)
//...
it('should reveal tooltip\'s mesage', () => {
const wrapper = shallowMount(ozFilters, {
propsData: {
//others stuffs,
label: false,
},
localVue,
store,
stubs: ['tooltipComponent', 'boxComponent', 'switchComponent'],
})
expect(wrapper.find('tooltipComponent-stub').exists()).toBeFalsy()
// wrapper.vm.label = true
wrapper.vm.handleHover(true)
expect(wrapper.find('tooltipComponent-stub').exists()).toBeTruthy()
})
I need to understand what should I do to test the tooltip component that is already a custom component.
Even without the -stub it does not work.
The error is occurring in this line expect(wrapper.find('tooltipComponent-stub').exists()).toBeTruthy() with says that the expect is false.
Well, there are a couple of things that need to be fixed/clarified.
First of all, you are using shallowMount to create a component which you want to test, it stubs all custom components provided in tested component so you don't have to additionally use stub parameter to stub them, you can easily delete this: stubs: ['tooltipComponent', 'boxComponent', 'switchComponent'].
When you want to find specific component it's recommended to use findComponent instead of just find. (Using find to search for a component is actually deprecated). To properly identify component it's best to import it in your test file and use it in findComponent argument, something like this:
import BoxComponent from 'path/to/BoxComponent'
it('lovely test', () => {
wrapper.findComponent(BoxComponent)
})
After this your test should pass, but there are some things to consider like using mount instead of shallowMount. It's explained here, but to wrap it up, mount will render actual component with its children, where shallowMount stubs components inside tested component, so you are not testing the actual component but some "shadow" of its true nature. To see a difference between this functions I would recommend to create a wrapper with both functions and then see what html() will return from them.

Why in my nuxt-link doesn't reload page with same url?

If I’m on a page with the URL 'http://localhost:8080/item' and I’m clicking on the same link on this page, then the page does not reload.
I need to make that if I click on the same link, the page will reload.
My link:
<nuxt-link :to="/item">
Any insight will be welcome. Thanks!
Use key, something like:
<router-view :key="$route.params.yourCustomParam"/>
Also you can use something like:
<router-link :to="{ params: { yourCustomParam: Data.now } }" replace>link</router-link>
Remember to is passed router.push() and it accept an object also. Doing that, it is more declarative and controllable. I'm using this to decide if the page of component should be rerendered since they will based on id params obtained from URL entry, and my child component can still using nesting .
I recently tried to solve a similar issue and to overcome this I used Vuex with :key (ref).
Firstly, in your store you need a state property such as:
export const state = () => ({
componentUpdates: {
item: 0,
//can add more as needed
}
})
In general, you could use only one property across the app if you prefer it that way. Just remember that later on, the key value needs to be unique - that is in the case if you used this property for two or more components within one page, for example. In this case, you could do something like this :key="$store.getters.getComponentUpdates.item+'uniqueString'"
then a getter:
export const getters = {
getComponentUpdates(state) {
return state.updateComponent;
}
}
finally a mutatation:
export const mutations = {
updateComponent(state, payload) {
return state.componentUpdates[payload.update]++
}
}
Now we can utilise the reactive :key wherever needed.
But first in your nuxt-link lets add an event to trigger the mutation, note the usage of #click.native to trigger the click event:
<nuxt-link #click.native="$store.commit('updateComponent', { update: 'item'})" :to="/item">
Now in the item page, for example. Let's imagine there is a component that needs to be updated. In this case we would add :key to it:
<my-item :key="$store.getters.getComponentUpdates.item" />
That is it. As you can see this solution utilises the benefits of nuxt-link but also allows us to selectively update only parts of our page that need updates (we could update the entire page this way as well if needed).
In case if you needed to trigger the logic from mounted or initial load in general, then you could use computed property and :key to your div container, right inside the <template> of your page.
Add :key to the div:
<template>
<div :key="$store.getters.getComponentUpdates.item"></div>
</template>
Create computed property:
computed: {
updateItemPage() {
//run your initial instructions here as if you were doing it in mounted then return the getter
this.initialLoadMethod()
return this.$store.getters.getComponentUpdates.item
}
}
The final touch, which is not crucial but can be implemented in order to reset the state property:
export const mutations = {
updateComponent(state, payload) {
return state.componentUpdates[payload.update] >= 10
? state.componentUpdates[payload.update] = 0
: state.componentUpdates[payload.update]++
}
}

Vue test utils - setChecked() not updating v-model

I am writing unit tests for some components I made at my job. We are using Mocha (TDD) and the Chai assertion library. I have a component with some checkboxes, and using the setChecked() method on them from vue-test-utils is not behaving as expected. I have made a small example that reproduces the error:
TestComponent.vue:
<template>
<div>
<input class="checkboxTest" type="checkbox" v-model="cbVal">
<input class="inputTest" type="text" v-model="textVal">
</div>
</template>
<script>
define([], function() {
return {
data: function() {
return {
cbVal: false,
textVal: ""
}
}
}
})
</script>
test.js:
suite("Random test", function() {
var VueTest;
var TestComponent;
//Import the vue test utils library and TestComponent
suiteSetup(function(done) {
requirejs(
["vue-test-utils", "vuec!components/TestComponent"],
function(VT, TC) {
VueTest = VT;
TestComponent = TC;
done();
}
);
});
//This test passes
test("fill in the input", function() {
var wrapper = VueTest.mount(TestComponent);
wrapper.find(".inputTest").setValue("Hello, world!");
assert.equal(wrapper.vm.textVal, "Hello, world!");
});
//This one does not
test("programatically check the box", function() {
var wrapper = VueTest.mount(TestComponent);
wrapper.find(".checkboxTest").setChecked(true);
//Prints out AssertionError: expected false to equal true
assert.equal(wrapper.vm.cbVal, true);
});
});
The textVal data member in TestComponent is getting changed, but cbVal is not. Can anyone please explain why setValue() works just fine, but setChecked() does not? Thank you in advance.
I had a similar issue and the accepted answer did not solve my problem. I don't think the accepted answer is correct either, as setChecked was added specifically to avoid having to manually set the values via the elements.
In my case, I wanted Vue to react to the v-model change and redraw. I tried async and many other methods, until finding the one that works: wrapper.vm.$forceUpdate().
Here's what my working code looks like:
wrapper.find("#someRadioButtonId").setChecked(true)
// manually force Vue to update
wrapper.vm.$forceUpdate()
expect(wrapper.find("#someRadioButtonId").classes()).toContain("selected") // success!
I can't answer why it doesn't work, but I can tell you your approach is incorrect in the first place.
You shouldn't be interacting with the html elements directly to set their values. When you set vue-model to cbVal you should instead be interacting with cbVal.
In other words, change your code from setChecked() to cbVal = true in order for it to comply with how Vue wants you to develop your project. There's no guarantee Vue can remain dynamic and reactive if you don't interact with your code the way Vue wants you to.

How to use Vue Router programatically with native events?

I have been trying to use native events with Vue router using this.$router.push and I keep producing the following error:
[Vue warn]: Invalid handler for event "click": got undefined
found in
---> <section>
I can't seem to figure out why and I have to use multiple solutions, all of which produce the above error. Some of them redirect to the page directly before triggering the event.
Things that I have tried:
1.
//in JSX
<section nativeOnClick={this.navigationPage('login')}></section>
// in methods I have the following:
methods: {
navigationPage: function (page) {
this.$router.push(page)
}
}
2.
//in JSX
<section nativeOnClick={this.$router.push('login')}></section>
One solution that works is the following:
//in JSX
<section nativeOnClick={this.navigateToLogin}></section>
// in methods I have the following:
methods: {
navigateToLogin: function () {
this.$router.push(page)
}
}
Could you please let me know what I am doing wrong in 1 and 2. The solution that works is fine but that requires me to create a method for every navigation. I personally like 2!!
Any help is appreciated
NEW
Below is the full component definition:
//navbar.js
import styles from './index.styl'
import logo from 'Src/assets/logo.svg'
import menu from 'Src/assets/menu.svg'
export default {
name: 'NavBar',
methods: {
navigationPage: function (page) {
this.$router.push(page)
}
},
render (h) {
return (
<ui-nav class={styles.nav}>
<ui-section class={styles.navLogo} nativeOnClick={ this.navigationPage('hello') }>
<ui-image source={logo} />
</ui-section>
<ui-spacer />
<ui-section class={styles.navMenu}>
<ui-image source={menu} />
</ui-section>
</ui-nav>
)
}
}
Note: This style also automatically navigates to /hello (i.e. before click event). I do not know why.
Have you tried removing the this reserved word from your jsx native invocation?, for example:
<section nativeOnClick={ navigationPage('login') }></section>
If you can you post the whole script of your single file component (or whatever you are editing) would be helpful for me to be in context.
It looks like there is one solution that works, but I can't understand why it works though!
working solution:
<section nativeOnClick={ () => this.$router.push('hello') }></section>
The thing that is confusing is why passing arguments without the handler produces the error by when wrapping with a function it does not.
i.e
<section nativeOnClick={ this.$router.push('hello') }></section> // GIVES error