2023년 6월 14일 수요일

[2023-06-14] Quasar fileuploader 문제 해결 422 (Unprocessable Entity) performUpload @ quasar.esm.js:36051 runFactory

 안녕하세요. 클스 입니다.


Quasar는 정말 vue3로 구현된 좋은 프레임워크 입니다. 

아직 사용자가 많지 않아서 레퍼런스가 별로 없다는 것이 문제긴 합니다.

파일 업로더가 있는데 파일을 Drag & Drop 하면 서버에 올려줍니다. 물론 서버 rest api 가 있어야 합니다.

https://quasar.dev/vue-components/uploader

그런데 메뉴얼 대로 했는데, 계속 오류가 납니다.

422 (Unprocessable Entity) performUpload @ quasar.esm.js:36051 runFactory


서버로 multi-part로 파일들을 보내는데, 서버쪽에서 files를 못받아서 문제 입니다.

RESTAPI 서버는 아래 오류를 계속 발생 시킵니다. swagger로 하면 문제없이 동작 합니다.

INFO:     127.0.0.1:59040 - "POST /api/upload/files HTTP/1.1" 422 Unprocessable Entity


fiileupload.py

@router.post("/upload/files")
async def create_upload_files(files: list[UploadFile]):
for file in files:
contents = await file.read()
with open(os.path.join(UPLOAD_DIR, file.filename), "wb") as fp:
fp.write(contents)
print(file.filename)

return {"filenames": [file.filename for file in files]}

fileupload.vue


<template>
<q-page class="bg-light-green window-height window-width row justify-center items-center">
<div class="q-pa-md">
<div class="q-gutter-sm row items-start">
<q-uploader
url="http://localhost:8001/api/upload/files"
label="Individual upload"
field-name="files" <!-- 서버에서 받을 변수명입니다. 아주 중요 -->
multiple
auto-upload
style="max-width: 300px"
/>
<q-uploader
url="http://localhost:8001/api/upload/files"
label="Batch upload"
multiple
field-name="files"
batch
style="max-width: 300px"
/>
</div>
</div>
</q-page>
</template>

오류의 원인이 이걸 안넣어줘서 그렇네요
field-name="files" <!-- 서버에서 받을 변수명입니다. 아주 중요 -->
async def create_upload_files(files: list[UploadFile]):
서버쪽에 넘겨줄 변수명이라 생각하면 됩니다. 그래서 맞춰주면 잘 올라갑니다.

아래 실행 화면에서 2개 타입이 있는데 하나는 파일을 선택하면 바로 올라가는 모드고, 다른 하나는
추가 후 upload 버튼으로 실행해줘야 합니다.

실행 화면



서버쪽에 올라간 파일 확인해보면 잘 올라가 있습니다.


이상 클스 였습니다.

라벨: , , , , ,

2023년 5월 31일 수요일

[2023-05-31] Quasar, Vue3 : Warning "Extraneous non-emits event listeners" shows up even when emits is set ...

안녕하세요. 클스 입니다.


Child.vue --> Parent.vue 와  데이터를 주고받을 때, emit을 사용합니다.

그런데 아래와 같은 오류가 발생합니다.

Warning "Extraneous non-emits event listeners" shows up even when emits is set

declare it using the "emits" option.


해결 방법1)

Parent.vue 에서 root 로 div를 감싸주는 된다. 그런데 UI 가 깨지는 문제가 발생한다.

<template> 
    <div>  : 추가
      .... 여러가지 컴포넌트 들....
    <MenuComponent msg="string input" @testChange="testChange"/>
   </div>   : 추가
</template>

Child.vue 에서 아래와 같이 보내준다.

<script lang="ts">

...

methods: {
emitTestChange(val) {
this.$emit('testChange', val);
},

</script>


해결 방법2) 

Child.vue에 emits 를 추가해준다.

Parent.vue 에서 root 로 div를 감싸주는 된다. 그런데 UI 가 깨지는 문제가 발생한다.

<template> 
      .... 여러가지 컴포넌트 들....
    <MenuComponent msg="string input" @testChange="testChange"/>
</template>

Child.vue 에서 아래와 같이 보내준다.

<script lang="ts">

export default defineComponent({
emits: ['testChange'], ==> 이거 한줄 추가
setup(props) {
return {
        //TODO:
    };
},
methods: {
emitTestChange(val) {
this.$emit('testChange', val);
},

</script>


이상 클스 였습니다.

테스트 버전 ; vue3   quasar 2.11.x  chrome, macos 13.4 입니다.

라벨: , , ,

2023년 5월 15일 월요일

[2023-05-15] Quasar QSelect 옵션 설명

 안녕하세요. 클스 입니다.


Quasar QSelect 옵션에 대한 설명을 하고자 합니다.


회사를 선택하거나, 여러 코드값을 선택할때 아래와 같은 화면이 필요합니다. 그리고 company_id 값으로

미리 선택되도록 해야 합니다. q-select 의 option 은 label, value 2개 속성이 기본 입니다. 

물론 option-value = 'yyy' , option-label = "xxx" 이렇게 해서 커스텀 가능합니다.

company_options: [
{
label: 'Google',
value: 'Google-value',
description: 'Search engine',
category: '1',
},


 
소스를 보면 아래와 같이 사용하면 됩니다. 그런데 map-options, emit-value 2개 옵션을 잘 사용해야 원하는 값을 얻게 됩니다.

<q-select
filled
v-model="company_model"
:options="company_options"
label="Standard"
map-options
emit-value />


map-options 은 value 값으로 선택을 할 수 있게 하는 옵션입니다.

emit-value 은 선택된 option의 value 값만 넘겨 줍니다.


앞에서 option을 object로 선언했기 때문에 map-option이 없으면 value 값이 화면에 표시됩니다.

아래 화면에서 보면 Apple-Value를 표시됩니다. 원하는건 Apple이 표시되길 원합니다.

이때 map-options 을 사용하면 value 값으로 매칭해서 Label을 출력해줍니다.

그리고 Object로 선언된 option이므로 선택하면 Object 가 값으로 넘어옵니다. company_id만 필요한데 불필요하게 Object 값이 넘어오니 company_model.value 이렇게 사용해야 됩니다.
바로 value를 string으로 받기 위해서 emit-value 사용하면 됩니다.

emit-value 를 사용하지 않으면 아래와 같이 값이 넘어옵니다.

최종적으로 우리가 원하는 화면을 위해서는 아래와 같이 전체 소스를 확인할 수 있습니다.

<!-- MainLayout.vue -->

<template>
<div class="q-pa-md" style="max-width: 300px">
<div class="q-gutter-md">
<q-badge color="secondary" multi-line> Model: "{{ company_model }}" </q-badge>
<p>Selected Value: {{ company_model }}</p>

<q-select filled v-model="company_model" :options="company_options" label="Standard" emit-value map-options />
</div>
</div>
</template>

<script>
import { ref } from 'vue';

export default {
setup() {
return {
company_model: ref(null),

company_options: [
{
label: 'Google',
value: 'Google-value',
description: 'Search engine',
category: '1',
},
{
label: 'Facebook',
value: 'Facebook-value',
description: 'Social media',
category: '1',
},
{
label: 'Twitter',
value: 'Twitter-value',
description: 'Quick updates',
category: '2',
},
{
label: 'Apple',
value: 'Apple-value',
description: 'iStuff',
category: '2',
},
{
label: 'Oracle',
value: 'Oracle-value',
disable: true,
description: 'Databases',
category: '3',
},
],
};
},
mounted() {
this.company_model = 'Apple-value';
},
};
</script>


라벨: , , ,