How to use less in Angular component without CLI - css

I've got an application where I have to configure and use less for dynamic theme implementation. The issue is we are not using angular-cli and configuration is bit weird here, so we are manually bootstrapping angular modules.
Following is the configuration of app:
package.json
"dependencies": {
"#angular/common": "^4.0.0",
"#angular/compiler": "^4.0.0",
"#angular/core": "^4.0.0",
"#angular/forms": "^4.0.0",
"#angular/http": "^4.0.0",
"#angular/platform-browser": "^4.0.0",
"#angular/platform-browser-dynamic": "^4.0.0",
"#angular/router": "^4.0.0",
"#angular/upgrade": "^4.0.0",
"#angular/animations": "^4.0.1",
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.8",
"rxjs": "^5.0.1",
"systemjs": "0.19.39",
"zone.js": "^0.8.4"
},
and so on..
main.ts
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { ThemeModule } from "./theme.module";
window.appnamespace.platform = platformBrowserDynamic();
window.appnamespace.AppModule = AppModule
window.appnamespace.ThemeModule = ThemeModule;
theme.module.ts
import { NgModule } from "#angular/core";
import { BrowserModule } from "#angular/platform-browser";
import { ThemeComponent } from "./theme.component";
import { ThemeToolbar } from "./Themes/theme-toolbar/theme-toolbar.component";
import { ThemePreview } from "./Themes/theme-preview/theme-preview.component";
import { ThemeService } from "./services/themeManagement.service";
const PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {
suppressScrollX: true
};
#NgModule({
imports: [
BrowserModule,
],
declarations: [
ThemeComponent,
ThemeToolbar,
ThemePreview,
SetFocusDirective
],
providers: [ThemeService, LocalizeThemeService, CommonService],
bootstrap: [ThemeComponent]
})
export class ThemeModule { }
its component:
#Component({
selector: "my-theme",
templateUrl: "../js/divdrawer/Themes/theme.template.html"
})
export class ThemeComponent implements OnInit {
//
}
and bootstrapping it through javascript like this:
window.appnamespace.platform.bootstrapModule(window.appnamespace.ThemeModule);
theme.preview component
#Component({
selector: "theme-preview",
templateUrl: "../theme-preview/theme-preview.component.template.html",
styleUrls: ['../theme-preview/theme-style.less']
})
export class ThemePreview implements OnInit {
// some code
}
theme-style.less: contains the css
#import "./theme-variable.less";
// some css
theme-variable.less: contains the less variables
#header-bg :red;
#badge-title-bg : #ddd;
I want to use less variables and styles in my theme-preview component to change theme dynamically.
How can I configure less in theme component only.

In your #Component, the property styleUrl has all the style files which need to be complied together into a single CSS file. This is however done by angular CLI.
If you intend to use CLI to generate CSS from less, go to the file called angular.json and change the value of schematics.#schematics/angular:component.style to 'less'.
This however, may not help you in dynamic (run-time) theming. For dynamic theming,
import all the less file using #import statement in any 1 main file
For serer side compilation, compile it using node.js server and less.js when CSS is requested. Therefore, less.js will be a production dependency in your node.js project.
For client side compilation, put the main less file created in step 1 into html's head section as below.
<link href="main.less" rel="stylesheet" type="text/less"/>
Next, import less.js into the main.ts of angular app or use script tag for less.js in the HTML body.
In the #Component, styleUrls can be removed as there is no use of it.
Also, all the component related styles need to be wrapped in 1 class specific to the component(both HTML and less). This will ensure that you achieve the same behavior as that of angular CLI.
Hope this helps. Please comment for any more specific issue in this.

Related

Vuetify CSS missing when i build for production

