Multi modules in one page with angularjs - module

How to use multi modules in one single page in angular?
I read some docs and they say I can't use ngApp directive and should use angular.bootstrap.
My HTML code:
<div id="M1">
<div ng-controller="Ctrl">
<h1>Hello {{name}}</h1>
</div>
</div>
<div id="M2">
<div ng-controller="Ctrl">
[{{name}}]
</div>
</div>
My angular code:
var m1 = angular.module('M1', []);
m1.controller('Ctrl', function($scope) {
$scope.name = 'M1';
});
var module2 = angular.module('M2', []);
module2.controller('Ctrl', function($scope){
$scope.name = 'M2';
})
angular.bootstrap(document.getElementById('M1'), ['M1']);
angular.bootstrap(document.getElementById('M2'), ['M2']);
But it doesn't work.
This is a live demo: http://plnkr.co/edit/dkyL8eacoC5ZohfwSWz1?p=preview

Seems like the bootstrapping is occurring too soon. Modify the two boostrap lines to happen when the document is ready:
angular.element(document).ready(function() {
angular.bootstrap(document.getElementById('M1'), ['M1']);
angular.bootstrap(document.getElementById('M2'), ['M2']);
});
When modifying your plnkr in this way, both apps started working properly.
Live demo: http://plnkr.co/edit/Of0PYWR5ZEGT5BK4sfhI?p=preview

Related

How to render html partial in vue 3 router

In my apps I have a couple of pages that show some embedded forms...
The forms come from jotform, and are embedded with a js script like this
<section>
<script type="text/javascript" src="https://form.jotform.com/jsform/123124124"</script>
</section>
I cannot find a way to load this inside a component, so I'm trying to use a simple HTML partial. Is there a way to do this?
I try also with a component but it doesn't work
<script>
export default {
data: () => ({
}),
mounted() {
const scripts = [
"https://form.jotform.com/jsform/123124124124"
];
scripts.forEach(script => {
let tag = document.head.querySelector(`[src="${ script }"`);
if (!tag) {
tag = document.createElement("script");
tag.setAttribute("src", script);
tag.setAttribute("type", 'text/javascript');
console.log(document.body);
setTimeout(function() {
document.body.appendChild(tag);
}, 500)
}
});
}
}
</script>
<template>
<main class="main">
<h1 class="visuallyhidden">Funding Request</h1>
<section class="funding">
</section>
</main>
</template>
As suggested here you can try to use the iFrame version: https://www.jotform.com/help/148-getting-the-form-iframe-code/
FYI: including an embed script within a dynamic vue component is not an easy thing (as the JotForm support explaned here), so you might try to go for the iFrame version if it works for you :)

HowTo: Toggle dark mode with TailwindCSS + Vue3 + Vite

