SlideShare uma empresa Scribd logo
1 de 92
Baixar para ler offline
Dept. of Smart Finance, Korea Polytechnics Seoul Kangseo Campus
ApplicationDevelopmentw/
QuasarFramework
Basic Course
QuasarCLIInstallation
• Prerequisite


Node >=12.22.1 and NPM >=6.14.12


• recommendation node version : v14.17.5


check your node and npm : node -v


npm -v


• npm install -g @quasar/cli
Do not use uneven versions of Node i.e. 13, 15, etc. These versions are not tested with Quasar and often cause issues due to their experimental nature. We highly recommend always using the LTS version of Node.
Ref : https://quasar.dev/quasar-cli/installation
HelloworldⅠ(1/2)
•  we create a project folder with Quasar CLI


• quasar create <folder_name>
Ref : https://quasar.dev/quasar-cli/installation
(base) waynehwang@wayneui-MacBookPro temp % quasar create hello


___


/ _  _ _ __ _ ___ __ _ _ __


| | | | | | |/ _` / __|/ _` | '__|


| |_| | |_| | (_| __  (_| | |


_____,_|__,_|___/__,_|_|




? Project name (internal usage for dev) hello


? Project product name (must start with letter if building mobile apps) hello


? Project description hello world


? Author wayne hwang <wonyong@wonyong.net>


? Pick your CSS preprocessor: SCSS


? Check the features needed for your project: ESLint (recommended), Vuex, Axios


? Pick an ESLint preset: Prettier


? Continue to install project dependencies after the project has been created? (


recommended) NPM


Quasar CLI · Generated "hello".


[*] Installing project dependencies …


[*] Quasar Project initialization finished!


To get started:


cd hello


quasar dev
Helloworld Ⅰ(2/2)
• Look into package.json, App.vue, routes.js, Index.vue, EssentialLink.vue, …


• Run your app with below command:


quasar dev
HelloWorldⅡ
• Repository address :


https://github.com/wonyongHwang/kopoPro
fi
le


• git clone https://github.com/wonyongHwang/kopoPro
fi
le


• npm install


• quasar dev
kopoProfile
HelloWorldⅡ
• In this practice, we don’t have a


drawer and a footer either. So, only


<q-header> is being a
ff
ected by


<q-layout>.


•
layout
<q-layout view="lHh lpr lFf">
reference : https://quasar.dev/layout/layout#understanding-the-view-prop
HelloWorldⅡ
• quasar can detect platform : $q.platfrom.is.~~




eg> if you will deploy app to mobile device(android,


ios) app can detect platform using $q.platform.is.


cordova
$q.platform
<div v-bind:class="{'main': $q.platform.is.desktop, 'mobile_main': $q.platform.is.ipad || $q.platform.is.mobile}"


class="full-height" style="background-color:rgba(0, 0, 0, 0.7);">
reference : https://quasar.dev/options/platform-detection#introduction
HelloWorldⅡ
style
<q-header class="q-py-sm" style="background-color:rgba(0, 0, 0, 0.7);">
reference : https://quasar.dev/style/spacing#table-of-permutations
HelloWorldⅡ
q-tab
reference : https://quasar.dev/vue-components/tabs
<q-tabs v-model="selected_tab" shrink>


<q-tab name="about_me" :style="[selected_tab == 'about_me' ? {backgroundColor: 'green'} : {}]" class="q-mr-sm q-py-xs custom_tab"


@click="scrollToElement('id_about_me')" style="width:120px;min-height:auto !important;color: white"


label="About Me"/>
HelloWorldⅡ
q-card
<q-card class="my-card" flat bordered>


<q-card-section>
reference : https://quasar.dev/vue-components/card#introduction
HelloWorldⅡ
• Get/set scroll position (Quasar Scrolling Utils)


https://quasar.dev/quasar-utils/scrolling-utils#get-set-scroll-position
<script></script>
import {scroll} from 'quasar'


const {getScrollTarget, setScrollPosition} = scroll


export default {


name: 'PageIndex',


data() {


return {


name: "",


email: "",


message: "",


selected_tab: 'about_me',


}


},


methods: {


scrollToElement(id) {


let el = document.getElementById(id)


const target = getScrollTarget(el)


const o
ff
set = el.o
ff
setTop - 65


const duration = 800


setScrollPosition(target, o
ff
set, duration)


}


},


mounted () {


}


}
homework
• customizing web page


• upload source to your github repo.


- README.md should be edited
ex>
homework
• deploy your web pages via netlify


https://app.netlify.com/
homework
• check url


• if you have a own domain, netlify allow you to access your web site


• ‘https(let’s encrypt)’ supports both custom domain and netlify domain
Make Your First Page
Basic Practice
Layout
• quasar create <folder name>


• quasar dev


•
fi
rst page is served by Index.vue


Layout
• Quasar provides basic layouts
reference : https://quasar.dev/layout/gallery
SignIn
• what you need to implement a sign-in page:


2 Input Text
fi
eld


1 Check Box


1 Button


and more …
SignIn
• copy relevant codes and paste to your “Index.vue”


https://quasar.dev/vue-components/input
SignUp
• add Signup.Vue to your project


• add page mapping on routes.js
pagelink
• vue syntax for page link :


<router-link to=“/address”> ~~ </router-link>
<router-link to="/signup" >


<center>New User? Click Here to Register.</center>


</router-link>
Homework
• Place and Organize Input Text
fi
elds (E-mail, Password, etc …)


: center alignment


• Password should be masked while typing


• Place Sign-In / Sign-Up buttons
complete your sign-in / sign-up pages!
f
irebase
• https://
fi
rebase.google.com/?hl=ko
create a project
// For Firebase JS SDK v7.20.0 and later, measurementId is optional


const
fi
rebaseCon
fi
g = {


apiKey: "AIzaSyA~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",


authDomain: "kopologinchecker.
fi
rebaseapp.com",


projectId: "kopologinchecker",


storageBucket: "kopologinchecker.appspot.com",


messagingSenderId: "17~~~~~~~~~~~~",


appId: "1:17~~~~~~~~:web:d4~~~~~~~~",


measurementId: “G-H0~~~~~~~~~~~~"


};
• npm install --save
fi
rebase


• quasar new boot
fi
rebase


->
fi
rebase.js will be generated in src/boot


-> paste
fi
rebase con
fi
guration to
fi
rebase.js
f
irebase
setting config.
ref : https://quasar.dev/quasar-cli/boot-
fi
les
import firebase from 'firebase/app';


import 'firebase/auth';


import 'firebase/analytics'


const firebaseConfig = {


apiKey: "~",


authDomain: "kopologinchecker.firebaseapp.com",


projectId: "kopologinchecker",


storageBucket: "kopologinchecker.appspot.com",


messagingSenderId: “~",


appId: "~",


measurementId: "~"


};


if (!firebase.apps.length) {


firebase.initializeApp(firebaseConfig);


}


firebase.analytics();


export const auth = firebase.auth();
f
irebase
• enable email/password


• create a test user if needed
sign-in method
f
irebase
• https://
fi
rebase.google.com/docs/auth/web/start?hl=ko
sign-in method
fi
rebase.auth().signInWithEmailAndPassword(email, password
)

  .then((userCredential) =>
{

    // Signed i
n

    var user = userCredential.user
;

    // ..
.

  }
)

  .catch((error) =>
{

    var errorCode = error.code
;

    var errorMessage = error.message
;

  });
• let’s use quasar notify to show success/fail message


https://quasar.dev/quasar-plugins/notify#introduction
f
irebase
sign-in method
auth.signInWithEmailAndPassword(this.text, this.password).then(


(userCredential) => {


console.log(userCredential.user);


this.$router.push({ path: 'home' })


}


).catch(


(err) => {


console.log(err.code);


console.log(err.message);


this.$q.notify({


message: err.message,


color: 'purple'


})


}


)


message, color, postion, textColor, icon, …
import { useQuasar } from 'quasar'


const $q = useQuasar()


$q.notify({


message : "login success",


color : "blue"


})
vue 3
homework
• implement a sign-up function using
fi
rebase api


• validation logic should be implemented


ex> email/password should not be empty


password should be equal to ‘repeat password’


password must be at least 6 characters long


hint> https://levelup.gitconnected.com/
fi
rebase-auth-management-in-vue-js-with-vuex-9c4a5d9cedc


• move page after sign-up


hint> this.$router.push({ path: 'home' })
import { useRouter, useRoute } from 'vue-router'
export default
{

setup()
{

const router = useRouter()
const route = useRoute()
// Now you can access params like
:

console.log(route.params.id)
;

route.push({path: ‘home’}
)

}

};
vue v3
https://next.router.vuejs.org/guide/advanced/composition-api.html#accessing-the-router-and-current-route-inside-setup
• Parent -> child : props


• Child -> Parent : emit
management
f
irebaseauthw/vuex
• state management pattern and its library


• state : data


• mutations : change state (sync)


• actions : commit mutation (async)
what vuex is
Actions
State
View
Mutations
this.$store.commit


emit, props
Unidirectional
fl
ow
management
f
irebaseauthw/vuex
• state =
fi
re auth info


• ‘
fi
re auth info’ can acquire when we call


signInWithEmailAndPassword()


createUserWithEmailAndPassword()


and so on.
state
Actions
State
View
Mutations
this.$store.commit


import {auth} from "src/boot/firebase"


export default store(function (/* { ssrContext } */) {


const Store = createStore({


modules: {


// example


},


state: {


fireUser:null


},


…
State
management
f
irebaseauthw/vuex
• get the current sign-in user is by setting an observer on the Auth object


https://
fi
rebase.google.com/docs/auth/web/manage-users


• state should be null when user signed out


https://
fi
rebase.google.com/docs/auth/web/password-auth?hl=ko
action
actions: {


signOutAction({commit}) {


auth.signOut()


.then(() => {


commit("setFireUser", null);


})


},


authAction({ commit }) {


auth.onAuthStateChanged(user => {


if (user) {


commit("setFireUser", user);


}


});


},


},


mutations: {


setFireUser(state, firebaseUser){


state.fireUser = firebaseUser


}
Actions
Mutations
management
f
irebaseauthw/vuex
• prepare helper functions


https://joshua1988.github.io/web-development/vuejs/vuex-getters-mutations/
getters
getters: {


getFireUser(state) {


return state.fireUser;


},


isUserAuth(state) {


return !!state.fireUser;


}


},
management
f
irebaseauthw/vuex
source code
import { store } from 'quasar/wrappers'


import { createStore } from 'vuex'


import {auth} from "src/boot/firebase"


export default store(function (/* { ssrContext } */) {


const Store = createStore({


state: {


fireUser:null


},


actions: {


signOutAction({commit}) {


auth.signOut()


.then(() => {


commit("setFireUser", null);


})


.catch(error => {


commit("setFireError", error.message);


});


},


authAction({ commit }) {


auth.onAuthStateChanged(user => {


if (user) {


commit("setFireUser", user);


} else {


commit("setFireUser", null);


}


});


},


},


getters: {


getFireUser(state) {


return state.fireUser;


},


isUserAuth(state) {


return !!state.fireUser;


}


},


mutations: {


setFireUser(state, firebaseUser){


state.fireUser = firebaseUser


}


},


})


return Store


})
management
f
irebaseauthw/vuex
• commit state when my application obtain a
fi
rebase auth


• get a
fi
rebase auth to display user info


• check a
fi
rebase auth
usage example
auth.signInWithEmailAndPassword(this.text, this.password).then(


(userCredential) => {


this.$store.commit("setFireUser", userCredential.user);


}


)
<div class="text-h6" align="center">


Hi, {{ this.name }}({{ getFireUser.email }})


<br>


…


computed: {


...mapGetters(["getFireUser", "isUserAuth"])


},


mounted() {


this.authAction();


…


methods: {


...mapActions(["signOutAction","authAction"]),
import { useStore } from "vuex"
;

export default
{

setup()
{

const store = useStore()
;

 

store.commit("setFireUser", userCredential.user)
;

}

};
vue v3
https://kyounghwan01.github.io/blog/Vue/vue3/composition-api-vuex/#vuex-%E1%84%89%E1%85%A6%E1%84%90%E1%85%B5%E1%86%BC-%E1%84%86%E1%85%B5%E1%86%BE-store-module-1%E1%84%80%E1%85%A2%E1%84%85%E1%85%A9-%E1%84%89%E1%85%B5%E1%86%AF%E1%84%92%E1%85%A2%E1%86%BC
homework
• When a user logged in, move Vue Page and display user’s email using vuex


• tip>


<div v-if=“getFireUser">~~</div>


computed: {


...mapGetters(["getFireUser", "isUserAuth"])


},


<div class="text-h6" align="center">


Hi,({{ getFireUser.email }})


</div>


import { mapGetters } from "vuex";
vuelifecycle
• https://cornswrold.tistory.com/342


• beforeCreate, created, beforeMount, mounted,


beforeUpdate, updated, beforeDestroy, destroyed
Firestore
Firestore
Firestore
• Firestore data add: set() or add()


https://
fi
rebase.google.com/docs/
fi
restore/manage-data/add-data?hl=ko
API
db.collection("users").add({


id: this.text,


name: this.name


})


.then((docRef) => {


console.log("Document written with ID: ", docRef.id);


this.$q.notify({


message: "Register Success",


color: 'blue'


})


})


.catch((error) => {


console.error("Error adding document: ", error);


this.$q.notify({


message: error,


color: 'red'


})


});
Firestore
•
fi
rebase.js


import '
fi
rebase/
fi
restore'


export const db =
fi
rebase.
fi
restore();


• import


import { db } from "src/boot/
fi
rebase"
Preparations
Firestore
Data write
db.collection("users").add({


id: this.text,


name: this.name


})


.then((docRef) => {


console.log("Document written with ID: ", docRef.id);


})


.catch((error) => {


console.error("Error adding document: ", error);


});
Firestore
Data read
db.collection("users").where("id", "==", this.getFireUser.email )


.get()


.then((querySnapshot) => {


querySnapshot.forEach((doc) => {


// doc.data() is never undefined for query doc snapshots


console.log(doc.id, " => ", doc.data());


this.name = doc.data().name


});


})


.catch((error) => {


console.log("Error getting documents: ", error);


});
Homework
• When a user logged in, move page(/home) and display user’s name using vuex


- user name was saved at Firestore, so we have to read the data
fi
rst to show it.


• user name should be displayed on page(/home) even after refreshing page.
Homework
Hint
<template>


<div>


HOME


<div class="text-h6" align="center">


<div v-if="getFireUser">


Hi,({{ getFireUser.email }})


:) {{ name }}


</div>


</div>


</div>


</template>
<script>


import { defineComponent } from 'vue';


import { auth, db } from "src/boot/firebase"


import { useQuasar } from 'quasar'


import { mapGetters, mapActions } from "vuex"; //


export default defineComponent({


name: 'PageIndex',


computed: {


...mapGetters(["getFireUser", "isUserAuth"])


},


updated() {




if(this.getFireUser != null && this.name == ''){ // for refresh page //


db.collection("users").where("id", "==", this.getFireUser.email )


.get()


.then((querySnapshot) => {


querySnapshot.forEach((doc) => {


// doc.data() is never undefined for query doc snapshots


console.log(doc.id, " => ", doc.data());


this.name = doc.data().name


});


})


.catch((error) => {


console.log("Error getting documents: ", error);


});


}


},


mounted(){ //


this.authAction() // for refresh page //


if(this.getFireUser != null){


db.collection("users").where("id", "==", this.getFireUser.email )


.get()


.then((querySnapshot) => {


querySnapshot.forEach((doc) => {


// doc.data() is never undefined for query doc snapshots


console.log(doc.id, " => ", doc.data());


this.name = doc.data().name


});


})


.catch((error) => {


console.log("Error getting documents: ", error);


});


}


},


data(){


return{


name : ''


}


},




methods: {


...mapActions(["signOutAction","authAction"])






}


})


</script>
f
irebaseusermgmt.
• https://
fi
rebase.google.com/docs/auth/web/manage-users?hl=ko


• User Info management


- user info update


- user account delete
Some security-sensitive actions—such as deleting an account, setting a primary email address, and changing a password—require that the user has recently signed in. 
Implementationof“rememberme”usinglocalStorage
• UI


<q-checkbox v-model="remember" label="Remember Me" color="teal"/>


• Declaration


let remember = ref(‘false')


• mounted()


• When a user tries to log in: if (remember.value == true) {


localStorage.username = email.value;


localStorage.checkbox = remember.value;


} else {


localStorage.username = "";


localStorage.checkbox = "";


}


mounted(){


if(localStorage.checkbox && localStorage.checkbox !==""){


this.remember = true


this.email = localStorage.username


} else {


this.remember = false


}


}
Implementationof“rememberme”usinglocalStorage
• Test
Homework
• Elaborate your work(home/sign-in/sign-up)


• Conceptualizing your idea
OAuth
• https://
fi
rebase.google.com/docs/auth/web/google-signin?hl=ko


•
google
OAuth
• https://
fi
rebase.google.com/docs/auth/web/facebook-login?hl=ko


• https://developers.facebook.com/apps


• Only https allowed
Facebook
OAuth
GitHub
https://github.com/settings/apps
Local
Firebase
github
f
irebasehosting
f
irebasehosting
• Hosting settings
f
irebasehosting
• Login results
f
irebasehosting
• Select hosting …
f
irebasehosting
• Public directory : dist/spa
f
irebasehosting
•
fi
rebase serve


• quasar build


•
fi
rebase deploy
f
irebasehosting
• Deploy results…
Favicon
• https://favicon.io/favicon-generator/
Menu
• components hierarchy


⎯ MainLayout.vue


EssentialLink.vue
props and emit
icon title


label
link
+
<template>


<q-item clickable tag="a" target="_blank" :href="link">


<q-item-section v-if="icon" avatar>


<q-icon :name="icon" />


</q-item-section>


<q-item-section>


<q-item-label>{{ title }}</q-item-label>


<q-item-label caption> {{ caption }} </q-item-label>


</q-item-section>


</q-item>


</template>


<script>


import { defineComponent } from 'vue'


export default defineComponent({


name: 'EssentialLink',


props: {


title: {


type: String,


required: true


},


caption: {


type: String,


default: ''


},


link: {


type: String,


default: '#'


},


icon: {


type: String,


default: ''


}


}


})


</script>
props : hey, parent component! control me
Menu
• components hierarchy


⎯ MainLayout.vue


EssentialLink.vue
props and emit
<EssentialLink v-for="link in essentialLinks" :key="link.title" v-bind="link" />


…


import EssentialLink from 'components/EssentialLink.vue'


const linksList = [


{


title: 'Docs',


caption: 'quasar.dev',


icon: 'school',


link: 'https://quasar.dev'


},


…


export default defineComponent({


name: 'MainLayout',


components: {


EssentialLink


},




setup () {


return {


essentialLinks: linksList,


}


}


…
parent component(MainLayout) now can set child props
Menu
• <Practice>


- open internal links in a current window


- open external links in a new tab (or window)
props and emit
Menu
• <Practice> child component(EssentialLink) modi
fi
cation


props





template
props and emit
external: {


type: Boolean,


default: false


}
<template>


<!-- <router-link :to="link" style="text-decoration: none"> -->


<section v-if="external">


<q-item clickable tag="a" target="_target" :href="link">


…


</q-item>


</section>


<section v-else>


<router-link :to="link" style="text-decoration: none; color: inherit;”>


…


</router-link>


</section>


</template>
Menu
• <Practice> parent component(MainLayout) modi
fi
cation


props and emit
const linksList = [


{


title: 'Home',


caption: 'Home',


icon: 'home',


link: '/home'


},


{


title: 'QR Scanner',


caption: 'QR Check for Admin',


icon: 'qr_code_scanner',


link: 'https://kopologinchecker.web.app/#/qrscanner',


external: true


},


…
Menu
• <Practice>


- close menu when a external link is clicked
props and emit
emit : hey, parent component! handle this
Menu
• <Practice> child component(EssentialLink)
modi
fi
cation


template





methods
props and emit
<q-item clickable tag="a" target="_target" :href="link" @click="sendEvent()">
methods: {


sendEvent() {


this.$emit('closeDrawer');


console.log("emit emit")


}


},
• <Practice> parent component(MainLayout)
modi
fi
cation


template





methods
<EssentialLink v-on:closeDrawer="closeme()"


v-for="link in essentialLinks" :key="link.title" v-bind="link" />
methods: {


closeme() {


console.log('event received');


this.leftDrawerOpen = false;


}


},
q-table
• https://quasar.dev/vue-components/table
Homework
• save user login timestamps to
fi
restore


• display user login info (w/ timestamp)
build your own table
q-table
• save user data to Firestore


var cdate = Date.now()


// firestore insert => then we can get doc id to generate qr code


db.collection("qrgen").add({


date: cdate,


id: this.getFireUser.email,


status: this.sliderValue //this.group


})


.then((docRef) => {


// console.log("Document written with ID: ", docRef.id);




})


.catch((error) => {


console.error("Error adding document: ", error);


});
q-table
• basic layout
<section v-if="!onloading">


<q-table :title="date" :rows="rows" :columns="columns" row-key="no" v-model:pagination="pagination" :filter="filter">


</q-table>


</section>
‘onloading’ set to be false when data is completely loaded data() {


return {


pagination: {


sortBy: 'time',


descending: false,


rowsPerPage: 15, // current rows per page being displayed




},


date: '2019/02/01',


filter : '',


columns : [


{


name: 'no',


required: true,


label: 'SID',


align: 'left',


field: row => row.no,


format: val => `${val}`,


sortable: true


},


{ name: 'name', align: 'left', label: 'Name', field: 'name', sortable: true },


{ name: 'time', align: 'left', label: 'Check-In Time', field: 'time', sortable: true },


{ name: 'temperature', label: 'Temperature', field: 'temperature', sortable: true }


],


rows : [ ],


cnt : 1,


onloading : true


}


},
q-table
data load
dataLoad: function () {


this.cnt = 1


var now = new Date();


var startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());


var timestamp = startOfDay / 1;


db.collection("checkin").where("time", ">=", timestamp) // 오늘 체크인만 확인


.onSnapshot((snapshot) => {


snapshot.docChanges().forEach((change) => {


if (change.type === "added") {


var tempRow = {name: '', time:'', temperature:''}


tempRow.name = change.doc.data().name


const getTime = (timeStamp, offset) => {


let d = new Date((timeStamp + offset));


var hour = d.getHours()


var min = d.getMinutes()


var sec = d.getSeconds()


if (hour < 10) { hour = "0" + hour; }


if (min < 10) { min = "0" + min; }


if (sec < 10) { sec = "0" + sec; }


let ret = (d.getMonth()+1)+ "/"+(d.getDate())+ " "+hour+ ":"+min+":"+sec


return ret


}


tempRow.time = getTime(change.doc.data().time,0)


tempRow.no = this.cnt


this.cnt++


tempRow.temperature = change.doc.data().temperature


this.rows.push(tempRow)


}


});


});


this.onloading = false


} // end of dataLoad
ref : https://
fi
rebase.google.com/docs/
fi
restore/query-data/listen?hl=ko
q-table
• additional function - search
<section v-if="!onloading">


<q-table :title="date" :rows="rows" :columns="columns" row-key="no" :pagination.sync="pagination" :filter="filter">


<template v-slot:top-right>


<q-input borderless dense debounce="300" v-model="filter" placeholder="Search">


<template v-slot:append>


<q-icon name="search"></q-icon>


</template>


</q-input>


</template>


</q-table>


</section>
q-table
• additional function - date picker
<template v-slot:top-left>


<div class="q-pa-md" style="width: 150px; margin-top: 20px">


<q-input borderless dense debounce="300" v-model="date" mask="date" :rules="['date']">


<template v-slot:append>


<q-icon name="event" class="cursor-pointer">


<q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">


<q-date v-model="date">


<div class="row items-center justify-end">


<q-btn v-close-popup label="Close" color="primary" flat @click="datePicked()"></q-btn>


</div>


</q-date>


</q-popup-proxy>


</q-icon>


</template>


</q-input>


</div>


</template>


should be coded between <q-table> and </q-table>
q-table
• additional function


- date picker
datePicked(){


this.cnt = 1


this.rows = []


var yyyyMMdd = String(this.date);


var sYear = yyyyMMdd.substring(0,4);


var sMonth = yyyyMMdd.substring(5,7);


var sDate = yyyyMMdd.substring(8,10);


var startOfDay = new Date(Number(sYear), Number(sMonth)-1, Number(sDate));


var timestamp = startOfDay / 1;


var timestampp1d = timestamp + 86400000


db.collection("users").where("date", ">=", timestamp).where("date", "<", timestampp1d)


.get()


.then((querySnapshot) => {


querySnapshot.forEach((doc) => {


var tempRow = {name: '', time:'', temperature:''}


tempRow.name = doc.data().email


const getTime = (timeStamp, offset) => {


let d = new Date((timeStamp + offset));


console.log(d)


var hour = d.getHours()


var min = d.getMinutes()


var sec = d.getSeconds()


if (hour < 10) { hour = "0" + hour; }


if (min < 10) { min = "0" + min; }


if (sec < 10) { sec = "0" + sec; }


let ret = (d.getMonth()+1)+ "/"+(d.getDate())+" "+hour+ ":"+min+":"+sec


return ret


}


tempRow.time = getTime(doc.data().date,0)


tempRow.no = this.cnt


this.cnt++


tempRow.name = doc.data().name


this.rows.push(tempRow)


});


})


.catch((error) => {


console.log("Error getting documents: ", error);


});


}
q-table
• additional function - csv export
<div align="right">


<q-btn


color="primary"


icon-right="archive"


label="Export to csv"


no-caps


@click="exportTable"


>


</q-btn>


</div>
exportTable () {


var columns = this.columns


var rows = this.rows


// naive encoding to csv format


const content = [columns.map(col => this.wrapCsvValue(col.label))].concat(


rows.map(row => columns.map(col => this.wrapCsvValue(


typeof col.field === 'function'


? col.field(row)


: row[ col.field === void 0 ? col.name : col.field ],


col.format


)).join(','))


).join('rn')


const status = exportFile(


'table-export.csv',


content,


'text/csv'


)


if (status !== true) {


$q.notify({


message: 'Browser denied file download...',


color: 'negative',


icon: 'warning'


})


}


},
q-table
• additional function - csv export
wrapCsvValue (val, formatFn) {


let formatted = formatFn !== void 0


? formatFn(val)


: val


formatted = formatted === void 0 || formatted === null


? ''


: String(formatted)


formatted = formatted.split('"').join('""')


/**


* Excel accepts n and r in strings, but some other CSV parsers do not


* Uncomment the next two lines to escape new lines


*/


// .split('n').join('n')


// .split('r').join('r')


return `"${formatted}"`


},


import { exportFile } from 'quasar'
FirebaseSecurityRules
• https://khreniak.medium.com/cloud-
fi
restore-security-rules-basics-fac6b6bea18e
makeyourprivacypolicy
• https://www.privacy.go.kr/a3sc/per/inf/perInfStep01.do
checklicenses
• https://www.npmjs.com/package/license-checker
OSS(Open Source Software)
npm install -g license-checker


license-checker --csv --out ./licenses.csv
androidstudio
• install


• license package download after installation


https://devvkkid.tistory.com/204


• build


• run (w/ AVD manager)
cordova
• npm install -g cordova


• quasar dev -m android --ide


android studio error might be occurred :


“Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Manager.”


if you get above error, then you should modify version settings in your build.grade as below image











then type below command on your terminal (https://stackover
fl
ow.com/questions/68387270/android-studio-error-installed-build-tools-revision-31-0-0-is-corrupted)


cd ~/Library/Android/sdk/build-tools/31.0.0 && mv d8 dx && cd lib && mv d8.jar dx.jar


then close android studio and re-run “quasar dev -m android —ide"
cordova
• result
cordova
• quasar build -m cordova -T android


• brew install gradle
if you get a net error on simulator, then check you port in quasar.conf.js
cordova
• https://bottlecok.tistory.com/177
aab build
cordova
• quasar dev -m cordova -T ios


• quasar build -m cordova -T ios


• https://www.youtube.com/watch?v=P_Ox5s4anCI
ios
Vue3 tutorial
extra
BasicStructure
• setup is called between “before created” and “created”


• you can set a style whatever you want
step 1
<template>


<div class="test">


Hello World, {{ name }}


</div>


</template>


<script>


import { defineComponent } from 'vue';


export default defineComponent({


name: 'PageIndex',


setup() {


const name = "KOPO"


return {


name


}


}


})


</script>


<style lang="scss">


.test {


color: red;


background-color: yellow;


text-align: center;


font-size: 30px;




}


</style>
BasicStructure
• add function “printName”
add a function
<template>


<div class="test">


Hello World, {{ name }}


<p></p>


{{ printName() }}


</div>


</template>


<script>


import { defineComponent } from 'vue';


export default defineComponent({


name: 'PageIndex',


setup() {


const name = "KOPO"


const printName = () => {


return name + "!!"


}


return {


name,


printName


}


}


})


</script>
BasicStructure
• add a parameter : param


• call the function with parameter
add a parameter
<template>


<div class="test">


Hello World, {{ name }}


<p></p>


{{ printName(name) }}


</div>


</template>


<script>


import { defineComponent } from 'vue';


export default defineComponent({


name: 'PageIndex',


setup() {


const name = "KOPO"


const printName = (param) => {


return 'hello '+ param + "!!"


}


return {


name,


printName


}


}


})


</script>
BasicStructure
• we want to change name string when a button is clicked.


but, the result is …


• tip> button width dynamically can be set.


use $ref !
add a button
<template>


<div class="test" ref="topitem">


Hello World, {{ name }}


<p></p>


{{ printName(name) }}


</div>


<q-btn


@click="changeName"


class="button is-primary"


color="primary"

label="BUTTON"


:style="{width: itemWidth+'px'}" />


</template>


<script>


import { defineComponent, ref } from 'vue';


export default defineComponent({


name: 'PageIndex',


mounted() {


this.itemWidth = this.$refs.topitem.clientWidth


console.log(this.itemWidth)


},


setup() {


let name = "KOPO"


let itemWidth = ref(0)


const printName = (param) => {


return 'hello '+ param + "!!"


}


const changeName = () => {


name = "Button Clicked"


console.log("name chaged? ", name)


}


return {


name,


itemWidth,


printName,


changeName


}


}


})


</script>
<style lang="scss">


.test {


color: red;


background-color: yellow;


text-align: center;


font-size: 30px;


width: 300px;


}


</style>
BasicStructure
• “KOPO” —> ref(“KOPO”)


• name —> name.value
use ref to make the variable to be reactive
setup() {


let name = ref("KOPO")


let itemWidth = ref(0)


const printName = (param) => {


return 'hello '+ param + "!!"


}


const changeName = () => {


name.value = "Button Clicked"


console.log("name chaged? ", name)


}


if you use object or array data, use reactive(“~”) instead ref(“~”)

Mais conteúdo relacionado

Mais procurados

Introducing Swagger
Introducing SwaggerIntroducing Swagger
Introducing SwaggerTony Tam
 
Angular 10 course_content
Angular 10 course_contentAngular 10 course_content
Angular 10 course_contentNAVEENSAGGAM1
 
Selenium Grid
Selenium GridSelenium Grid
Selenium Gridnirvdrum
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 formsEyal Vardi
 
Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...
Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...
Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...Simplilearn
 
Automation Framework Presentation
Automation Framework PresentationAutomation Framework Presentation
Automation Framework PresentationBen Ngo
 
Test Driven Development with PHPUnit
Test Driven Development with PHPUnitTest Driven Development with PHPUnit
Test Driven Development with PHPUnitMindfire Solutions
 
ASP.NET MVC Presentation
ASP.NET MVC PresentationASP.NET MVC Presentation
ASP.NET MVC Presentationivpol
 
Introduction to AngularJS
Introduction to AngularJSIntroduction to AngularJS
Introduction to AngularJSDavid Parsons
 
Integration using Apache Camel and Groovy
Integration using Apache Camel and GroovyIntegration using Apache Camel and Groovy
Integration using Apache Camel and GroovyClaus Ibsen
 
Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...
Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...
Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...Edureka!
 
Maven 3 Overview
Maven 3  OverviewMaven 3  Overview
Maven 3 OverviewMike Ensor
 

Mais procurados (20)

Introducing Swagger
Introducing SwaggerIntroducing Swagger
Introducing Swagger
 
Testing Angular
Testing AngularTesting Angular
Testing Angular
 
Angular 10 course_content
Angular 10 course_contentAngular 10 course_content
Angular 10 course_content
 
Selenium Grid
Selenium GridSelenium Grid
Selenium Grid
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 forms
 
Selenium
SeleniumSelenium
Selenium
 
Webpack slides
Webpack slidesWebpack slides
Webpack slides
 
Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...
Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...
Selenium Tutorial For Beginners | Selenium Automation Testing Tutorial | Sele...
 
Automation Framework Presentation
Automation Framework PresentationAutomation Framework Presentation
Automation Framework Presentation
 
Test Driven Development with PHPUnit
Test Driven Development with PHPUnitTest Driven Development with PHPUnit
Test Driven Development with PHPUnit
 
ASP.NET MVC Presentation
ASP.NET MVC PresentationASP.NET MVC Presentation
ASP.NET MVC Presentation
 
Introduction to AngularJS
Introduction to AngularJSIntroduction to AngularJS
Introduction to AngularJS
 
.Net Core 1.0 vs .NET Framework
.Net Core 1.0 vs .NET Framework.Net Core 1.0 vs .NET Framework
.Net Core 1.0 vs .NET Framework
 
Integration using Apache Camel and Groovy
Integration using Apache Camel and GroovyIntegration using Apache Camel and Groovy
Integration using Apache Camel and Groovy
 
Vue.js for beginners
Vue.js for beginnersVue.js for beginners
Vue.js for beginners
 
Selenium ppt
Selenium pptSelenium ppt
Selenium ppt
 
Angular
AngularAngular
Angular
 
Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...
Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...
Selenium WebDriver Tutorial For Beginners | What Is Selenium WebDriver | Sele...
 
Maven 3 Overview
Maven 3  OverviewMaven 3  Overview
Maven 3 Overview
 
Introduction to selenium
Introduction to seleniumIntroduction to selenium
Introduction to selenium
 

Semelhante a App development with quasar (pdf)

EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationRob Tweed
 
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD ApplicationRob Tweed
 
Easy Cloud Native Transformation using HashiCorp Nomad
Easy Cloud Native Transformation using HashiCorp NomadEasy Cloud Native Transformation using HashiCorp Nomad
Easy Cloud Native Transformation using HashiCorp NomadBram Vogelaar
 
Kraken
KrakenKraken
KrakenPayPal
 
JSNation.com - Azure Static Web Apps (SWA) with Azure DevOps
JSNation.com - Azure Static Web Apps (SWA) with Azure DevOpsJSNation.com - Azure Static Web Apps (SWA) with Azure DevOps
JSNation.com - Azure Static Web Apps (SWA) with Azure DevOpsJuarez Junior
 
Provisioning, deploying and debugging node.js applications on azure
Provisioning, deploying and debugging node.js applications on azureProvisioning, deploying and debugging node.js applications on azure
Provisioning, deploying and debugging node.js applications on azurePatriek van Dorp
 
Intro To Node.js
Intro To Node.jsIntro To Node.js
Intro To Node.jsChris Cowan
 
Kraken Front-Trends
Kraken Front-TrendsKraken Front-Trends
Kraken Front-TrendsPayPal
 
Control your deployments with Capistrano
Control your deployments with CapistranoControl your deployments with Capistrano
Control your deployments with CapistranoRamazan K
 
Pyramid Deployment and Maintenance
Pyramid Deployment and MaintenancePyramid Deployment and Maintenance
Pyramid Deployment and MaintenanceJazkarta, Inc.
 
Building websites with Node.ACS
Building websites with Node.ACSBuilding websites with Node.ACS
Building websites with Node.ACSralcocer
 
Building websites with Node.ACS
Building websites with Node.ACSBuilding websites with Node.ACS
Building websites with Node.ACSRicardo Alcocer
 
Laravel Beginners Tutorial 1
Laravel Beginners Tutorial 1Laravel Beginners Tutorial 1
Laravel Beginners Tutorial 1Vikas Chauhan
 
以Vue開發電子商務網站
架構與眉角
以Vue開發電子商務網站
架構與眉角以Vue開發電子商務網站
架構與眉角
以Vue開發電子商務網站
架構與眉角Mei-yu Chen
 
OCI Oracle Functions Deployment
OCI Oracle Functions Deployment OCI Oracle Functions Deployment
OCI Oracle Functions Deployment Toni Epple
 
Server(less) Swift at SwiftCloudWorkshop 3
Server(less) Swift at SwiftCloudWorkshop 3Server(less) Swift at SwiftCloudWorkshop 3
Server(less) Swift at SwiftCloudWorkshop 3kognate
 

Semelhante a App development with quasar (pdf) (20)

EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
 
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
 
Easy Cloud Native Transformation using HashiCorp Nomad
Easy Cloud Native Transformation using HashiCorp NomadEasy Cloud Native Transformation using HashiCorp Nomad
Easy Cloud Native Transformation using HashiCorp Nomad
 
Write php deploy everywhere tek11
Write php deploy everywhere   tek11Write php deploy everywhere   tek11
Write php deploy everywhere tek11
 
Kraken
KrakenKraken
Kraken
 
JSNation.com - Azure Static Web Apps (SWA) with Azure DevOps
JSNation.com - Azure Static Web Apps (SWA) with Azure DevOpsJSNation.com - Azure Static Web Apps (SWA) with Azure DevOps
JSNation.com - Azure Static Web Apps (SWA) with Azure DevOps
 
Provisioning, deploying and debugging node.js applications on azure
Provisioning, deploying and debugging node.js applications on azureProvisioning, deploying and debugging node.js applications on azure
Provisioning, deploying and debugging node.js applications on azure
 
Intro To Node.js
Intro To Node.jsIntro To Node.js
Intro To Node.js
 
Kraken Front-Trends
Kraken Front-TrendsKraken Front-Trends
Kraken Front-Trends
 
Control your deployments with Capistrano
Control your deployments with CapistranoControl your deployments with Capistrano
Control your deployments with Capistrano
 
Pyramid Deployment and Maintenance
Pyramid Deployment and MaintenancePyramid Deployment and Maintenance
Pyramid Deployment and Maintenance
 
Building websites with Node.ACS
Building websites with Node.ACSBuilding websites with Node.ACS
Building websites with Node.ACS
 
Building websites with Node.ACS
Building websites with Node.ACSBuilding websites with Node.ACS
Building websites with Node.ACS
 
Nodejs web,db,hosting
Nodejs web,db,hostingNodejs web,db,hosting
Nodejs web,db,hosting
 
Laravel Beginners Tutorial 1
Laravel Beginners Tutorial 1Laravel Beginners Tutorial 1
Laravel Beginners Tutorial 1
 
以Vue開發電子商務網站
架構與眉角
以Vue開發電子商務網站
架構與眉角以Vue開發電子商務網站
架構與眉角
以Vue開發電子商務網站
架構與眉角
 
OCI Oracle Functions Deployment
OCI Oracle Functions Deployment OCI Oracle Functions Deployment
OCI Oracle Functions Deployment
 
Dancing with websocket
Dancing with websocketDancing with websocket
Dancing with websocket
 
Love at first Vue
Love at first VueLove at first Vue
Love at first Vue
 
Server(less) Swift at SwiftCloudWorkshop 3
Server(less) Swift at SwiftCloudWorkshop 3Server(less) Swift at SwiftCloudWorkshop 3
Server(less) Swift at SwiftCloudWorkshop 3
 

Mais de wonyong hwang

Hyperledger Explorer.pptx
Hyperledger Explorer.pptxHyperledger Explorer.pptx
Hyperledger Explorer.pptxwonyong hwang
 
하이퍼레저 페이지 단위 블록 조회
하이퍼레저 페이지 단위 블록 조회하이퍼레저 페이지 단위 블록 조회
하이퍼레저 페이지 단위 블록 조회wonyong hwang
 
토큰 증권 개요.pptx
토큰 증권 개요.pptx토큰 증권 개요.pptx
토큰 증권 개요.pptxwonyong hwang
 
Vue.js 기초 실습.pptx
Vue.js 기초 실습.pptxVue.js 기초 실습.pptx
Vue.js 기초 실습.pptxwonyong hwang
 
Deploying Hyperledger Fabric on Kubernetes.pptx
Deploying Hyperledger Fabric on Kubernetes.pptxDeploying Hyperledger Fabric on Kubernetes.pptx
Deploying Hyperledger Fabric on Kubernetes.pptxwonyong hwang
 
k8s practice 2023.pptx
k8s practice 2023.pptxk8s practice 2023.pptx
k8s practice 2023.pptxwonyong hwang
 
HyperLedger Fabric V2.5.pdf
HyperLedger Fabric V2.5.pdfHyperLedger Fabric V2.5.pdf
HyperLedger Fabric V2.5.pdfwonyong hwang
 
Ngrok을 이용한 Nginx Https 적용하기.pptx
Ngrok을 이용한 Nginx Https 적용하기.pptxNgrok을 이용한 Nginx Https 적용하기.pptx
Ngrok을 이용한 Nginx Https 적용하기.pptxwonyong hwang
 
Nginx Https 적용하기.pptx
Nginx Https 적용하기.pptxNginx Https 적용하기.pptx
Nginx Https 적용하기.pptxwonyong hwang
 
Kafka JDBC Connect Guide(Postgres Sink).pptx
Kafka JDBC Connect Guide(Postgres Sink).pptxKafka JDBC Connect Guide(Postgres Sink).pptx
Kafka JDBC Connect Guide(Postgres Sink).pptxwonyong hwang
 
Nginx Reverse Proxy with Kafka.pptx
Nginx Reverse Proxy with Kafka.pptxNginx Reverse Proxy with Kafka.pptx
Nginx Reverse Proxy with Kafka.pptxwonyong hwang
 
Kafka monitoring using Prometheus and Grafana
Kafka monitoring using Prometheus and GrafanaKafka monitoring using Prometheus and Grafana
Kafka monitoring using Prometheus and Grafanawonyong hwang
 
주가 정보 다루기.pdf
주가 정보 다루기.pdf주가 정보 다루기.pdf
주가 정보 다루기.pdfwonyong hwang
 
Hyperledger Fabric practice (v2.0)
Hyperledger Fabric practice (v2.0) Hyperledger Fabric practice (v2.0)
Hyperledger Fabric practice (v2.0) wonyong hwang
 
Hyperledger fabric practice(pdf)
Hyperledger fabric practice(pdf)Hyperledger fabric practice(pdf)
Hyperledger fabric practice(pdf)wonyong hwang
 
Hyperledger composer
Hyperledger composerHyperledger composer
Hyperledger composerwonyong hwang
 

Mais de wonyong hwang (20)

Hyperledger Explorer.pptx
Hyperledger Explorer.pptxHyperledger Explorer.pptx
Hyperledger Explorer.pptx
 
하이퍼레저 페이지 단위 블록 조회
하이퍼레저 페이지 단위 블록 조회하이퍼레저 페이지 단위 블록 조회
하이퍼레저 페이지 단위 블록 조회
 
토큰 증권 개요.pptx
토큰 증권 개요.pptx토큰 증권 개요.pptx
토큰 증권 개요.pptx
 
Vue.js 기초 실습.pptx
Vue.js 기초 실습.pptxVue.js 기초 실습.pptx
Vue.js 기초 실습.pptx
 
Deploying Hyperledger Fabric on Kubernetes.pptx
Deploying Hyperledger Fabric on Kubernetes.pptxDeploying Hyperledger Fabric on Kubernetes.pptx
Deploying Hyperledger Fabric on Kubernetes.pptx
 
k8s practice 2023.pptx
k8s practice 2023.pptxk8s practice 2023.pptx
k8s practice 2023.pptx
 
HyperLedger Fabric V2.5.pdf
HyperLedger Fabric V2.5.pdfHyperLedger Fabric V2.5.pdf
HyperLedger Fabric V2.5.pdf
 
Ngrok을 이용한 Nginx Https 적용하기.pptx
Ngrok을 이용한 Nginx Https 적용하기.pptxNgrok을 이용한 Nginx Https 적용하기.pptx
Ngrok을 이용한 Nginx Https 적용하기.pptx
 
Nginx Https 적용하기.pptx
Nginx Https 적용하기.pptxNginx Https 적용하기.pptx
Nginx Https 적용하기.pptx
 
Kafka JDBC Connect Guide(Postgres Sink).pptx
Kafka JDBC Connect Guide(Postgres Sink).pptxKafka JDBC Connect Guide(Postgres Sink).pptx
Kafka JDBC Connect Guide(Postgres Sink).pptx
 
Nginx Reverse Proxy with Kafka.pptx
Nginx Reverse Proxy with Kafka.pptxNginx Reverse Proxy with Kafka.pptx
Nginx Reverse Proxy with Kafka.pptx
 
Kafka Rest.pptx
Kafka Rest.pptxKafka Rest.pptx
Kafka Rest.pptx
 
Kafka monitoring using Prometheus and Grafana
Kafka monitoring using Prometheus and GrafanaKafka monitoring using Prometheus and Grafana
Kafka monitoring using Prometheus and Grafana
 
주가 정보 다루기.pdf
주가 정보 다루기.pdf주가 정보 다루기.pdf
주가 정보 다루기.pdf
 
KAFKA 3.1.0.pdf
KAFKA 3.1.0.pdfKAFKA 3.1.0.pdf
KAFKA 3.1.0.pdf
 
kubernetes practice
kubernetes practicekubernetes practice
kubernetes practice
 
Hyperledger Fabric practice (v2.0)
Hyperledger Fabric practice (v2.0) Hyperledger Fabric practice (v2.0)
Hyperledger Fabric practice (v2.0)
 
Docker practice
Docker practiceDocker practice
Docker practice
 
Hyperledger fabric practice(pdf)
Hyperledger fabric practice(pdf)Hyperledger fabric practice(pdf)
Hyperledger fabric practice(pdf)
 
Hyperledger composer
Hyperledger composerHyperledger composer
Hyperledger composer
 

Último

EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 

Último (20)

EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 

App development with quasar (pdf)

  • 1. Dept. of Smart Finance, Korea Polytechnics Seoul Kangseo Campus ApplicationDevelopmentw/ QuasarFramework Basic Course
  • 2. QuasarCLIInstallation • Prerequisite 
 Node >=12.22.1 and NPM >=6.14.12 • recommendation node version : v14.17.5 
 check your node and npm : node -v 
 npm -v • npm install -g @quasar/cli Do not use uneven versions of Node i.e. 13, 15, etc. These versions are not tested with Quasar and often cause issues due to their experimental nature. We highly recommend always using the LTS version of Node. Ref : https://quasar.dev/quasar-cli/installation
  • 3. HelloworldⅠ(1/2) •  we create a project folder with Quasar CLI • quasar create <folder_name> Ref : https://quasar.dev/quasar-cli/installation (base) waynehwang@wayneui-MacBookPro temp % quasar create hello ___ / _ _ _ __ _ ___ __ _ _ __ | | | | | | |/ _` / __|/ _` | '__| | |_| | |_| | (_| __ (_| | | _____,_|__,_|___/__,_|_| ? Project name (internal usage for dev) hello ? Project product name (must start with letter if building mobile apps) hello ? Project description hello world ? Author wayne hwang <wonyong@wonyong.net> ? Pick your CSS preprocessor: SCSS ? Check the features needed for your project: ESLint (recommended), Vuex, Axios ? Pick an ESLint preset: Prettier ? Continue to install project dependencies after the project has been created? ( recommended) NPM Quasar CLI · Generated "hello". [*] Installing project dependencies … [*] Quasar Project initialization finished! To get started: cd hello quasar dev
  • 4. Helloworld Ⅰ(2/2) • Look into package.json, App.vue, routes.js, Index.vue, EssentialLink.vue, … • Run your app with below command: 
 quasar dev
  • 5. HelloWorldⅡ • Repository address : 
 https://github.com/wonyongHwang/kopoPro fi le • git clone https://github.com/wonyongHwang/kopoPro fi le • npm install • quasar dev kopoProfile
  • 6. HelloWorldⅡ • In this practice, we don’t have a 
 drawer and a footer either. So, only 
 <q-header> is being a ff ected by 
 <q-layout>. • layout <q-layout view="lHh lpr lFf"> reference : https://quasar.dev/layout/layout#understanding-the-view-prop
  • 7. HelloWorldⅡ • quasar can detect platform : $q.platfrom.is.~~ 
 
 eg> if you will deploy app to mobile device(android, 
 ios) app can detect platform using $q.platform.is. 
 cordova $q.platform <div v-bind:class="{'main': $q.platform.is.desktop, 'mobile_main': $q.platform.is.ipad || $q.platform.is.mobile}" class="full-height" style="background-color:rgba(0, 0, 0, 0.7);"> reference : https://quasar.dev/options/platform-detection#introduction
  • 8. HelloWorldⅡ style <q-header class="q-py-sm" style="background-color:rgba(0, 0, 0, 0.7);"> reference : https://quasar.dev/style/spacing#table-of-permutations
  • 9. HelloWorldⅡ q-tab reference : https://quasar.dev/vue-components/tabs <q-tabs v-model="selected_tab" shrink> <q-tab name="about_me" :style="[selected_tab == 'about_me' ? {backgroundColor: 'green'} : {}]" class="q-mr-sm q-py-xs custom_tab" @click="scrollToElement('id_about_me')" style="width:120px;min-height:auto !important;color: white" label="About Me"/>
  • 10. HelloWorldⅡ q-card <q-card class="my-card" flat bordered> <q-card-section> reference : https://quasar.dev/vue-components/card#introduction
  • 11. HelloWorldⅡ • Get/set scroll position (Quasar Scrolling Utils) 
 https://quasar.dev/quasar-utils/scrolling-utils#get-set-scroll-position <script></script> import {scroll} from 'quasar' const {getScrollTarget, setScrollPosition} = scroll export default { name: 'PageIndex', data() { return { name: "", email: "", message: "", selected_tab: 'about_me', } }, methods: { scrollToElement(id) { let el = document.getElementById(id) const target = getScrollTarget(el) const o ff set = el.o ff setTop - 65 const duration = 800 setScrollPosition(target, o ff set, duration) } }, mounted () { } }
  • 12. homework • customizing web page • upload source to your github repo. 
 - README.md should be edited ex>
  • 13. homework • deploy your web pages via netlify 
 https://app.netlify.com/
  • 14. homework • check url • if you have a own domain, netlify allow you to access your web site • ‘https(let’s encrypt)’ supports both custom domain and netlify domain
  • 15. Make Your First Page Basic Practice
  • 16. Layout • quasar create <folder name> • quasar dev • fi rst page is served by Index.vue 

  • 17. Layout • Quasar provides basic layouts reference : https://quasar.dev/layout/gallery
  • 18. SignIn • what you need to implement a sign-in page: 
 2 Input Text fi eld 
 1 Check Box 
 1 Button 
 and more …
  • 19. SignIn • copy relevant codes and paste to your “Index.vue” 
 https://quasar.dev/vue-components/input
  • 20. SignUp • add Signup.Vue to your project • add page mapping on routes.js
  • 21. pagelink • vue syntax for page link : 
 <router-link to=“/address”> ~~ </router-link> <router-link to="/signup" > <center>New User? Click Here to Register.</center> </router-link>
  • 22. Homework • Place and Organize Input Text fi elds (E-mail, Password, etc …) 
 : center alignment • Password should be masked while typing • Place Sign-In / Sign-Up buttons complete your sign-in / sign-up pages!
  • 23. f irebase • https:// fi rebase.google.com/?hl=ko create a project // For Firebase JS SDK v7.20.0 and later, measurementId is optional const fi rebaseCon fi g = { apiKey: "AIzaSyA~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", authDomain: "kopologinchecker. fi rebaseapp.com", projectId: "kopologinchecker", storageBucket: "kopologinchecker.appspot.com", messagingSenderId: "17~~~~~~~~~~~~", appId: "1:17~~~~~~~~:web:d4~~~~~~~~", measurementId: “G-H0~~~~~~~~~~~~" };
  • 24. • npm install --save fi rebase • quasar new boot fi rebase 
 -> fi rebase.js will be generated in src/boot 
 -> paste fi rebase con fi guration to fi rebase.js f irebase setting config. ref : https://quasar.dev/quasar-cli/boot- fi les import firebase from 'firebase/app'; import 'firebase/auth'; import 'firebase/analytics' const firebaseConfig = { apiKey: "~", authDomain: "kopologinchecker.firebaseapp.com", projectId: "kopologinchecker", storageBucket: "kopologinchecker.appspot.com", messagingSenderId: “~", appId: "~", measurementId: "~" }; if (!firebase.apps.length) { firebase.initializeApp(firebaseConfig); } firebase.analytics(); export const auth = firebase.auth();
  • 25. f irebase • enable email/password • create a test user if needed sign-in method
  • 26. f irebase • https:// fi rebase.google.com/docs/auth/web/start?hl=ko sign-in method fi rebase.auth().signInWithEmailAndPassword(email, password )   .then((userCredential) => {     // Signed i n     var user = userCredential.user ;     // .. .   } )   .catch((error) => {     var errorCode = error.code ;     var errorMessage = error.message ;   });
  • 27. • let’s use quasar notify to show success/fail message 
 https://quasar.dev/quasar-plugins/notify#introduction f irebase sign-in method auth.signInWithEmailAndPassword(this.text, this.password).then( (userCredential) => { console.log(userCredential.user); this.$router.push({ path: 'home' }) } ).catch( (err) => { console.log(err.code); console.log(err.message); this.$q.notify({ message: err.message, color: 'purple' }) } ) message, color, postion, textColor, icon, … import { useQuasar } from 'quasar' const $q = useQuasar() $q.notify({ message : "login success", color : "blue" }) vue 3
  • 28. homework • implement a sign-up function using fi rebase api • validation logic should be implemented 
 ex> email/password should not be empty 
 password should be equal to ‘repeat password’ 
 password must be at least 6 characters long 
 hint> https://levelup.gitconnected.com/ fi rebase-auth-management-in-vue-js-with-vuex-9c4a5d9cedc • move page after sign-up 
 hint> this.$router.push({ path: 'home' }) import { useRouter, useRoute } from 'vue-router' export default { setup() { const router = useRouter() const route = useRoute() // Now you can access params like : console.log(route.params.id) ; route.push({path: ‘home’} ) } }; vue v3 https://next.router.vuejs.org/guide/advanced/composition-api.html#accessing-the-router-and-current-route-inside-setup
  • 29. • Parent -> child : props • Child -> Parent : emit
  • 30. management f irebaseauthw/vuex • state management pattern and its library • state : data • mutations : change state (sync) • actions : commit mutation (async) what vuex is Actions State View Mutations this.$store.commit emit, props Unidirectional fl ow
  • 31. management f irebaseauthw/vuex • state = fi re auth info • ‘ fi re auth info’ can acquire when we call 
 signInWithEmailAndPassword() 
 createUserWithEmailAndPassword() 
 and so on. state Actions State View Mutations this.$store.commit import {auth} from "src/boot/firebase" export default store(function (/* { ssrContext } */) { const Store = createStore({ modules: { // example }, state: { fireUser:null }, … State
  • 32. management f irebaseauthw/vuex • get the current sign-in user is by setting an observer on the Auth object 
 https:// fi rebase.google.com/docs/auth/web/manage-users • state should be null when user signed out 
 https:// fi rebase.google.com/docs/auth/web/password-auth?hl=ko action actions: { signOutAction({commit}) { auth.signOut() .then(() => { commit("setFireUser", null); }) }, authAction({ commit }) { auth.onAuthStateChanged(user => { if (user) { commit("setFireUser", user); } }); }, }, mutations: { setFireUser(state, firebaseUser){ state.fireUser = firebaseUser } Actions Mutations
  • 33. management f irebaseauthw/vuex • prepare helper functions 
 https://joshua1988.github.io/web-development/vuejs/vuex-getters-mutations/ getters getters: { getFireUser(state) { return state.fireUser; }, isUserAuth(state) { return !!state.fireUser; } },
  • 34. management f irebaseauthw/vuex source code import { store } from 'quasar/wrappers' import { createStore } from 'vuex' import {auth} from "src/boot/firebase" export default store(function (/* { ssrContext } */) { const Store = createStore({ state: { fireUser:null }, actions: { signOutAction({commit}) { auth.signOut() .then(() => { commit("setFireUser", null); }) .catch(error => { commit("setFireError", error.message); }); }, authAction({ commit }) { auth.onAuthStateChanged(user => { if (user) { commit("setFireUser", user); } else { commit("setFireUser", null); } }); }, }, getters: { getFireUser(state) { return state.fireUser; }, isUserAuth(state) { return !!state.fireUser; } }, mutations: { setFireUser(state, firebaseUser){ state.fireUser = firebaseUser } }, }) return Store })
  • 35. management f irebaseauthw/vuex • commit state when my application obtain a fi rebase auth • get a fi rebase auth to display user info • check a fi rebase auth usage example auth.signInWithEmailAndPassword(this.text, this.password).then( (userCredential) => { this.$store.commit("setFireUser", userCredential.user); } ) <div class="text-h6" align="center"> Hi, {{ this.name }}({{ getFireUser.email }}) <br> … computed: { ...mapGetters(["getFireUser", "isUserAuth"]) }, mounted() { this.authAction(); … methods: { ...mapActions(["signOutAction","authAction"]), import { useStore } from "vuex" ; export default { setup() { const store = useStore() ; store.commit("setFireUser", userCredential.user) ; } }; vue v3 https://kyounghwan01.github.io/blog/Vue/vue3/composition-api-vuex/#vuex-%E1%84%89%E1%85%A6%E1%84%90%E1%85%B5%E1%86%BC-%E1%84%86%E1%85%B5%E1%86%BE-store-module-1%E1%84%80%E1%85%A2%E1%84%85%E1%85%A9-%E1%84%89%E1%85%B5%E1%86%AF%E1%84%92%E1%85%A2%E1%86%BC
  • 36. homework • When a user logged in, move Vue Page and display user’s email using vuex • tip> 
 <div v-if=“getFireUser">~~</div> 
 computed: { ...mapGetters(["getFireUser", "isUserAuth"]) }, <div class="text-h6" align="center"> Hi,({{ getFireUser.email }}) </div> import { mapGetters } from "vuex";
  • 37. vuelifecycle • https://cornswrold.tistory.com/342 • beforeCreate, created, beforeMount, mounted, 
 beforeUpdate, updated, beforeDestroy, destroyed
  • 40. Firestore • Firestore data add: set() or add() 
 https:// fi rebase.google.com/docs/ fi restore/manage-data/add-data?hl=ko API db.collection("users").add({ id: this.text, name: this.name }) .then((docRef) => { console.log("Document written with ID: ", docRef.id); this.$q.notify({ message: "Register Success", color: 'blue' }) }) .catch((error) => { console.error("Error adding document: ", error); this.$q.notify({ message: error, color: 'red' }) });
  • 41. Firestore • fi rebase.js 
 import ' fi rebase/ fi restore' 
 export const db = fi rebase. fi restore(); • import 
 import { db } from "src/boot/ fi rebase" Preparations
  • 42. Firestore Data write db.collection("users").add({ id: this.text, name: this.name }) .then((docRef) => { console.log("Document written with ID: ", docRef.id); }) .catch((error) => { console.error("Error adding document: ", error); });
  • 43. Firestore Data read db.collection("users").where("id", "==", this.getFireUser.email ) .get() .then((querySnapshot) => { querySnapshot.forEach((doc) => { // doc.data() is never undefined for query doc snapshots console.log(doc.id, " => ", doc.data()); this.name = doc.data().name }); }) .catch((error) => { console.log("Error getting documents: ", error); });
  • 44. Homework • When a user logged in, move page(/home) and display user’s name using vuex 
 - user name was saved at Firestore, so we have to read the data fi rst to show it. • user name should be displayed on page(/home) even after refreshing page.
  • 45. Homework Hint <template> <div> HOME <div class="text-h6" align="center"> <div v-if="getFireUser"> Hi,({{ getFireUser.email }}) :) {{ name }} </div> </div> </div> </template> <script> import { defineComponent } from 'vue'; import { auth, db } from "src/boot/firebase" import { useQuasar } from 'quasar' import { mapGetters, mapActions } from "vuex"; // export default defineComponent({ name: 'PageIndex', computed: { ...mapGetters(["getFireUser", "isUserAuth"]) }, updated() { if(this.getFireUser != null && this.name == ''){ // for refresh page // db.collection("users").where("id", "==", this.getFireUser.email ) .get() .then((querySnapshot) => { querySnapshot.forEach((doc) => { // doc.data() is never undefined for query doc snapshots console.log(doc.id, " => ", doc.data()); this.name = doc.data().name }); }) .catch((error) => { console.log("Error getting documents: ", error); }); } }, mounted(){ // this.authAction() // for refresh page // if(this.getFireUser != null){ db.collection("users").where("id", "==", this.getFireUser.email ) .get() .then((querySnapshot) => { querySnapshot.forEach((doc) => { // doc.data() is never undefined for query doc snapshots console.log(doc.id, " => ", doc.data()); this.name = doc.data().name }); }) .catch((error) => { console.log("Error getting documents: ", error); }); } }, data(){ return{ name : '' } }, methods: { ...mapActions(["signOutAction","authAction"]) } }) </script>
  • 46. f irebaseusermgmt. • https:// fi rebase.google.com/docs/auth/web/manage-users?hl=ko • User Info management 
 - user info update 
 - user account delete Some security-sensitive actions—such as deleting an account, setting a primary email address, and changing a password—require that the user has recently signed in. 
  • 47. Implementationof“rememberme”usinglocalStorage • UI 
 <q-checkbox v-model="remember" label="Remember Me" color="teal"/> • Declaration 
 let remember = ref(‘false') • mounted() • When a user tries to log in: if (remember.value == true) { localStorage.username = email.value; localStorage.checkbox = remember.value; } else { localStorage.username = ""; localStorage.checkbox = ""; } mounted(){ if(localStorage.checkbox && localStorage.checkbox !==""){ this.remember = true this.email = localStorage.username } else { this.remember = false } }
  • 49. Homework • Elaborate your work(home/sign-in/sign-up) • Conceptualizing your idea
  • 61. Menu • components hierarchy 
 ⎯ MainLayout.vue 
 EssentialLink.vue props and emit icon title label link + <template> <q-item clickable tag="a" target="_blank" :href="link"> <q-item-section v-if="icon" avatar> <q-icon :name="icon" /> </q-item-section> <q-item-section> <q-item-label>{{ title }}</q-item-label> <q-item-label caption> {{ caption }} </q-item-label> </q-item-section> </q-item> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ name: 'EssentialLink', props: { title: { type: String, required: true }, caption: { type: String, default: '' }, link: { type: String, default: '#' }, icon: { type: String, default: '' } } }) </script> props : hey, parent component! control me
  • 62. Menu • components hierarchy 
 ⎯ MainLayout.vue 
 EssentialLink.vue props and emit <EssentialLink v-for="link in essentialLinks" :key="link.title" v-bind="link" /> … import EssentialLink from 'components/EssentialLink.vue' const linksList = [ { title: 'Docs', caption: 'quasar.dev', icon: 'school', link: 'https://quasar.dev' }, … export default defineComponent({ name: 'MainLayout', components: { EssentialLink }, 
 
 setup () { return { essentialLinks: linksList, } } … parent component(MainLayout) now can set child props
  • 63. Menu • <Practice> 
 - open internal links in a current window 
 - open external links in a new tab (or window) props and emit
  • 64. Menu • <Practice> child component(EssentialLink) modi fi cation 
 props 
 

 template props and emit external: { type: Boolean, default: false } <template> <!-- <router-link :to="link" style="text-decoration: none"> --> <section v-if="external"> <q-item clickable tag="a" target="_target" :href="link"> … </q-item> </section> <section v-else> <router-link :to="link" style="text-decoration: none; color: inherit;”> … </router-link> </section> </template>
  • 65. Menu • <Practice> parent component(MainLayout) modi fi cation 
 props and emit const linksList = [ { title: 'Home', caption: 'Home', icon: 'home', link: '/home' }, { title: 'QR Scanner', caption: 'QR Check for Admin', icon: 'qr_code_scanner', link: 'https://kopologinchecker.web.app/#/qrscanner', 
 external: true }, …
  • 66. Menu • <Practice> 
 - close menu when a external link is clicked props and emit emit : hey, parent component! handle this
  • 67. Menu • <Practice> child component(EssentialLink) modi fi cation 
 template 
 

 methods props and emit <q-item clickable tag="a" target="_target" :href="link" @click="sendEvent()"> methods: { sendEvent() { this.$emit('closeDrawer'); console.log("emit emit") } }, • <Practice> parent component(MainLayout) modi fi cation 
 template 
 

 methods <EssentialLink v-on:closeDrawer="closeme()" 
 v-for="link in essentialLinks" :key="link.title" v-bind="link" /> methods: { closeme() { console.log('event received'); this.leftDrawerOpen = false; } },
  • 69. Homework • save user login timestamps to fi restore • display user login info (w/ timestamp) build your own table
  • 70. q-table • save user data to Firestore 
 var cdate = Date.now() // firestore insert => then we can get doc id to generate qr code db.collection("qrgen").add({ date: cdate, id: this.getFireUser.email, status: this.sliderValue //this.group }) .then((docRef) => { // console.log("Document written with ID: ", docRef.id); }) .catch((error) => { console.error("Error adding document: ", error); });
  • 71. q-table • basic layout <section v-if="!onloading"> <q-table :title="date" :rows="rows" :columns="columns" row-key="no" v-model:pagination="pagination" :filter="filter"> </q-table> </section> ‘onloading’ set to be false when data is completely loaded data() { return { pagination: { sortBy: 'time', descending: false, rowsPerPage: 15, // current rows per page being displayed }, date: '2019/02/01', filter : '', columns : [ { name: 'no', required: true, label: 'SID', align: 'left', field: row => row.no, format: val => `${val}`, sortable: true }, { name: 'name', align: 'left', label: 'Name', field: 'name', sortable: true }, { name: 'time', align: 'left', label: 'Check-In Time', field: 'time', sortable: true }, { name: 'temperature', label: 'Temperature', field: 'temperature', sortable: true } ], rows : [ ], cnt : 1, onloading : true } },
  • 72. q-table data load dataLoad: function () { this.cnt = 1 var now = new Date(); var startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()); var timestamp = startOfDay / 1; db.collection("checkin").where("time", ">=", timestamp) // 오늘 체크인만 확인 .onSnapshot((snapshot) => { snapshot.docChanges().forEach((change) => { if (change.type === "added") { var tempRow = {name: '', time:'', temperature:''} tempRow.name = change.doc.data().name const getTime = (timeStamp, offset) => { let d = new Date((timeStamp + offset)); var hour = d.getHours() var min = d.getMinutes() var sec = d.getSeconds() if (hour < 10) { hour = "0" + hour; } if (min < 10) { min = "0" + min; } if (sec < 10) { sec = "0" + sec; } let ret = (d.getMonth()+1)+ "/"+(d.getDate())+ " "+hour+ ":"+min+":"+sec return ret } tempRow.time = getTime(change.doc.data().time,0) tempRow.no = this.cnt this.cnt++ tempRow.temperature = change.doc.data().temperature this.rows.push(tempRow) } }); }); this.onloading = false } // end of dataLoad ref : https:// fi rebase.google.com/docs/ fi restore/query-data/listen?hl=ko
  • 73. q-table • additional function - search <section v-if="!onloading"> <q-table :title="date" :rows="rows" :columns="columns" row-key="no" :pagination.sync="pagination" :filter="filter"> <template v-slot:top-right> <q-input borderless dense debounce="300" v-model="filter" placeholder="Search"> <template v-slot:append> <q-icon name="search"></q-icon> </template> </q-input> </template> </q-table> </section>
  • 74. q-table • additional function - date picker <template v-slot:top-left> <div class="q-pa-md" style="width: 150px; margin-top: 20px"> <q-input borderless dense debounce="300" v-model="date" mask="date" :rules="['date']"> <template v-slot:append> <q-icon name="event" class="cursor-pointer"> <q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale"> <q-date v-model="date"> <div class="row items-center justify-end"> <q-btn v-close-popup label="Close" color="primary" flat @click="datePicked()"></q-btn> </div> </q-date> </q-popup-proxy> </q-icon> </template> </q-input> </div> </template> should be coded between <q-table> and </q-table>
  • 75. q-table • additional function 
 - date picker datePicked(){ this.cnt = 1 this.rows = [] var yyyyMMdd = String(this.date); var sYear = yyyyMMdd.substring(0,4); var sMonth = yyyyMMdd.substring(5,7); var sDate = yyyyMMdd.substring(8,10); var startOfDay = new Date(Number(sYear), Number(sMonth)-1, Number(sDate)); var timestamp = startOfDay / 1; var timestampp1d = timestamp + 86400000 db.collection("users").where("date", ">=", timestamp).where("date", "<", timestampp1d) .get() .then((querySnapshot) => { querySnapshot.forEach((doc) => { var tempRow = {name: '', time:'', temperature:''} tempRow.name = doc.data().email const getTime = (timeStamp, offset) => { let d = new Date((timeStamp + offset)); console.log(d) var hour = d.getHours() var min = d.getMinutes() var sec = d.getSeconds() if (hour < 10) { hour = "0" + hour; } if (min < 10) { min = "0" + min; } if (sec < 10) { sec = "0" + sec; } let ret = (d.getMonth()+1)+ "/"+(d.getDate())+" "+hour+ ":"+min+":"+sec return ret } tempRow.time = getTime(doc.data().date,0) tempRow.no = this.cnt this.cnt++ tempRow.name = doc.data().name this.rows.push(tempRow) }); }) .catch((error) => { console.log("Error getting documents: ", error); }); }
  • 76. q-table • additional function - csv export <div align="right"> <q-btn color="primary" icon-right="archive" label="Export to csv" no-caps @click="exportTable" > 
 </q-btn> </div> exportTable () { var columns = this.columns var rows = this.rows // naive encoding to csv format const content = [columns.map(col => this.wrapCsvValue(col.label))].concat( rows.map(row => columns.map(col => this.wrapCsvValue( typeof col.field === 'function' ? col.field(row) : row[ col.field === void 0 ? col.name : col.field ], col.format )).join(',')) ).join('rn') const status = exportFile( 'table-export.csv', content, 'text/csv' ) if (status !== true) { $q.notify({ message: 'Browser denied file download...', color: 'negative', icon: 'warning' }) } },
  • 77. q-table • additional function - csv export wrapCsvValue (val, formatFn) { let formatted = formatFn !== void 0 ? formatFn(val) : val formatted = formatted === void 0 || formatted === null ? '' : String(formatted) formatted = formatted.split('"').join('""') /** * Excel accepts n and r in strings, but some other CSV parsers do not * Uncomment the next two lines to escape new lines */ // .split('n').join('n') // .split('r').join('r') return `"${formatted}"` }, import { exportFile } from 'quasar'
  • 80. checklicenses • https://www.npmjs.com/package/license-checker OSS(Open Source Software) npm install -g license-checker license-checker --csv --out ./licenses.csv
  • 81. androidstudio • install • license package download after installation 
 https://devvkkid.tistory.com/204 • build • run (w/ AVD manager)
  • 82. cordova • npm install -g cordova • quasar dev -m android --ide 
 android studio error might be occurred : 
 “Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Manager.” 
 if you get above error, then you should modify version settings in your build.grade as below image 
 







 then type below command on your terminal (https://stackover fl ow.com/questions/68387270/android-studio-error-installed-build-tools-revision-31-0-0-is-corrupted) 
 cd ~/Library/Android/sdk/build-tools/31.0.0 && mv d8 dx && cd lib && mv d8.jar dx.jar 
 then close android studio and re-run “quasar dev -m android —ide"
  • 84. cordova • quasar build -m cordova -T android • brew install gradle if you get a net error on simulator, then check you port in quasar.conf.js
  • 86. cordova • quasar dev -m cordova -T ios • quasar build -m cordova -T ios • https://www.youtube.com/watch?v=P_Ox5s4anCI ios
  • 88. BasicStructure • setup is called between “before created” and “created” • you can set a style whatever you want step 1 <template> <div class="test"> Hello World, {{ name }} </div> </template> <script> import { defineComponent } from 'vue'; export default defineComponent({ name: 'PageIndex', setup() { const name = "KOPO" return { name } } }) </script> <style lang="scss"> .test { color: red; background-color: yellow; text-align: center; font-size: 30px; } </style>
  • 89. BasicStructure • add function “printName” add a function <template> <div class="test"> Hello World, {{ name }} <p></p> {{ printName() }} </div> </template> <script> import { defineComponent } from 'vue'; export default defineComponent({ name: 'PageIndex', setup() { const name = "KOPO" const printName = () => { return name + "!!" } return { name, printName } } }) </script>
  • 90. BasicStructure • add a parameter : param • call the function with parameter add a parameter <template> <div class="test"> Hello World, {{ name }} <p></p> {{ printName(name) }} </div> </template> <script> import { defineComponent } from 'vue'; export default defineComponent({ name: 'PageIndex', setup() { const name = "KOPO" const printName = (param) => { return 'hello '+ param + "!!" } return { name, printName } } }) </script>
  • 91. BasicStructure • we want to change name string when a button is clicked. 
 but, the result is … • tip> button width dynamically can be set. 
 use $ref ! add a button <template> <div class="test" ref="topitem"> Hello World, {{ name }} <p></p> {{ printName(name) }} </div> <q-btn @click="changeName" class="button is-primary" color="primary" label="BUTTON" :style="{width: itemWidth+'px'}" /> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ name: 'PageIndex', mounted() { this.itemWidth = this.$refs.topitem.clientWidth console.log(this.itemWidth) }, setup() { let name = "KOPO" let itemWidth = ref(0) const printName = (param) => { return 'hello '+ param + "!!" } const changeName = () => { name = "Button Clicked" console.log("name chaged? ", name) } return { name, itemWidth, printName, changeName } } }) </script> <style lang="scss"> .test { color: red; background-color: yellow; text-align: center; font-size: 30px; width: 300px; } </style>
  • 92. BasicStructure • “KOPO” —> ref(“KOPO”) • name —> name.value use ref to make the variable to be reactive setup() { let name = ref("KOPO") let itemWidth = ref(0) const printName = (param) => { return 'hello '+ param + "!!" } const changeName = () => { name.value = "Button Clicked" console.log("name chaged? ", name) } if you use object or array data, use reactive(“~”) instead ref(“~”)