We purchased a web app written in Vue from someone and we developing to change/improve it. One thing we added was Vuetify so we can use the Vuetify elements and everything has been working great while in development mode, but when we build for production the CSS for Vuetify elements is missing.
I have searched for this online already and have already tried what everybody is suggesting without any luck.
Anybody has an idea of what could be wrong and why npm run build would be missing some of the CSS?
What's weird is that all the UI functionality for Vue elements is working perfectly, just the CSS is missing.
Please see code samples below.
main.js:
import '#fortawesome/fontawesome-free/css/all.css'
import Vue from "vue";
import App from "./App.vue";
import VueMoment from "vue-moment";
import VueAnalytics from "vue-analytics";
import VueMeta from "vue-meta";
import { library } from "#fortawesome/fontawesome-svg-core";
import {
faCoffee,
faPlusCircle,
faChartLine,
faChevronDown,
faMobile,
faEnvelope,
faClock,
faUsers,
faPaperPlane,
faCheckCircle,
faCheck,
faLeaf,
} from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/vue-fontawesome";
import axios from "axios";
import router from "./router";
import store from "./store";
import vuetify from './plugins/vuetify';
import Vuetify from 'vuetify/lib'
library.add([
faCoffee,
faPlusCircle,
faChartLine,
faChevronDown,
faMobile,
faEnvelope,
faClock,
faUsers,
faPaperPlane,
faCheckCircle,
faCheck,
faLeaf,
]);
Vue.use(VueAnalytics, {
id: "xxx",
router,
});
Vue.use(VueMoment);
Vue.use(VueMeta);
Vue.component("font-awesome-icon", FontAwesomeIcon);
Vue.use(Vuetify)
axios.interceptors.response.use(undefined, async function (error) {
if (error.response.status === 401) {
await store.dispatch("auth/logout");
router.push("/login");
}
return Promise.reject(error);
});
// Plugins
// ...
// Sass file
require("./assets/styles/main.css");
Vue.config.productionTip = false;
new Vue({
router,
store,
vuetify,
render: (h) => h(App)
}).$mount("#app");
App.vue:
<template>
<v-app>
<v-main>
<router-view/>
</v-main>
</v-app>
</template>
<style>
.text-white {
color: #fff !important;
}
.text-gray-600 {
color: #757575 !important;
}
.font-semibold, .text-gray-700 {
color: #616161 !important;
}
</style>
package.json:
{
"name": "reviewgrower-spa",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"deploy": "git push dokku master"
},
"dependencies": {
"#fortawesome/fontawesome-svg-core": "^1.2.25",
"#fortawesome/free-solid-svg-icons": "^5.11.2",
"#fortawesome/vue-fontawesome": "^0.1.8",
"#fullhuman/postcss-purgecss": "^1.3.0",
"axios": "^0.19.0",
"chart.js": "^2.9.4",
"core-js": "^2.6.10",
"i": "^0.3.6",
"jquery": "^3.5.1",
"npm": "^6.13.0",
"tailwindcss-spinner": "^0.2.0",
"tailwindcss-toggle": "github:TowelSoftware/tailwindcss-toggle",
"url-parse": "^1.4.7",
"vue": "^2.6.10",
"vue-analytics": "^5.17.2",
"vue-chartjs": "^3.5.1",
"vue-click-outside": "^1.0.7",
"vue-clickaway": "^2.2.2",
"vue-feather-icons": "^4.22.0",
"vue-js-toggle-button": "^1.3.3",
"vue-meta": "^1.6.0",
"vue-moment": "^4.0.0",
"vue-router": "^3.1.3",
"vue-stripe-elements-plus": "^0.2.10",
"vuetify": "^2.4.0",
"vuex": "^3.0.1",
"vuex-persist": "^2.1.1"
},
"devDependencies": {
"#fortawesome/fontawesome-free": "^5.15.2",
"#vue/cli-plugin-babel": "^3.12.1",
"#vue/cli-plugin-eslint": "^3.12.1",
"#vue/cli-service": "^3.12.1",
"babel-eslint": "^10.0.3",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.2.3",
"sass": "^1.32.0",
"sass-loader": "^7.1.0",
"tailwindcss": "^1.1.3",
"vue-cli-plugin-vuetify": "~2.1.0",
"vue-template-compiler": "^2.5.21",
"vuetify-loader": "^1.7.0"
}
}
It's a little tough to understand what is missing where. If you think that is just missing then please try adding css onto the HTML file from the cdn and check the working.
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
I see that you are using webpack to compile the code. So, this could be also something related to webpack configuration. In your webpack rules do you have rules for css and scss. Because vuetify files are in scss.
My webpack configuration is as below when I do these type of circus.
--webpack.config.js--
const path = require("path");
const VuetifyLoaderPlugin = require("vuetify-loader/lib/plugin");
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
watch: true,
entry: {
main: 'main.js'
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
test: /\.vue$/,
use: "vue-loader",
},
{
test: /\.s(c|a)ss$/,
use: [
"vue-style-loader",
"css-loader",
{
loader: "sass-loader",
// Requires sass-loader#^8.0.0
// options: {
// implementation: require('sass'),
// sassOptions: {
// fiber: require('fibers'),
// indentedSyntax: true // optional
// },
// },
},
],
},
],
},
plugins: [
new VueLoaderPlugin(),
new VuetifyLoaderPlugin({
/**
* This function will be called for every tag used in each vue component
* It should return an array, the first element will be inserted into the
* components array, the second should be a corresponding import
*
* originalTag - the tag as it was originally used in the template
* kebabTag - the tag normalised to kebab-case
* camelTag - the tag normalised to PascalCase
* path - a relative path to the current .vue file
* component - a parsed representation of the current component
*/
match(originalTag, { kebabTag, camelTag, path, component }) {
if (kebabTag.startsWith("core-")) {
return [
camelTag,
`import ${camelTag} from '#/components/core/${camelTag.substring(
4
)}.vue'`,
];
}
},
}),
],
}
Check your postcss.config.js, see if it has something to do with the purgecss.
You have to config the whitelist to ignore the vuetify styles.
Here is a sample for your reference:
const autoprefixer = require("autoprefixer");
const postcssImport = require("postcss-import");
const purgecss = require("#fullhuman/postcss-purgecss");
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
let plugins = [];
if (IS_PROD) {
plugins.push(postcssImport);
plugins.push(
purgecss({
content: [
"./src/**/*.vue",
"./public/**/*.html",
`./node_modules/vuetify/src/**/*.ts`,
`./node_modules/vuetify/dist/vuetify.css`
],
defaultExtractor (content) {
const contentWithoutStyleBlocks = content.replace(/<style[^]+?<\/style>/gi, '')
return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || []
},
safelist: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ],
whitelist: [
'container',
'row',
'spacer',
'aos-animate',
'col',
'[type=button]',
'v-application p',
],
whitelistPatterns: [
/^v-.*/,
/^col-.*/,
/^theme-.*/,
/^rounded-.*/,
/^data-aos-.*/,
/^(red|grey)--text$/,
/^text--darken-[1-4]$/,
/^text--lighten-[1-4]$/
],
whitelistPatternsChildren: [
/^post-content/,
/^v-input/,
/^swiper-.*/,
/^pswp.*/,
/^v-text-field.*/,
/^v-progress-linear/
]
})
);
}
module.exports = {
plugins:[
require('cssnano')({
preset: 'default'
}),
require('postcss-pxtorem')({
remUnit:15, //每个rem对应的px值
threeVersion:true
}),
...plugins,autoprefixer
]
}``
You are simply missing an include in your main.js (see vuetify docs):
import 'vuetify/dist/vuetify.min.css'
This will ensure that webpack includes the vuetify styles in the bundled CSS for production. This fixed the same issue for me (i.e. it worked locally but not in production).

