How can I use vue-router-tab in Vue3? - tabs

I installed vue-router-tab. (npm i vue-router-tab -S) and configured.
main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import RouterTab from "vue-router-tab";
import "vue-router-tab/dist/lib/vue-router-tab.css";
const app = createApp(App);
app.use(RouterTab);
app.use(store);
app.use(router);
app.mount("#app");
router/index.js
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
import { RouterTabRoutes } from "vue-router-tab";
import Frame from "../components/layout/Frame.vue";
const routes = [
{
path: "/",
component: Frame,
children: [
...RouterTabRoutes,
{
path: "/",
name: "Home",
component: Home,
meta: {
title: "Home",
},
},
{
path: "/about",
name: "About",
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
meta: {
title: "About",
},
},
],
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
components/layout/Frame.vue
<template>
<router-tab />
</template>
<script>
export default {
name: "Frame",
setup() {},
};
</script>
App.vue
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</template>
<script>
export default {};
</script>
npm run serve throws error.
It works fine in vue2, but in vue3, an error occurs saying the tabs property is not defined.
How can I use vue-router-tab in Vue3? help me please.

Related

Router in Vue not workin

I have tried a 100 times but my router is not working at all!
I cant just navigate or switch to different pages in my vue project. I followed all the steps given in the link https://www.thepolyglotdeveloper.com/2017/11/router-navigate-pages-vuejs-application/ too, but no change.
Here are my codes:
main.js
import Vue from 'vue'
import App from './App.vue'
import "bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
//import router from '../router'
//import VueRouter from 'vue-router'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
index.js
import Vue from 'vue'
import Router from 'vue-router'
import Page1 from '#/components/page1'
import Page2 from '#/components/page2'
Vue.use(Router)
export default new Router({
routes: [
{
path: "/",
redirect: {
name: "Page1"
}
},
{
path: '/page1',
name: 'Page1',
component: Page1
},
{
path: '/page2',
name: 'Page2',
component: Page2
}
]
})
Now here is an example of one of my page whos address i expect to be localhost:8080/page1 but its not coming..
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<router-link to="/page2">Navigate to Page2</router-link>
</div>
</template>
<script>
export default {
name: 'Page1',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
SOMEONE PLEASE TELL ME WHAT TO DO
Your main.js must look like:
import Vue from 'vue'
import App from './App.vue'
import "bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
import router from '../router';
Vue.config.productionTip = false;
new Vue({
render: h => h(App),
router,
}).$mount('#app')
Also in App.vue you must have <RouterView />:
<template>
<RouterView />
</template>
Source: https://router.vuejs.org/guide/#html

Exclude JSON files from the main bundle with webpack for react-lottie

In our web app we have a few JSON files that are ~10-80k lines each. These are getting included in our main bundle. These are used by an animation plugin called react-lottie.
An example of our webpack.config.js
module.exports = {
entry: ["./src/index.js"],
module: {
rules: [
{ test: /\.(js|jsx)$/, exclude: /node_modules/, use: ["babel-loader"] },
{
test: /\.(jpg|png|gif|ico)$/,
use: {
loader: "file-loader",
options: { name: "[path][name].[hash].[ext]" }
}
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
output: {
path: __dirname + "/dist",
publicPath: "/",
filename: "[name].[hash].js"
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({ hash: false, template: "src/index.html" }),
new DashboardPlugin(),
new CopyWebpackPlugin([
{
from: "src/components/Assets/BookingBar.js",
to: "assets/BookingBar.js"
}
]),
new BundleAnalyzerPlugin()
],
devServer: {
contentBase: "./dist",
hot: true,
historyApiFallback: true,
port: 4000
}
};
What is the expected behavior?
There should be a way to exclude .json files from the main bundle. I've tried File-Loader, json-loader, and const someJson = require(./someJson)
Other relevant information:
webpack version: 4.16.1
Node.js version: 10.12.0
Operating System: Mac OS 10.14 Mojave
ANSWER BELOW (AT LEAST FOR HOW I SOLVED IT). I couldn't initialize the lottie without any data.
The expected behavior is that the JSON will get bundled because it's, presumably, needed synchronously at runtime. JSON data differs from something like image files which are loaded asynchronously by the browser as they are rendered on the page via src attributes etc.
As the comments mentioned, you should be using code splitting. The latest version of Webpack supports dynamic imports if you install and use the #babel/plugin-syntax-dynamic-import plugin.
npm install --save-dev #babel/plugin-syntax-dynamic-import
Then in babel.config.js:
module.exports = {
...
plugins: [
"#babel/plugin-syntax-dynamic-import"
]
...
};
Example
Say you have a React component that might need some JSON data, but doesn't need to load it synchronously as part of the bundle. Your non-code splitted version might look something like this:
import React from 'react';
import myJSON from './myJSON.json';
export default class MyComponent extends React.Component {
render() {
return <div>{JSON.stringify(myJSON, null, 2)}</div>
}
}
Instead you can use a dynamic import - basically a runtime import that returns a Promise you can use to asynchronously load some data chunked separately from your bundle:
import React from 'react';
import myJSON from './myJSON.json';
export default class MyComponent extends React.Component {
state = {data: {}};
componentDidMount() {
import(/* webpackChunkName: 'myJSON' */ './myJSON.json')
.then((data) => {
this.setState({data});
});
}
render() {
return <div>{JSON.stringify(this.state.data, null, 2)}</div>
}
}
Alternately, you can use React's new lazy and Suspense API (v16.6.0 and higher) to dynamically import React components that get chunked separately from the bundle. This might be preferable if you want to chunk a component and its corresponding JSON data together, but separately from the main bundle:
// MyComponent.jsx
import React from 'react';
import myJSON from './myJSON.json';
export default class MyComponent extends React.Component {
render() {
return <div>{JSON.stringify(myJSON, null, 2)}</div>
}
}
// SomeParent.jsx
import React, {lazy, Suspense} from 'react';
const MyComponent = lazy(() => import(/* webpackChunkName: 'MyComponent' */ './MyComponent'));
export default class SomeParent extends React.Component {
render() {
return <div>
<Suspense fallback={<div>Loading...<div>} >
<MyComponent />
</Suspense>
</div>;
}
}
In the above example, <MyComponent /> and its corresponding code -- including the JSON data -- will only be loaded when the component is actually rendered at runtime.
Ultimately I took the answer above below me but wasn't able to initialize the lottie without any JSON data. I ended up doing this:
import React, { PureComponent } from "react"
import Lottie from 'react-lottie'
export default class AnimationAutomatedCommunication extends PureComponent {
constructor(props) {
super(props)
this.state = {
animation: <div />
}
}
async componentDidMount() {
const animation = await import(/* webpackChunkName: "AnimationAutomatedCommunication" */ './JsonData/AnimationAutomatedCommunication.json')
const defaultOptions = {
loop: true,
autoplay: true,
animationData: animation.default
}
this.setState({
animation: <div className={this.props.className}>
<Lottie key="lottie-win-jobs" options={defaultOptions}
isStopped={this.props.isStopped} />
</div>
})
}
render() {
return (
<React.Fragment>
{this.state.animation}
</React.Fragment>
)
}
}

Cannot go directly to dynamic route with react-router and webpack-dev-server

After starting webpack-dev-server, I can go directly to a static route (e.g. http://localhost:3456/one), but I cannot go directly to a dynamic route (e.g. http://localhost:3456/two/1234).
I believe I am missing something in my webpack-dev-server config, but not sure what.
The browser console outputs this error:
GET http://localhost:3456/two/dev-bundle.js 404 (Not Found)
Refused to execute script from 'http://localhost:3456/two/dev-bundle.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled
webpack.config.js
const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require("webpack")
module.exports = {
mode: "development",
devtool: "eval-source-map",
entry: [
"./index.js",
],
output: {
filename: "dev-bundle.js",
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
exclude: /node_modules/,
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "dev.html")
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
historyApiFallback: true,
hot: true,
port: 3456,
stats: "minimal"
}
}
app.js
import React, { Component } from "react"
import { hot } from "react-hot-loader"
import { BrowserRouter as Router, Switch, Route } from "react-router-dom"
import ComponentOne from "./components/ComponentOne"
import ComponentTwo from "./components/ComponentTwo"
const MyApp = () => (
<div>
<Router>
<Switch>
<Route exact path="/" component={ComponentOne} />
<Route exact path="/one" component={ComponentOne} />
<Route path="/two/:id" component={ComponentTwo} />
</Switch>
</Router>
</div>
)
export default hot(module)(MyApp)
ComponentTwo.js
import React, { Component } from "react"
import { Link } from "react-router-dom"
export default class ComponentTwo extends Component {
render() {
return (
<div>
<h1>ComponentTwo for {this.props.match.params.id}</h1>
</div>
)
}
}
Any help is appreciated.
I was able to resolve this by updating part of the webpack config:
output: {
filename: "dev-bundle.js",
publicPath: "/", // added this line
},
The console error remains, but at least the page loads.

Angular testing: Failed to load html component

I caught this error when testing a component with templateUrl. I don't know how to fix it.
After reading a post I added TestBed.resetTestEnvironment() and TestBed.initTestEnvironment() in the test file, but it doesn't solve the problem.
It seems like something is missing in the config file and the html file cannot be loaded.
Here are the karma logs:
Here is my karma.config.js file:
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', 'karma-typescript'],
files: [
// Zone:
'./node_modules/zone.js/dist/zone.js', // 'Uncaught ReferenceError: Zone is not defined'
'./node_modules/zone.js/dist/proxy.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
'./node_modules/zone.js/dist/sync-test.js',
'./node_modules/zone.js/dist/async-test.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
'./node_modules/zone.js/dist/fake-async-test.js',
'./node_modules/zone.js/dist/jasmine-patch.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
// Angular:
'./node_modules/angular/angular.js',
'./node_modules/#uirouter/angularjs/release/angular-ui-router.js',
'./node_modules/angular-mocks/angular-mocks.js',
// ANY OTHER FILES TO LOAD FOR YOUR TESTS
// App:
'./assets/app/app.component.ts',
'./assets/app/app.component.html',
'./assets/app/app.component.spec.ts',
],
exclude: [
'./assets/app/main.aot.ts'
],
preprocessors: {
"**/*.ts": "karma-typescript"
},
reporters: ['spec', 'karma-typescript'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
concurrency: Infinity,
mime: {
'text/x-typescript': ['ts', 'tsx']
}})}
In the following you can find the app.component.ts file:
import { Component } from '#angular/core';
import { Constants } from './utils/constants';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styles:[
`
#status {
background:#f8f9fa;
bottom:0;
left:0;
margin:0;
padding:0;
position:fixed;
width:100%;
}
#status div {
margin:0;
padding:5px;
text-align:center;
color:#c8c9ca;
}
`
]})
export class AppComponent {
appTitle = "App Title";
demoMode=(Constants.DEMO_MODE)?"Demo":"DefaultMode";
}
Finally, I attach the app.component.spec.ts file:
import { AppComponent } from "./app.component";
import { ComponentFixture, TestBed } from "#angular/core/testing";
import { BrowserDynamicTestingModule,
platformBrowserDynamicTesting } from '#angular/platform-browser-dynamic/testing';
import { Constants } from "./utils/constants";
describe('AppComponent', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let h1: HTMLElement;
beforeEach(() => {
TestBed.resetTestEnvironment();
TestBed.initTestEnvironment(BrowserDynamicTestingModule,
platformBrowserDynamicTesting());
TestBed.configureTestingModule({
declarations: [AppComponent],
})
.compileComponents().then(()=>{
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
h1 = fixture.nativeElement.querySelector('h1');
}).catch((error)=>{
console.log(error);
});
});
it('should display original title', () => {
expect(h1.textContent).toContain("App Title");
});
it('status bar text should correspond to the working mode', () =>{
let text=(Constants.DEMO_MODE)?"Demo":"DefaultMode";
expect(document.getElementById("status-text").textContent).toEqual(text);
});
});
Thanks in advance!
You should be taking the nativeElement of the the text box as below,
h1 = fixture.nativeElement.querySelector('h1').nativeElement;
Also, please query the fixture inside the it statement and add the fixture.detectChanges(); inside the it to trigger the change detection

Using Jest to test a Link from react-router v4

I'm using jest to test a component with a <Link> from react-router v4.
I get a warning that <Link /> requires the context from a react-router <Router /> component.
How can I mock or provide a router context in my test? (Basically how do I resolve this warning?)
Link.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import { Link } from 'react-router-dom';
test('Link matches snapshot', () => {
const component = renderer.create(
<Link to="#" />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
The warning when the test is run:
Warning: Failed context type: The context `router` is marked
as required in `Link`, but its value is `undefined`.
You can wrap your component in the test with the StaticRouter to get the router context into your component:
import React from 'react';
import renderer from 'react-test-renderer';
import { Link } from 'react-router-dom';
import { StaticRouter } from 'react-router'
test('Link matches snapshot', () => {
const component = renderer.create(
<StaticRouter location="someLocation" context={context}>
<Link to="#" />
</StaticRouter>
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Have a look at the react router docs about testing
I had the same issue and using StaticRouter would still require the context which needed more configuration to have it available in my test, so I ended up using the MemoryRouter which worked very well and without any issues.
import React from 'react';
import renderer from 'react-test-renderer';
import { MemoryRouter } from 'react-router-dom';
// SampleComponent imports Link internally
import SampleComponent from '../SampleComponent';
describe('SampleComponent', () => {
test('should render', () => {
const component = renderer
.create(
<MemoryRouter>
<SampleComponent />
</MemoryRouter>
)
.toJSON();
expect(component).toMatchSnapshot();
});
});
The answer of #Mahdi worked for me! In 2023 if you want to test a component that includes <Link> or <NavLink>, we just need to wrap it with the <MemoryRouter> in the test file:
// App.test.js
import { render, screen } from "#testing-library/react";
import MyComponent from "./components/MyComponent";
import { MemoryRouter } from "react-router-dom"; // <-- Import MemoryRouter
test("My test description", () => {
render(
<MemoryRouter> // <-- Wrap!
<MyComponent />
</MemoryRouter>
);
});
my test like this:
import * as React from 'react'
import DataBaseAccout from '../database-account/database-account.component'
import { mount } from 'enzyme'
import { expect } from 'chai'
import { createStore } from 'redux'
import reducers from '../../../reducer/reducer'
import { MemoryRouter } from 'react-router'
let store = createStore(reducers)
describe('mount database-account', () => {
let wrapper
beforeEach(() => {
wrapper = mount(
< MemoryRouter >
<DataBaseAccout store={store} />
</MemoryRouter >
)
})
afterEach(() => {
wrapper.unmount()
wrapper = null
})
})
but I don't konw why MemoryRouter can solve this。
Above solutions have a common default defact:
Can't access your component's instance! Because the MemoryRouter or StaticRouter component wrapped your component.
So the best to solve this problem is mock a router context, code as follows:
import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
describe('YourComponent', () => {
test('test component with react router', () => {
// mock react-router context to avoid violation error
const context = {
childContextTypes: {
router: () => void 0,
},
context: {
router: {
history: createMemoryHistory(),
route: {
location: {
hash: '',
pathname: '',
search: '',
state: '',
},
match: { params: {}, isExact: false, path: '', url: '' },
}
}
}
};
// mount component with router context and get component's instance
const wrapper = mount(<YourComponent/>, context);
// access your component as you wish
console.log(wrapper.props(), wrapper.state())
});
beforeAll(() => {
configure({ adapter: new Adapter() });
});
});