Vue3 script setup without all the imports from vue - vuejs3

I find it very repetitive to have to import vue things like:
import { ref, computed } from 'vue'
In the script setup section.
Would it be a bad practice to, let's say assign vue to a special character, like $ and then use it to access these like
let drawer = $.ref(null);
If so what would be the reasoning behind?

You can use the experimental version of vue3:
// vite.config.js
export default {
plugins: [
vue({
reactivityTransform: true
})
]
}
after that there is an auto import available and you don't have to write .value if using a $ref or $computed.
Because $ref() is a macro and not a runtime API, it doesn't need to be imported from vue.

Related

Import .stories file into another .stories file with Storybook?

Can you import one .stories file into another .stories with Storybook?
Eg I have
/component1/component1.tsx
/component1/component1.stories.tsx
/component2/component2.tsx
/component2/component2.stories.tsx
I would like to also have a story for all of my components:
In /all-components/all-components.stories.tsx
import * as React from 'react';
import Component1Story from '../component1/component1.stories.tsx';
import Component2Story from '../component2/component2.stories.tsx';
export const Test = () => {
return (
<div>
<Component1Story />
<Component2Story />
</div>
);
};
export default {
title: 'Components',
};
I get this error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of storyFn.
this should be doable as your stories are just React components. Your problem is happening because you're trying to import the default from your module, which is actually just an object:
export default {
title: 'Components',
};
All stories are named exports, and you should import them with destructuring:
import { Component1Story } from '../component1/component1.stories';
import { Component2Story } from '../component2/component2.stories';
I created an example for you which shows a working scenario here.
p.s. It's interesting to know that starting with Storybook 6 there's a new mechanism to simplify the creation and reuse of stories so stay tuned! It's called Args.

Next.js Global CSS cannot be imported from files other than your Custom <App>