Tailwindcss does not work in Vue.js when all process of intergration has been done?

i installed tailwindcss into a vuejs SPA did all the setup
create a assets/css/tailwind.css and added the necessary base styles
imported it in the main.js file
create a postcss.config.js file and copied the required configuration from the official documentation but the tailwind styles don't apply to my markups.
Inside the tailwind.css:
#tailwind base;
#tailwind components;
#tailwind utilities;
Inside the postcss.config.js:
module.exports = {
plugins: [
// ...
require("tailwindcss"),
require("autoprefixer"),
// ...
],
}
Inside the main.js file:
import Vue from "vue"
import App from "./App.vue"
import "./registerServiceWorker"
import router from "./router"
import store from "./store"
import axios from "axios"
import "./assets/css/tailwind.css"
import firebase from "firebase/app"
import "firebase/firestore"
import "firebase/auth"
The package.json file:
"dependencies": {
"autoprefixer": "^9.7.6",
"axios": "^0.19.2",
"core-js": "^3.6.4",
"firebase": "^7.14.2",
"register-service-worker": "^1.7.1",
"tailwindcss": "^1.4.0",
"vue": "^2.6.11",
"vue-router": "^3.1.6",
"vuex": "^3.1.3"
},
I don't know what am doing wrong.
My solution:
step 1: yarn postcss
step 2: create file postcss.config.js and add content:
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
From Tailwind Vue docs:
After the steps you described it is necessary to run vue again.

