Exclude Vue Component from stylesheet of existing application - javascript

We build a Vue component(using vuetify) into an existing .net MVC application.
The application loads the webpack into a div.
The problem is that the vue component inherits all the CSS of the existing application.
A simplified HTML version looks like this:
<html>
<head>
</head>
<body>
...
<div class="VUE_CLASS">
//vue component...
</div>
...
</body>
</html>
The Style is written in css.less.
I tried to exclude the VUE_CLASS from all CSS Rules of the existing application by applying a :not(.VUE_CLASS) and a div:not(.VUE_CLASS). I also tried to wrap it around all rules in the css.less:
*:not(.VUE_CLASS){
//...css rules of the existing application
}
It doesn't work
I read about some other strategies (https://kloudless.com/blog/2019/02/05/creating-a-reusable-vuetify-component-to-use-in-other-apps/). Using an iframe is not an option because we can't access our backed from an iframe. I can't use Web components as well, because we have to support ie11.
Is it possible to exclude the div and all its child elements using less?
Thank you & Best regards,
Finn

What if you just reset all CSS styles for VUE_CLASS selector?
Try adding this rule at the end of the <style> tag:
.VUE_CLASS {
all: unset !important;
/* Write the other styles for this component below */
}

Related

How to querySelector an element in an html import from a document that imports it?

Let say I have a html document with this div inside :
div.html
<div class="thediv">
<h1>test</h1>
<p class="insert"></p>
</div>
from index.html, I import it
<link rel="import" href="/path/to/div.html">
Now If I type in the console or in a script
const div = document.querySelector('.thediv');
div is null, as if the div just vanished. But I am sure there is a way to select the element because some webcomponents libraries allow programmer to define their own custom elements one by file.
I tried many things but can't find the solution.
Thanks for the help.
update :
This is true for any HTML Elements (not just div). I just pick an example for the question.
You can reference the document set at <link> element with rel attribute set to "import" using .import property of <link> element, see also Is there a way to know if a link/script is still pending or has it failed, How to append a whole html file with jquery
document.querySelector("link[rel=import][href='template1.html']")
.import.querySelector("#template1")
Solution found here: https://www.html5rocks.com/en/tutorials/webcomponents/imports/
import.html
<template>
<h1>Hello World!</h1>
<!-- Img is not requested until the <template> goes live. -->
<img src="world.png">
<script>alert("Executed when the template is activated.");</script>
</template>
index.html
<head>
<link rel="import" href="import.html">
</head>
<body>
<div id="container"></div>
<script>
var link = document.querySelector('link[rel="import"]');
// Clone the <template> in the import.
var template = link.import.querySelector('template');
var clone = document.importNode(template.content, true);
document.querySelector('#container').appendChild(clone);
</script>
</body>
I had 2 nice answers on my question and they helped me to find a more suitable solution for my specific problem I'm sharing here.
Again what I tried to achieve was to querySelector an element defined in an html import from the main document (i.e. index.html).
If we write something like
<div id="thediv">
...
</div>
at the root of an html import, the element won't be included in the body of the main document (I think each document has its own namespace or something like that).
#guest271314 recommended to use the import property of a link element to access the content of the fetched resource but see my comment on the same post. I think this is not really neat when you deal with documents that imports other documents that imports others...
#MaxBilbow recommended the same method providing a link and an example chunk using template. Again this is not really nice when you deal with relational imports. But the link gave me the solution.
In my imported document, I write an additional script :
div.html
<div id="thediv">
...
</div>
<script>
document.body.prepend(
document.currentScript.ownerDocument.querySelector('#thediv'));
</script>
Now when the html import is fetched the script will get executed and prepend the html chunk into the body of the main document.
index.html
<link rel="import" href="div.html">
<script>
// now I can querySelector('#thediv')
</script>
This is just an example and it is a dummy one, but I think I will mostly use that technique for templates, templates are hidden and it's ok to prepend them to the main document.
If you think that method could have flaws, please comment.
HTML Imports is deprecated and has now been removed as of M80. See https://www.chromestatus.com/features/5144752345317376 and https://developers.google.com/web/updates/2019/07/web-components-time-to-upgrade for more details.
You ca use fetch() to import your web component
How to separate web components to individual files and load them?

Ads with script tags in template [Vue.js]

In our project we've previously been using Thymeleaf, but now that we're moving over to Vue.js, we're experiencing some issues using the same ad scripts. The scripts look like this. I've only altered the URLs.
<script data-adfscript="sub.adcompany.net/asdf/?id=256746"></script>
<script src="//sub.adcompany.net/url/to/advertisement/script.js" async="async" defer="defer"></script>
If we put these tags in the <template>, Webpack gives the following message:
Templates should only be responsible for mapping the state to the UI.
Avoid placing tags with side-effects in your templates, such as
, as they will not be parsed.
So I've then been Googling all over to find a similar case. There are some plugins that do this for Google Ads, but they won't work for us. Escaping the script tags <\/script> works in a way, but then the script isn't added to the DOM until after loaded, and so it doesn't run.
Has anyone run into similar issues? If so, what was your solution?
Vue file looks something like this:
<template>
<aside class="sidebar-ad ui-wide">
<script data-adfscript="sub.adcompany.net/asdf/?id=256746"></script>
<script src="//sub.adcompany.net/url/to/advertisement/script.js" async="async" defer="defer"></script>
</aside>
</template>
<script>
export default {
data () {
return {}
}
}
</script>
There is a workaround. Works with style tag too.
<component is="script" src="https://www.example.com/example.js" async></component>
You should not treat Vue templates as a your final HTML, although their syntax is nearly identical and is also HTML-syntax compliant.
Templates are just a UI scaffolds for the data (that is why it is called a data-driven paradigm). They get parsed and transformed into render functions that in the end will produce the final and reactive DOM tree. During this process the <script> tags are indeed ommited cause it is not a place for any logic to happen.
However if you really need to embed any 3rd party script within your component there is a neat way to do this.
First, create container for the script:
<template>
<div id="component-root">
<!-- (...) -->
<div v-el:script-holder></div>
</div>
</template>
Then dynamicly create <script> tag and insert it directly to the DOM tree (using pure Vanilla JS):
<script>
export default {
data () {
return {};
},
ready() {
let scriptEl = document.createElement('script');
scriptEl.setAttribute('src', 'https://cdn.com/somescript.js');
scriptEl.setAttribute('data-some-param', 'paramvalue');
this.$els.scriptHolder.appendChild(scriptEl);
},
}
</script>
The this.$els.scriptHolder returns actual DOM element, calling the appendChild() forces the browser to insert DOM node and run the script just like during ordinary HTML code rendering.
Instead of $els you could also use $el which would return the components root DOM element (in this example the <div id="component-root">) or even the $root.$el which would return the Vue App root DOM element.
Note that this.$els is a Vue 1 feature, which has been replaced with $refs in Vue 2: https://v2.vuejs.org/v2/guide/migration.html#v-el-and-v-ref-replaced
You can try with this package
https://www.npmjs.com/package/vue-script2
I made a vue component to handle script loading within the template tags.
Personally, I use it to load ads in my vuejs apps.
here:
https://github.com/TheDynomike/vue-script-component
I found a way to work around this, not sure how well it works but this the way I did it.
Run the <script> in normal html then go to Dev tools and copy the iframe code made by the script and then paste it into the code as iframe instead of the <script>.
just place your in vue template scripts after your app, in exemple at the end of the body
(function($) {
$('#your-app').find('script').appendTo('body')
<script> your vue script (create app)</script>
})(jQuery);

How to remove reference of specific CSS files files from View

I have many JavaScript Files and CSS Files referenced in the Layout file. Now I have a couple of views which are referencing some custom libraries which are interfering with the existing CSS files. So my question is can I somehow remove those references from these two views only or not allow them to load at all in the said views.
What you can do is, create a custom css file which overrides the existing styles from your regular (ex : bootstrap) css file. You can include this css files conditionally after your regular css styles in the views you want.
You may also use the !important attribute to explicitly override the values as needed.
So in your layout, add a new Section called CustomStyles
<head>
<!-- Existing css include goes heree -->
#RenderSection("CustomStyles", required: false)
</head>
And in your specific view, you can simply include the custom css file
#section CustomStyles
{
<link href="~/MyCustomCss.css" rel="stylesheet">
}
EDIT : As per the comment.
If you are ok to completely ignore the specific css files in some views, you can update your Layout to conditionally include/exclude those.
So in your layout
<head>
#if (ViewBag.IncludeBootStrap == null|| (bool) ViewBag.IncludeBootStrap !=false)
{
<link href="~/Content/css/bootstrap.min.css" rel="stylesheet" />
}
</head>
And in the view's in which you do not want to load this css file, Set the ViewBag.IncludeBootStrap value to false.
#{
ViewBag.IncludeBootStrap = false;
}
<h1>This view will not use bootstrap styles</h1>
Give an id to the tag.
<link rel="stylesheet" href="style1.css" id="style1" />
<link rel="stylesheet" href="style2.css" id="style2" />
And use this code:
You can use whatever event you want or just use the code without an event it's your choice
$("#A").click(function(){
$("#style1").attr("disabled", "disabled");
});
Note: While there is no disabled attribute in the HTML standard, there is a disabled attribute on the HTMLLinkElement DOM object.
The use of disabled as an HTML attribute is non-standard and only used by some Microsoft browsers. Do not use it. To achieve a similar effect, use one of the following techniques:
If the disabled attribute has been added directly to the element on the page, do not include the <link> element instead;
Set the disabled property of the DOM object via scripting.