My React App was working fine, using global CSS also.
I ran npm i next-images, added an image, edited the next.config.js, ran npm run dev, and now I'm getting this message
Global CSS cannot be imported from files other than your Custom <App>. Please move all global CSS imports to pages/_app.js.
Read more: https://err.sh/next.js/css-global
I've checked the docs, but I find the instructions a little confusing as I am new to React.
Also, why would this error happen now? Do you think it has anything to do with the npm install?
I've tried to remove new files I've added along with their code, but this doesn't fix the problem. I've also tried what the Read more: suggests.
My highest tier component.
import Navbar from './Navbar';
import Head from 'next/head';
import '../global-styles/main.scss';
const Layout = (props) => (
<div>
<Head>
<title>Bitcoin Watcher</title>
</Head>
<Navbar />
<div className="marginsContainer">
{props.children}
</div>
</div>
);
export default Layout;
My next.config.js
// next.config.js
const withSass = require('#zeit/next-sass')
module.exports = withSass({
cssModules: true
})
My main.scss file
#import './fonts.scss';
#import './variables.scss';
#import './global.scss';
my global.scss
body {
margin: 0;
}
:global {
.marginsContainer {
width: 90%;
margin: auto;
}
}
The thing I find the weirdest is that this error came without changing anything to do with CSS, or Layout.js, and it was previously working?
I've moved my main.scss import to the pages/_app.js page, but the styles still aren't coming through. This is what the _app.js page looks like
import '../global-styles/main.scss'
export default function MyApp({ Component, props }) {
return <Component {...props} />
}
Use the built-in Next.js CSS loader (see here)
instead of legacy #zeit/next-sass.
Replace #zeit/next-sass package with sass.
Remove next.config.js. Or do not change CSS loading in it.
Move the global CSS as suggested in the error message.
Since Next.js 9.2 global CSS must be imported in Custom <App> component.
// pages/_app.js
import '../global-styles/main.scss'
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
To add styles only to a specific component or page you can use built-in support of CSS modules. (see here)
For example, if you have a component Button.js you can create a Sass file button.module.scss and include it in the component.
Next.js stops complaining when your file has module in naming, e.g., changing import '../global-styles/main.scss'; to import '../global-styles/main.module.scss'; would fix the warning and you could have your styles in the global-styles, or for example, in your component.
No extra dependencies/configurations in next.config.js is required.
You can replace the opinionated (and overly-complex?) NextJs CSS loaders with your own. Here's a simple one for global css:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
reactStrictMode: true,
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
// Find and remove NextJS css rules.
const cssRulesIdx = config.module.rules.findIndex(r => r.oneOf)
if (cssRulesIdx === -1) {
throw new Error('Could not find NextJS CSS rule to overwrite.')
}
config.module.rules.splice(cssRulesIdx, 1)
// Add a simpler rule for global css anywhere.
config.plugins.push(
new MiniCssExtractPlugin({
experimentalUseImportModule: true,
filename: 'static/css/[contenthash].css',
chunkFilename: 'static/css/[contenthash].css',
})
)
config.module.rules.push({
test: /\.css$/i,
use: !isServer ? ['style-loader', 'css-loader'] : [MiniCssExtractPlugin.loader, 'css-loader'],
})
return config
},
}
Add this to your _app.js
import '../styles/globals.css'
For me the problem was because I had used two module.exports in my next.config.js file like this
const withPlugins = require('next-compose-plugins')
const sass = require('#zeit/next-sass')
const css = require('#zeit/next-css')
const nextConfig = {
webpack: function(config){
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]'
}}
})
return config
}
}
module.exports = withPlugins([
[css],
[sass, {
cssModules: true
}]
], nextConfig)
module.exports = {
env: {
MONGO_URI = 'your uri'
}
}
. 1I modified it to change the export module like this.
const nextConfig = {
webpack: function(config){
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]'
}}
})
return config
},
env: {
MONGO_URI: "your uri"
}
}
2then I deleted the second module.exports
This node package provides a perfect solution for it. You can find it here
Steps to fix it:
1. Add package:
npm install next-remove-imports
or
yarn add next-remove-imports
2. Add this wrapper variable inside your next.config.js
const removeImports = require('next-remove-imports')({
test: /node_modules([\s\S]*?)\.(tsx|ts|js|mjs|jsx)$/,
matchImports: "\\.(less|css|scss|sass|styl)$"
});
All it is doing is re-enabling global styling import rule for tsx|ts|js|mjs|jsx files
3. Wrap your next config export with this next-remove-imports wrapper. Something like this:
module.exports = removeImports((nextConfig)
4. Now restart your react app and you will be able to import CSS files inside any ts|js|js|jsx|mjs file or component.
Try to include ".module" in your scss file name.
Change main.scss to main.module.scss
Example:
import styles from './todolist-profile-info.module.scss'
You did not need to do anything inside of next.config.js.
Let's assume you are using a global css like Bootstrap, meaning it contains css that is meant to be applied to your entire application and all the different pages inside of it.
Global css files have to be wired up to NextJS in a very particular fashion.
So inside of the pages/ directory you need to create _app.js.
It's critical that the file be named _app.js.
Then at the top of that file you would import Bootstrap css in the following manner:
import 'bootstrap/dist/css/bootstrap.css';
Then you would add the following:
export default ({ Component, pageProps }) => {
return <Component {...pageProps} />;
};
So what is going on in that code?
Well, behind the scenes, whenever you try to navigate to some distinct page with NextJS, NextJS will import your component from one of the different files inside your pages/ directory.
NextJS does not just take your component and show it on the screen.
Instead it wraps it up inside of its own custom default component and that is referred to inside of NextJS as the App.
What you are doing by defining the _app.js is to define your own custom app component.
So whenever you try to visit a route inside a browser or your root route, NextJS is going to import that given component and pass it into the AppComponent as the Component prop.
So Component there is equal to whatever components you have in the pages/ directory. And then pageProps is going to be the set of components that you are intending to pass to your files inside of pages/.
So long story short, this thing is like thin wrapper around the component that you are trying to show on the screen.
Why do you have to define this at all?
Well, if you ever want to include some global css to the project, Bootstrap being a global css for example, you can only import global css into the _app.js file.
It turns out that if you try to visit other components or other pages, NextJS does not load up or even parse those files.
So any css you may have imported inside there will not be included in the final HTML file.
So you have a global css that must be included on every single page, it has to be imported into the app file because it's the only file that is guaranteed to be loaded up every single time a user goes to your application.
Don't forget that in addition to importing the css inside of _app.js, you also have to run an npm install bootstrap in your terminal.
You can read more on this here:
https://nextjs.org/docs/messages/css-global
For me, i got this error because I had used improper naming for my project's parent folder, had used special characters in it,
like project#1,
after removing special chars, and changing the folder name to like project-1, the error got away.
In my case there was typo in navbar.module.css
I've written navbar.moduile.css
you must for every component css/scss write navbar.module.css/scss/sass.Next js doesnt compile navbar.css/scss/sass. If hope my answer helps you !.

what is the proper way to use createContainer() in Meteor + Blaze + React?

i have a working component where i'm doing this:
import React, {Component, PropTypes} from 'react';
import {createContainer} from 'meteor/react-meteor-data';
export default class Foo extends Component {
}
export default createContainer(() => {
}, Foo);
import Foo from '/imports/ui/components/Foo';
i am using Blaze to wrap the React components, like this:
import Foo from '/imports/ui/components/Foo';
Template.registerHelper("Foo", function() {
return Foo;
);
<div>
{{> React component=Foo}}
</div>
i realize that i shouldn't be doing multiple default exports in a single file, but it does work. note that this is with these versions: Meteor v1.4.1.1, Meteor npm v3.10.6, Meteor node v4.5.0.
i now have a test harness, with Meteor v1.4.2.3, Meteor npm v3.10.9 and Meteor node v4.6.2, where this has stopped working. not surprisingly, in my server console:
While building for web.browser:
imports/ui/components/Foo.jsx:58: Only one default export allowed
per module. (58:0)
so now i'm looking for a way to get this back working, and in the proper way.
what i've tried:
first, keeping the component and the create container in the same file, i did proper ES6 exporting:
const Foo = class Foo extends Component {
const FooContainer = createContainer(() => {
export {Foo, FooContainer};
... and imported Foo.
result: Foo loaded in the app, but the container code never ran.
second, i put the component and the create container in two different files, and reverted to exporting defaults:
// Foo.jsx
export default class Foo extends Component {
// FooContainer.jsx
export default createContainer(() => {
... and used Foo:
import Foo from '/imports/ui/components/Foo';
Template.registerHelper("Foo", function() {
return Foo;
});
<div>
{{> React component=Foo}}
</div>
result: Foo loaded in the app, but the container code never ran.
third, similar to the above, but i instead tried putting FooContainer on the page:
import FooContainer from '/imports/ui/components/FooContainer';
Template.registerHelper("Foo", function() {
return FooContainer;
});
<div>
{{> React component=Foo}}
</div>
result: big error message from React that basically i wasn't doing it right.
any idea on the proper way to get this to work?
update:
attempts 4 and 5
put both back into same class, like this:
export class Foo extends Component {
export default createContainer(() => {
... with 2 different ways of importing it:
import Foo from '/imports/ui/components/Foo';
that ran createContainer() but did not put my component on the page.
import {Foo} from '/imports/ui/components/Foo';
that did the opposite: did not run createContainer(), but i did see the component.
got it working, in 1 jsx file:
export class Foo extends Component {
export default createContainer(() => {
in the helper, relying on the default export:
import Foo from '/imports/ui/components/Foo';
the actual problem was i had incorrectly imported a server file to publish, and that caused a chain reaction which caused the component to not render. doh.

Missing observable methods RxJS 5.0.0-beta.0

I'm having a problem using RxJS with Angular 2. Most of methods suggested from Typescript definition file are not defined on my Observable object like...
then I figured out, that methods does not exists on the Observable prototype.
I know a lot of things changed from version 4 to 5,so do I miss something?
Browserify added it for me...
Without seeing your actual code, I can't tell you exactly what to add to fix it.
But the general problem is this: RxJS 5 is not included with Angular 2 any longer now that it has entered the Beta stage. You will need to import either the operator(s) you want, or import them all. The import statements looks like this:
import 'rxjs/add/operator/map'; // imports just map
import 'rxjs/add/operator/mergeMap'; // just mergeMap
import 'rxjs/add/operator/switchMap'; // just switchMap
import {delay} from 'rxjs/operator/delay'; // just delay
or like
import 'rxjs/Rx'; // import everything
To determine the path to your desired module, look at the source tree. Every import with add will add properties to Observable or Observable.prototype. Without add, you'd need to do import {foo} from 'rxjs/path/to/foo'.
You will also need to make sure that RxJS is being brought into the project correctly. Something like this would go into your index.html file:
System.config({
map: {
'rxjs': 'node_modules/rxjs' // this tells the app where to find the above import statement code
},
packages: {
'app': {defaultExtension: 'js'}, // if your app in the `app` folder
'rxjs': {defaultExtension: 'js'}
}
});
System.import('app/app'); // main file is `app/app.ts`
If you use Webpack to build the Angular 2 app like in this Github project (like I did), then you don't need that System stuff and the imports should do it.
Yes, in Angular 2.0 you have to include the operators/observables you need.
I do it like this:
import 'rxjs/operator/map';
import 'rxjs/operator/delay';
import 'rxjs/operator/mergeMap';
import 'rxjs/operator/switchMap';
import 'rxjs/observable/interval';
import 'rxjs/observable/forkJoin';
import 'rxjs/observable/fromEvent';
However, you also need to configure this in System.js
System.config({
defaultJSExtensions: true,
paths: {
'rxjs/observable/*' : './node_modules/rxjs/add/observable/*.js',
'rxjs/operator/*' : './node_modules/rxjs/add/operator/*.js',
'rxjs/*' : './node_modules/rxjs/*.js'
}
});
Here is working code: https://github.com/thelgevold/angular-2-samples
I have a JSPM setup in my project, so adding rxjs to the path section was not enough.
jspm added the following to my SystemJS configuration (map section):
"npm:angular2#2.0.0-beta.6": {
"crypto": "github:jspm/nodelibs-crypto#0.1.0",
"es6-promise": "npm:es6-promise#3.1.2",
"es6-shim": "npm:es6-shim#0.33.13",
"process": "github:jspm/nodelibs-process#0.1.2",
"reflect-metadata": "npm:reflect-metadata#0.1.2",
"rxjs": "npm:rxjs#5.0.0-beta.0",
"zone.js": "npm:zone.js#0.5.14"
},
So if you use jspm make sure you remove the rxjs mapping above, otherwise some rxjs files will be loaded twice, once via jspm_packages and once via node_modules.

Moment.js with Vuejs

I try to print out date time using like the following in vue-for
{{ moment().format('MMMM Do YYYY, h:mm:ss a') }}
but, it does not appear. It's just a blank. How I can try to use moment in vue?
With your code, the vue.js is trying to access the moment() method from its scope.
Hence you should use a method like this:
methods: {
moment: function () {
return moment();
}
},
If you want to pass a date to the moment.js, I suggest to use filters:
filters: {
moment: function (date) {
return moment(date).format('MMMM Do YYYY, h:mm:ss a');
}
}
<span>{{ date | moment }}</span>
[demo]
If your project is a single page application, (eg project created by vue init webpack myproject),
I found this way is most intuitive and simple:
In main.js
import moment from 'moment'
Vue.prototype.moment = moment
Then in your template, simply use
<span>{{moment(date).format('YYYY-MM-DD')}}</span>
In your package.json in the "dependencies" section add moment:
"dependencies": {
"moment": "^2.15.2",
...
}
In the component where you would like to use moment, import it:
<script>
import moment from 'moment'
...
And in the same component add a computed property:
computed: {
timestamp: function () {
return moment(this.<model>.attributes['created-at']).format('YYYY-MM-DD [at] hh:mm')
}
}
And then in the template of this component:
<p>{{ timestamp }}</p>
I made it work with Vue 2.0 in single file component.
npm install moment in folder where you have vue installed
<template>
<div v-for="meta in order.meta">
{{ getHumanDate(meta.value.date) }}
</div>
</template>
<script>
import moment from 'moment';
export default {
methods: {
getHumanDate : function (date) {
return moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY');
}
}
}
</script>
Here is an example using a 3rd party wrapper library for Vue called vue-moment.
In addition to binding Moment instance into Vue's root scope, this library includes moment and duration filters.
This example includes localization and is using ES6 module imports, an official standard, instead of NodeJS's CommonJS module system requires.
import Vue from 'vue';
import moment from 'moment';
import VueMoment from 'vue-moment';
// Load Locales ('en' comes loaded by default)
require('moment/locale/es');
// Choose Locale
moment.locale('es');
Vue.use(VueMoment, { moment });
Now you can use the Moment instance directly in your Vue templates without any additional markup:
<small>Copyright {{ $moment().year() }}</small>
Or the filters:
<span>{{ 3600000 | duration('humanize') }}</span>
<!-- "an hour" -->
<span>{{ [2, 'years'] | duration('add', 1, 'year') | duration('humanize') }}</span>
<!-- "3 years" -->
// plugins/moment.js
import moment from 'moment';
moment.locale('ru');
export default function install (Vue) {
Object.defineProperties(Vue.prototype, {
$moment: {
get () {
return moment;
}
}
})
}
// main.js
import moment from './plugins/moment.js';
Vue.use(moment);
// use this.$moment in your components
For moment.js at Vue 3
npm install moment --save
Then in any component
import moment from 'moment'
...
export default {
created: function () {
this.moment = moment;
},
...
<div class="comment-line">
{{moment(new Date()).format('DD.MM.YYYY [ ] HH:mm')}}
</div>
Moment.js with Vue3 js
npm install moment --save # npm
yarn add moment # yarn
Main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import moment from 'moment'
const app = createApp(App)
app.config.globalProperties.$moment = moment
app.use(router).mount('#app')
Used moments in vue3 js component
{{ $moment(item.created_at).format("YYYY-MM-DD") }} // 2021-07-03
global members are not available by default in your <template>'s scope. But you can easily pass them on using computed properties.
computed: {
moment: () => moment,
console: () => console,
window: () => window
}
Now you can use any of them in your template. i.e: console.log(moment(), window).
Note this doesn't add any overhead.
vue-moment
very nice plugin for vue project and works very smoothly with the components and existing code.
Enjoy the moments...😍
// in your main.js
Vue.use(require('vue-moment'));
// and use in component
{{'2019-10-03 14:02:22' | moment("calendar")}}
// or like this
{{created_at | moment("calendar")}}
I've read the solutions posted here and it seems to be more complex than my solution so I'm presenting this one, what I do is like this
The thing you need:
import moment from 'moment';
...
data() {
return {
moment: moment, // This is the one line of code that you need
}
}
So this is what it looks like
HTML (Now this works):
<h1>{{moment().format('MMMM Do YYYY, h:mm:ss a')}}</h1>
JS:
import moment from 'moment';
export default {
data() {
return {
moment: moment, // This is the one line of code that you need
}
}
}
I'd simply import the moment module, then use a computed function to handle my moment() logic and return a value that's referenced in the template.
While I have not used this and thus can not speak on it's effectiveness, I did find https://github.com/brockpetrie/vue-moment for an alternate consideration
TESTED
import Vue from 'vue'
Vue.filter('formatYear', (value) => {
if (!value) return ''
return moment(value).format('YYYY')
})
Install the moment module:
npm i moment
In your vue component:
import moment from 'moment';
export default {
data(){
},
methods:{
moment(date){ return moment(date) }
}
}
Inside the template:
<span>{{ moment().format('MMMM Do YYYY, h:mm:ss a') }}</span>

Resources