Bootstrap-vue styling not applying correctly

I am having a lot of trouble getting bootstrap-vue working. See the image of what happens when I copy and paste the navbar component from the boostrap-vue.js.org. Bits and pieces are missing and I don't understand why. When I create b-links I can create router links that work but they have zero styling. The primary button seems to be OK but then often buttons don't look correct and radios are invisible. Basically something is not right.
boostrap vue navbar example
vue navbar screenshot
Package.json
"dependencies": {
"bootstrap": "^4.0.0-alpha.6",
"bootstrap-vue": "^2.0.0-rc.11",
"vue": "^2.5.17",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
},
"devDependencies": {
"#babel/core": "^7.1.0",
"#fortawesome/fontawesome-free": "^5.3.1",
"#vue/cli-plugin-babel": "^3.0.3",
"#vue/cli-plugin-eslint": "^3.0.3",
"#vue/cli-service": "^3.0.3",
"node-sass": "^4.9.3",
"sass-loader": "^7.1.0",
"vue-template-compiler": "^2.5.17"
main.js
import Vue from 'vue';
import BootstrapVue from 'bootstrap-vue';
import App from './App.vue';
import router from './router';
import store from './store';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap-vue/dist/bootstrap-vue.css';
Vue.use(BootstrapVue);
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Make sure you have both vue-style-loader and css-loader in package.json
Check this post

Material 2 attributes in Angular 4 aren't working with webpack, ASP.NET

I'm trying to get Material 2 to work with Angular 4.4.6 using webpack in an ASP.NET Core 2 web application. I get no errors but I currently get no styling and the mat-button attribute has no effect on the output DOM.
I have done the following:
Environment is as follows:
Visual Studio (Professional) 2017 version 15.4.0
ASP.NET Core 2 web application with Angular, from VS template
Update #angular packages to ^4.4.6 in NPM and restore packages
"#angular/animations": "^4.4.6",
"#angular/common": "^4.4.6",
"#angular/compiler": "^4.4.6",
"#angular/compiler-cli": "^4.4.6",
"#angular/core": "^4.4.6",
"#angular/forms": "^4.4.6",
"#angular/http": "^4.4.6",
"#angular/platform-browser": "^4.4.6",
"#angular/platform-browser-dynamic": "^4.4.6",
"#angular/platform-server": "^4.4.6",
"#angular/router": "^4.4.6",
Per the Material guide, run the following command in the project directory:
npm install --save #angular/material #angular/cdk
Update webpack.config.vendor.js to add the following lines to the treeShakableModules array, just after '#angular/router':
'#angular/material',
'#angular/material/prebuilt-themes/deeppurple-amber.css',
In app.module.browser.ts, import the module:
import { MatButtonModule } from '#angular/material';
#NgModule({
bootstrap: [ AppComponent ],
imports: [
BrowserModule,
MatButtonModule,
AppModuleShared
],
providers: [
{ provide: 'BASE_URL', useFactory: getBaseUrl }
]
})
In home.component.html, add the following line at the end of the file:
<button mat-raised-button>This is a button</button>
From the project directory, run webpack to update the vendor files:
webpack --config webpack.config.vendor.js
Build and run the application
Observe the home page button is not styled. There are no errors in the console suggesting missing styles or invalid attributes.
I have the following configuration:
Angular 4.4.6
Material 2.0.0-beta.12
Windows 10 Professional
TypeScript 2.4.1 (NPM)
Chrome Version 61.0.3163.100 (Official Build) (64-bit)
Verified the styles do exist, as specifying class="mat-raised-button" on the button has an effect (though it does not look like the raised button) it does change the interior styling of the button.
I note that the attribute does not appear to have any effect on the styling or content of the output HTML (versus what I see when inspecting the elements on the guide website), suggesting that something has gone wrong with the setup of the module, but I can't for the life of me figure out what that might be.
EDIT: By request, here is the webpack.config.vendor.js up to the boilerplate:
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const merge = require('webpack-merge');
const treeShakableModules = [
'#angular/animations',
'#angular/common',
'#angular/compiler',
'#angular/core',
'#angular/forms',
'#angular/http',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
'#angular/router',
'zone.js',
];
const nonTreeShakableModules = [
'#angular/cdk',
'#angular/material',
'#angular/material/button',
'#angular/material/prebuilt-themes/deeppurple-amber.css',
'bootstrap',
'bootstrap/dist/css/bootstrap.css',
'es6-promise',
'es6-shim',
'event-source-polyfill',
'jquery',
];
const allModules = treeShakableModules.concat(nonTreeShakableModules);
module.exports = (env) => {
const extractCSS = new ExtractTextPlugin('vendor.css');
const isDevBuild = !(env && env.prod);
const sharedConfig = {
stats: { modules: false },
resolve: { extensions: [ '.js' ] },
module: {
rules: [
{ test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' }
]
},
output: {
publicPath: 'dist/',
filename: '[name].js',
library: '[name]_[hash]'
},
plugins: [
new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
new webpack.ContextReplacementPlugin(/\#angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580
new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)#angular/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/14898
new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
]
};
... boilerplate follows this ...
So somewhat counterintuitively, the solution requires that the module importing be done in app.module.shared.ts, not app.module.browser.ts. If you are using the default Angular template, use the same steps as above, except for step 5, do the following:
Edit app.module.shared.ts to add the MatButtonModule module as an import, as follows:
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { RouterModule } from '#angular/router';
import { MatButtonModule } from '#angular/material/button';
import { AppComponent } from './components/app/app.component';
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
#NgModule({
declarations: [
AppComponent,
NavMenuComponent,
CounterComponent,
FetchDataComponent,
HomeComponent
],
imports: [
CommonModule,
HttpModule,
FormsModule,
MatButtonModule,
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])
]
})
export class AppModuleShared {
}
Note also it is not necessary to add #angular/cdk or #angular/material/button to the modules list in webpack.config.vendor.js. It is sufficient to add the ones described in the guide.
See: https://github.com/angular/material2/issues/7997
According to official docs Here
You've to Import theming to your Global style file this is as simple as including one line in your styles.css file:
#import '~#angular/material/prebuilt-themes/deeppurple-amber.css';
or Alternatively, you can just reference the file directly.
Available pre-built themes:
deeppurple-amber.css
indigo-pink.css
pink-bluegrey.css
purple-green.css