Angular 2.x change <title> in head (outside my app)

I think it should be easy but I cannot find how.
I have something like
<html>
<head>
<title>{{'a' + 'b'}}</title>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
It seems like I cannot access anything outside my-app.
In angular 1.x it was easy, I was able to add ng-app on any element (<html ng-app="myApp">).
Now I think I'm able only bootstrap in body.
I know I can manually bootstrap somehow (didn't try yet), but dynamically change title in single page applications should be super-easy, shouldn't it?
Angular2 can't be bootstrapped to entire html. But you can use Title Service.
A service that can be used to get and set the title of a current HTML document.
It has 2 methods:
getTitle()
setTitle()
Don't forget to check the dependency injection section out to see how you can use the services.
EDIT:
As of the release (2.0.0), this is how you can do it:
import { Title } from '#angular/platform-browser';
export class SomeComponent {
constructor(title: Title) {
// title.getTitle();
// title.setTitle('new title');
}
}
And the docs for the Title service are now here: https://angular.io/docs/ts/latest/api/platform-browser/index/Title-class.html

Javascript single page architecture with module autonomy

I just started this HTML5 project where we decided to make it a single page architecture by leveraging jQuery $.load() method. Unfortunately, as soon as the JS started to grow, we quickly started running into issues where the modules loaded into the master dashboard have no knowledge of their parent.
The architecture looks like this:
dashboard.html (master file)
moduleA.html
moduleA.js
moduleB.html
moduleB.js
moduleC.html
moduleC.js
Since we decided to also keep the JS as separate files, we are having to load all JS files through dashboard.html in order to invoke them individually when modulex is loaded.
So when loading moduleA.html into the dashboard we have to call its corresponding JS. To do this we simply wrote the JS using a Module Pattern so we can easily invoke it by doing a function call, like:
<script>
moduleA
</script>
or this if we want to access a specific property of this member.
<script>
moduleA.someMethod();
</script>
Now, I know there are is gotta be a nicer way of doing this, right? I hate having to have script tags in the HTML modules in order to load its corresponding JS file.
Another limitation of this is the fact that we no longer can work on modules individually, since the scripts and CSS invocation happens on the parent (dashboard.html) so certainly when moduleA.html is loaded directly, it is pure HTML with no script or CSS.
I looked through the other questions but I didn't see anyone with the same problem.
I looked at AngularJS, EmberJS, KO.JS and BoilerPlateJS but none of them addresses what we are trying to accomplish. The only one that has a similar single page concept is jQuery Mobile but I don't know if you can switch from jQuery to jQuery Mobile and everything remains working.
Has anyone face this issue yet? Is there a solution or would I have to go with a custom solution.
Thanks!
I could argue about AngularJS with you. It is exactly what you need
dashboard.html is layout with some directives attached, but power lies in AngularJs if you use ng-view directive
here is example:
dashboard.js
var app = angular.module("modularApp",[]);
app.config(['$routeProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
controller:'HomeCtrl',
templateUrl:'templates/home.html'
});
$routeProvider.when('/moduleA', {
controller:'ModuleACtrl',
templateUrl:'templates/moduleA.html'
});
$routeProvider.when('/moduleB', {
controller:'ModuleBCtrl',
templateUrl:'templates/moduleB.html'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
templates/dashboard.html
<html ng-app="modularApp">
<head>
<!--.... include angular minified js file and what else you need...-->
<script type="text/javascript" src="dashboard.js"></script>
<script type="text/javascript" src="moduleACtrl.js"></script>
<script type="text/javascript" src="moduleBCtrl.js"></script>
</head>
<body>
<a ng-href="#/moduleA">Open Module A View</a>
<a ng-href="#/moduleB">Open Module B View</a>
<!-- Add widgets header menus .... -->
<ng-view></ng-view>
</body>
</html>
moduleACtrl.js
var app=angular.module("modularApp");
app.controller("ModuleACtrl",function($scope){
$scope.scopeValue="Hellow from view";
});
moduleBCtrl.js
var app=angular.module("modularApp");
app.controller("ModuleBCtrl",function($scope){
$scope.scopeValue="Hellow from another view";
});
templates/moduleA.html
<div>{{scopeValue}} in module A</div>
templates/moduleB.html
<div>{{scopeValue}} in module B</div>
You can do more complex things with angular then just this. All depends on your needs. Do you have any special requirements :)
Also, you could create your own directive, like ng-view and use your own $route service and $routeProvider so you can add css and javascript you want to dynamically load when some rute match url.
so instead of above routing table, you could have
app.config(['$myRouteProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
javascript:'javascript/dashboard.js',
templateUrl:'templates/dashboard.html',
cssfile: 'css/dashboard.css'
});
$routeProvider.when('/moduleA', {
javascript:'javascript/moduleA.js',
templateUrl:'templates/moduleA.html',
cssfile: 'css/moduleA.css'
});
$routeProvider.when('/moduleB', {
javascript:'javascript/moduleB.js',
templateUrl:'templates/moduleB.html',
cssfile: 'css/moduleB.css'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
But that is, pardon on my French, stup. There are couple libs I tried in ruby on rails to acheive similar, but backend is rendering content, or just part of content. But I'm not sure which backend you are using and are you interested to switch to rails anyway.
DomController in BoilerplateJS does what you need, without using any custom HTML attributes. Your dashboard.html can just have place holders where you want to inject your components. I'm just pulling out some html below from BoilerplateJS index.html to show how it works:
<body>
<section id="page-content">
<header>
<section class="theme"></section>
<section class="language"></section>
</header>
<aside>
<section class="main-menu"></section>
</aside>
</section>
</body>
theme, language and main-menu sections above are just place holders in to which relavant components would be injected by the DomController. The DomController can be now used to register the components with appropriate selectors as below:
//scoped DomController that will be effective only on $('#page-content')
var controller = new Boiler.DomController($('#page-content'));
//add routes with DOM node selector queries and relavant components
controller.addRoutes({
".main-menu" : new MainMenuRouteHandler(context),
".language" : new LanguageRouteHandler(context),
".theme" : new ThemeRouteHandler(context)
});
controller.start();
Above code is extracted from "/boilerplatejs/src/modules/baseModule/module.js"

Categories