I'm a beginner regarding Vite/Vue3 and currently I am facing an issue where I need the combined knowledge of the community.
I've created a Vite/Vue3 app and installed TailwindCSS to it:
npm create vite#latest my-vite-vue-app -- --template vue
cd my-vite-vue-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Then I followed the instructions on Tailwind's homepage:
Add the paths to all of your template files in your tailwind.config.js file.
Import the newly-created ./src/index.css file in your ./src/main.js file. Create a ./src/index.css file and add the #tailwind directives for each of Tailwind’s layers.
Now I have a working Vite/Vue3/TailwindCSS app and want to add the feature to toggle dark mode to it.
The Tailwind documentation says this can be archived by adding darkMode: 'class' to tailwind.config.js and then toggle the class dark for the <html> tag.
I made this work by using this code:
Inside index.html
<html lang="en" id="html-root">
(...)
<body class="antialiased text-slate-500 dark:text-slate-400 bg-white dark:bg-slate-900">
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
Inside About.vue
<template>
<div>
<h1>This is an about page</h1>
<button #click="toggleDarkMode">Toggle</botton>
</div>
</template>
<script>
export default {
methods: {
toggleDarkMode() {
const element = document.getElementById('html-root')
if (element.classList.contains('dark')) {
element.classList.remove('dark')
} else {
element.classList.add('dark')
}
},
},
};
</script>
Yes, I know that this isn't Vue3-style code. And, yes, I know that one could do element.classList.toggle() instead of .remove() and .add(). But maybe some other beginners like me will look at this in the future and will be grateful for some low-sophisticated code to start with. So please have mercy...
Now I'll finally come to the question I want to ask the community:
I know that manipulating the DOM like this is not the Vue-way of doing things. And, of course, I want to archive my goal the correct way. But how do I do this?
Believe me I googled quite a few hours but I didn't find a solution that's working without installing this and this and this additional npm module.
But I want to have a minimalist approach. As few dependancies as possbile in order not to overwhelm me and others that want to start learning.
Having that as a background - do you guys and gals have a solution for me and other newbies? :-)
The target element of your event is outside of your application. This means there is no other way to interact with it other than by querying it via the DOM available methods.
In other words, you're doing it right.
If the element was within the application, than you'd simply link class to your property and let Vue handle the specifics of DOM manipulation:
:class="{ dark: darkMode }"
But it's not.
As a side note, it is really important your toggle method doesn't rely on whether the <body> element has the class or not, in order to decide if it should be applied/removed. You should keep the value saved in your app's state and that should be your only source of truth.
That's the Vue principle you don't want break: let data drive the DOM state, not the other way around.
It's ok to get the value (on mount) from current state of <body>, but from that point on, changes to your app's state will determine whether or not the class is present on the element.
vue2 example:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
darkMode: document.body.classList.contains('dark')
}),
methods: {
applyDarkMode() {
document.body.classList[
this.darkMode ? 'add' : 'remove'
]('dark')
}
},
watch: {
darkMode: 'applyDarkMode'
}
})
body.dark {
background-color: #191919;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id="app">
<label>
<input type="checkbox" v-model="darkMode">
dark mode
</label>
</div>
vue3 example:
const {
createApp,
ref,
watchEffect
} = Vue;
createApp({
setup() {
const darkMode = ref(document.body.classList.contains('dark'));
const applyDarkMode = () => document.body.classList[
darkMode.value ? 'add' : 'remove'
]('dark');
watchEffect(applyDarkMode);
return { darkMode };
}
}).mount('#app')
body.dark {
background-color: #191919;
color: white;
}
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
<div id="app">
<label>
<input type="checkbox" v-model="darkMode">
dark mode
</label>
</div>
Obviously, you might want to keep the state of darkMode in some external store, not locally, in data (and provide it in your component via computed), if you use it in more than one component.
What you're looking for is Binding Classes, but where you're getting stuck is trying to manipulate the <body> which is outside of the <div> your main Vue instance is mounted in.
Now your problem is your button is probably in a different file to your root <div id="app"> which starts in your App.vue from boilerplate code. Your two solutions are looking into state management (better for scalability), or doing some simple variable passing between parents and children. I'll show the latter:
Start with your switch component:
// DarkButton.vue
<template>
<div>
<h1>This is an about page</h1>
<button #click="toggleDarkMode">Toggle</button>
</div>
</template>
<script>
export default {
methods: {
toggleDarkMode() {
this.$emit('dark-switch');
},
},
};
</script>
This uses component events ($emit)
Then your parent/root App.vue will listen to that toggle event and update its class in a Vue way:
<template>
<div id="app" :class="{ dark: darkmode }">
<p>Darkmode: {{ darkmode }}</p>
<DarkButton #dark-switch="onDarkSwitch" />
</div>
</template>
<script>
import DarkButton from './components/DarkButton.vue';
export default {
name: 'App',
components: {
DarkButton,
},
data: () => ({
darkmode: false,
}),
methods: {
onDarkSwitch() {
this.darkmode = !this.darkmode;
},
},
};
</script>
While tailwind say for Vanilla JS to add it into your <body>, you generally shouldn't manipulate that from that point on. Instead, don't manipulate your <body>, only go as high as your <div id="app"> with things you want to be within reach of Vue.

(Vue.js) Should I erase semicolon at an API? or ignore eslint?

After I use API at my program, lots of errors/warnings appear because of eslint. Eslint told me to erase semicolon at API. But I'm not sure it's okay to erase semicolon at the API. So I just want to ignore eslint. I write this code between template and script.
/* eslint-disable */
But It didn't work, I don't know why.
So this is my question. Can you kindly answer to these questions? Thank you.
Is it okay to erase semicolon at the API?
Why /* eslint-disable */ didn't work?
A Whole code is at the bottom.
<template>
<div>
<div class="searchAroundMe">
<form action="search.php" method="post">
<input class="btn_text" type="text">
<input class="btn_submit" type="submit" value="SEARCH">
</form>
</div>
<h1 class="header2"></h1>
<div id="map" class="map"></div>
</div>
</template>
/* eslint-disable */
<script>
export default {
mounted() {
window.kakao && window.kakao.maps
? this.initMap()
: this.addKakaoMapScript();
},
methods: {
addKakaoMapScript() {
const script = document.createElement("script");
/* global kakao */
script.onload = () => kakao.maps.load(this.initMap);
script.src =
"http://dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=*APPKEY*";
document.head.appendChild(script);
},
initMap() {
var container = document.getElementById("map");
var options = {
center: new kakao.maps.LatLng(33.450701, 126.570667),
level: 3
};
var map = new kakao.maps.Map(container, options);
}
}
};
</script>
Your eslint-disable command is a JavaScript comment, but you have written it in HTML code (after a template element and before a script element, so it is treated as plaintext). It would need to be within the <script>...</script> tags for it to get recognized.
Semi-colons are not required at the end of statements in JavaScript, so your eslint configuration may expect them to be omitted. You can change the configuration settings to always require them if that is what your project prefers.

Showing a spinner in vue js and bootsrap using v-show

I am using vue js 2.5.17 and vue router as front end and laravel as back end. I have a table with over 1000 record so before it shows I want to use a spinner or loader to show the progress. I have managed to use spinner-grow from bootstrap but it keeps showing even when the data is displayed. What I am doing wrong.
In the template I have this:
<div v-show="isloading=true" >
<div class="spinner-grow" role="status">
<span class="sr-only">Loading...</span>
</div>
In data if I have
isloading:true,
In my method i have
loadUser(){
axios.get("api/customer").then((
{data})=>(
this.users=data));
this.isloading=false;
console.log(this.isloading);
},
I think you could try something like this
data() {
return {
isloading: false, // default value
};
},
<div v-show="isloading" >
<div class="spinner-grow" role="status">
<span class="sr-only">Loading...</span>
</div>
loadUser(){
this.isloading=true; // make it true to show the loading
axios.get("api/customer").then((
{data})=>(this.users=data));
this.isloading=false; // then hide it here
console.log(this.isloading);
},
try change this <div v-show="isloading=true" > to this <div v-if="isloading" >

How to make Vue js directive working in an appended html element

I have a Vue directive added in an appended html element like v-on directive but it's not working on my end. Basically I want to know the equivalent of .on() in jquery.
"Vue.js compilation happens when you instantiate/mount the root instance. It doesn't detect new DOM being injected unless it is a result of a directive (e.g. v-repeat, v-partial will dynamically create new DOM fragments)."
https://github.com/vuejs/Discussion/issues/77
You have to compile your newly added element like this:
html:
<div id="app">
<button v-on="click: addNewElement()">Add Element</button>
<br />
</div>
JavaScript
new Vue({
el: '#app',
data: {
sampleElement: '<button v-on="click: test()">Test</button>'
},
methods:{
addNewElement: function(){
var element = $('#app').append(this.sampleElement);
/* compile the new content so that vue can read it */
this.$compile(element.get(0));
},
test: function(){
alert('Test');
}
}
});
See this working Fiddle on Firefox: http://jsfiddle.net/chrislandeza/syewmx4e/
Update
$compile has been removed on Vue 2.x
I've seen people suggesting Vue.compile or
var tmp = Vue.extend({
template: 'Content'
})
new tmp().$mount(' id or refs ')
But those 2 does not behave like the old $compile.
For Vue 2.x, the new solution is referenced here in the doc : https://v2.vuejs.org/v2/api/#vm-mount (see 'Example').
Custom example :
var MyComponent = Vue.extend({
template: '<div v-on:click="world">Hello!</div>',
methods : {
world : function() {
console.log('world')
}
}
})
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app"></div>
Works like a charm!
You need to register the html as a template of the component.