Angular2-meteor socially demo No Directive annotation found

I am doing the Socially demo for angular2-meteor but I am not getting passed step 0: Bootstrapping.
The url to the tutorial is: https://www.angular-meteor.com/tutorials/socially/angular2/bootstrapping
My client/app.ts file contains this currently:
import 'reflect-metadata';
import { Component } from '#angular/core';
import { bootstrap } from '#angular/platform-browser-dynamic';
#Component({
selector: 'app',
templateUrl: '/client/app.html'
})
class Socially { }
bootstrap(Socially);
My app.html file:
Hello World!
and my index.html contains this:
<body>
<app></app>
</body>
Once I run the code however, I receive this exception:
EXCEPTION: No Directive annotation found on Socially
I run windows 10 pro and the project is created as a meteor --release 1.3.2.4 project.
This is what my package.json looks like:
{
"name": "djvanderburgt",
"private": true,
"scripts": {
"start": "meteor run"
},
"dependencies": {
"#angular/common": "^2.0.0-rc.1",
"#angular/compiler": "^2.0.0-rc.1",
"#angular/core": "^2.0.0-rc.1",
"#angular/platform-browser": "^2.0.0-rc.1",
"#angular/platform-browser-dynamic": "^2.0.0-rc.1",
"#angular/router": "3.0.0-alpha.3",
"angular2-meteor": "^0.5.5",
"angular2-meteor-auto-bootstrap": "0.5.5",
"angular2-meteor-polyfills": "^0.1.1",
"es6-shim": "^0.35.1",
"meteor-node-stubs": "^0.2.3",
"reflect-metadata": "^0.1.2",
"rxjs": "^5.0.0-beta.6",
"typings": "^1.3.0",
"zone.js": "^0.6.12"
}
}
According to angular2-meteor issue #301, the problem that you have mentioned should be fixed by updating angular2-meteor to 0.6.0.

Resources