nuxt数据库之增删改查,父组件子组件传值

nuxt学到数据库这里,就涉及到响应数据,父组件向子组件传值,子组件向父组件传值,最终还是需要掌握vue3的组件知识了。学习真的是一个长期的过程,不管学习了什么知识,有多少,都应该及时的记录下来,这个记录的过程,就是不断巩固自身知识的过程。之前学习其他框架的时候,不用,过两天就忘记了,当许多插件解决问题,忘记这个插件名字后,那想不起来的时候就特别痛苦了。好记忆不如烂笔头,及时记录下来吧。

ui.nuxt.com中的form章节,有很多实例代码:Form - Nuxt UI

还有,github上有很多样例代码,比如这个Table的代码: https://github.com/nuxt/ui/blob/dev/docs/components/content/examples/TableExampleAdvanced.vue

这个Table例子看了好多遍,代码还是掌握不了啊,感觉有点难啊。Table - Nuxt UI

还是根据table上面的简单的例子,做一些修改吧,从简单的入手。

首先接着之前的文章,把drizzle orm的增删改查等做成对应的api接口路由。https://hub.nuxt.com/docs/recipes/drizzle#usage

读:select

server/api/users/index.get.ts

TypeScript 复制代码
export default eventHandler(async () => {
  const todos = await useDrizzle().select().from(tables.users).all()

  return todos
})

增:insert

server/api/users/index.post.ts

TypeScript 复制代码
export default eventHandler(async (event) => {
  const { name, email, password, avatar, createdAt } = await readBody(event);
  const todo = await useDrizzle()
    .insert(tables.users)
    .values({
      name,
      email,
      password,
      avatar,
      createdAt: new Date(),
    })
    .returning()
    .get();

  return todo;
});

改:update

server/api/users/[id].patch.ts

TypeScript 复制代码
export default eventHandler(async (event) => {
  const { id } = getRouterParams(event);
  const { completed } = await readBody(event);

  const todo = await useDrizzle()
    .update(tables.users)
    .set({
      completed,
    })
    .where(eq(tables.users.id, Number(id)))
    .returning()
    .get();

  return todo;
});

删:delete

server/api/users/[id].delete.ts

TypeScript 复制代码
export default eventHandler(async (event) => {
  const { id } = getRouterParams(event);

  const deletedTodo = await useDrizzle()
    .delete(tables.users)
    .where(and(eq(tables.users.id, Number(id))))
    .returning()
    .get();

  if (!deletedTodo) {
    throw createError({
      statusCode: 404,
      message: 'User not found',
    });
  }
  return deletedTodo;
});

读取的时候,都是一次性的把数据全部select出来,这个当然是不对的,不过初学的时候,先这样应付着写,之后看看怎么带参数select,进行一页一页的读取。

修改和删除的时候,是根据id来操作的,这个时候,url中就需要带上id的值这个参数了。

使用UTable控件把表渲染出来。第一部就是添加数据了。先进行简单的:

TypeScript 复制代码
    const datas = {
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123',
        avatar: 'https://example.com/avatar/john.png',
        createdAt: new Date(),
}



$fetch('/api/users', {
      method: 'POST',
      body: datas,
    }).then((users: any) => {
      useToast().add({
        title: `${users.id}号添加成功!`,
        description: `
            用户名:${users.name}
            邮箱:${users.email}
            `,
      });
    });

先定义需要添加的数据,然后使用$fetch进行post添加。成功后弹出消息提示一下。

下一步就是点击按钮,弹窗添加新用户,弹出的窗口做成控件,那就涉及到父组件向子组件传值,和子组件向父组件传值的了。

简单来说,子组件向父组件传值,就是要emit,子组件emit一个方法,在父组件用这个方法名绑定一个新的方法,父组件上的新的方法就可以执行子组件方法里的方法了,也可以执行父组件上的其他方法。

父组件向子组件传值,那就更简单了,就是子组件先定义一个属性值,不用赋初始值,在父组件上使用的时候,给这个属性值绑定一个数值,在子组件里,就可以直接获取这个属性值的值(父组件上绑定的值)。

先看子组件向父组件传值(方法):【Vue/Nuxt.js】使用Composition API实现子组件向父组件传递数据的方法 - TeHub

TypeScript 复制代码
子组件
<script setup>
const emit = defineEmits(['hogeEvent']); //emit之外也可以
const hoge = () =>{
    emit('hogeEvent');
}
</script>
<template>
    <button type="button" @click="hoge">emit测试</button>
</template>
父组件
<script setup>
const customEvent = () =>{
  console.log('test')
}
</script>
<template>
  <ChildComponents @hogeEvent="customEvent"></ChildComponents>
</template>

在我这个user的实例中,有很多属性值,所可以emit可以多个函数值:

TypeScript 复制代码
/**
 * 属性
 */
type Props = { modelValue: boolean; info: number };
// defineProps<Props>();
const props = defineProps<Props>();
const { info } = toRefs(props);

最后一行info被从属性值里解构出来变成响应式数据后,就可以直接使用了。

TypeScript 复制代码
/**
 * 事件
 */
const emit = defineEmits(['parentRefresh', 'update:modelValue', 'hogeEvent']);

const onUpdate = (value: boolean) => {
  emit('update:modelValue', value);
};

const parentRefresh = () => {
  const data = { message: '这是子组件传递的数据' };
  emit('parentRefresh', data);
};

emit可以定义多个方法,并且需要在下面进行方法的实现,如果父组件中在绑定的方法中有其他方法需要执行,比如刷新表refresh()操作,就需要在最后执行一下parentRefresh()

父组件中绑定的方法或属性值:

TypeScript 复制代码
      <DataUserForm
        v-model="isOpen"
        @parentRefresh="showrefresh"
        @hoge-event="customEvent"
        :info="info"
      />


const isOpen = ref(false);

const info = computed(() => {
  console.log(users.value.length);

  return users.value.length;
});

/**
 * 添加用户 按钮事件
 */
const addUser = () => {
  isOpen.value = true;
};
const showrefresh = (data: any) => {
  console.log('父组件接收到的数据:', data);
  refresh();
};
const customEvent = () => {
  console.log('test');
};

isOpen是父组件上的按钮点击后显示子组件弹窗的UModal控件。info在父组件是计算select出来所有users表的数据长度,定义的时候,涉及到其他数据来源,所以需要使用computed方法,才能让数据保持响应式数据,自动同步更新。这个打印数据的动作console.log(users.value.length);,就是测试数据是否同步:

打印出来第一次的0,就是info的初始值0,第二次59,是获取数据库数据后,computed计算出来的值。或者加上服务端客服端执行判断:

TypeScript 复制代码
const info = computed(() => {
  if (import.meta.server) {
    console.log('服务端执行:' + users.value.length);
  }
  if (import.meta.client) {
    console.log('客户端端执行:' + users.value.length);
  }
  return users.value.length;
});

这样看就比较明显了,先执行客户端,info的值为0,然后等从数据库select的值获取到后,再次同步了info的值59,客户端执行了2次。 这个就是响应式数据自动更新的动作,数据变动,就是computed再次执行一次。

refresh()刷新数据表,就是读取数据方法useLazyFetch的一个互动操作:

TypeScript 复制代码
const { status, data, refresh } = await useLazyFetch<any>('/api/users');

const users = computed(() => {
  filtered.value.sort((a: any, b: any) => {
    return b.id - a.id;
  });
  return filtered.value;
});


  <UTable
    :columns="columns"
    :emptyState="table.emptyState"
    :loadingState="table.loadingState"
    :rows="rows"
    :loading="status === 'pending'"
    class="mb-2"
  >

:loading="status === 'pending'" 是加载的时候,在获取数据前有个加载动画。获取的对象数组的排序方法非常有意思:

TypeScript 复制代码
  array.sort((a: any, b: any) => {
    return b.id - a.id;
  });

这个是降序,如果想升序,return a.id-b.id就可以了。

还有一个知识点,就是node时间格式化操作,其他框架有不少操作插件,安装之后就可以进行日期格式化了,nuxt上搜索了一些插件,比如moment,time等,研究一下还是不行,最后点到new Date()上发现,typescript官方就有日期本地格式化的操作啊!!!

幸亏跟着drizzle orm 的教程添加了seed种子(https://hub.nuxt.com/docs/recipes/drizzle#seed-the-database-optional),不然还发现不了,估计要耽搁很多时间啊。

关于数据表UTable的很多操作,还需要从ui/docs/components/content/examples/TableExampleAdvanced.vue at dev · nuxt/ui · GitHub

这个源码里慢慢的学习,加上baidu和google搜索,不然很花时间啊。

还有一个,就是需要安装vscode职能插件,我安装的是 通义灵码,免费的,还是很不错的,可以提醒不同的写法。

关于创建表单数据,并提交,在ui.nuxt.com上的form栏,有不同的数据库实体类的插件,可以挑选https://ui.nuxt.com/components/form#usage

使用方法大同小异。

TypeScript 复制代码
/**
 * zod
 */
import { z } from 'zod';
import type { FormError, FormSubmitEvent } from '#ui/types';

const schema = z.object({
  name: z.string(),
  email: z.string().email('邮箱格式不正确'),
  password: z.string().min(4, '最少4位字符'),
  avatar: z.string(),
});
type Schema = z.output<typeof schema>;

const state = computed(() => {
  return reactive({
    name: z.string().parse('andu' + (info.value + 1)),
    email: z.string().parse('andu' + (info.value + 1) + '@qq.com'),
    password: z.string().parse('andu' + (info.value + 1) + 'password'),
    avatar: z.string().parse('https://picsum.photos/200/200'),
  });
});

const validate = (state: any): FormError[] => {
  const errors = [];
  if (!state.email) errors.push({ path: 'email', message: '必填项' });
  if (!state.password) errors.push({ path: 'password', message: '必填项' });
  return errors;
};

在添加用户的时候,我想自动的给表单赋值,免得一个个手动输入太麻烦了。我的想法是获取数据长度,传给子组件,然后在名称和邮箱名后面数值+1,这样邮箱不就不重复了吗?info就是从父组件传递进来的数据长度值,怎么让表单的数据都自动更新呢?当然是要把它们放到computed里面去了。这样添加数据的是很不用一个个输入的了。

子组件最上角就是获取的数据长度。注意:这个赋值的过程,根本搜不到啊,非常非常意外的是,捣腾了好久,终于,通义灵码给自动推荐出来了!!!好意外!!!

TypeScript 复制代码
name: z.string().parse('andu' + (info.value + 1)),

就是这个z.string().parse()方法,原理是啥,我也不懂,就这么特么的,智能推荐出来了。AI写代码,有的时候是真牛啊。

查询、增加做完了,下面就是删除,删除最简单:

TypeScript 复制代码
const items = (row: any) => [
  [
    {
      label: '编辑',
      icon: 'i-heroicons-pencil-square-20-solid',
    },
    {
      label: '复制',
      icon: 'i-heroicons-document-duplicate-20-solid',
    },
  ],
  [
    {
      label: '存档',
      icon: 'i-heroicons-archive-box-20-solid',
    },
    {
      label: '移动',
      icon: 'i-heroicons-arrow-right-circle-20-solid',
    },
  ],
  [
    {
      label: '删除',
      icon: 'i-heroicons-trash-20-solid',
      click: () => {
        $fetch('/api/users/' + row.id, {
          method: 'DELETE',
        }).then((users: any) => {
          useToast().add({
            title: `${users.id}号删除成功!`,
            description: `
          用户名:${users.name}<br/>
          邮箱:${users.email}
          `,
          });
        });
        refresh();
      },
    },
  ],
];



    <template #actions-data="{ row }">
      <UDropdown :items="items(row)">
        <UButton
          color="gray"
          variant="ghost"
          icon="i-heroicons-ellipsis-horizontal-20-solid"
        />
      </UDropdown>
    </template>
  </UTable>

删除是放在表格最后一列里,这个也是官网的例子有的。只要是路由有id:[id].delete,就需要在$fetch方法中的url里加上row.id

删除后,刷新一下表refresh()。

修改还没做,应该也不难了。这几天卡在好几个点。第一个是日期格式化操作,尝试了几个插件和使用,花了不少时间。还有就是父组件和子组件之间的相互传值,这个应该是vue的基本知识,或者说是vue3的知识,网上多是Vue2的代码,搜索不少时间,慢慢尝试和总结出来传值的规律了。还有就是响应式数据问题,info的获取后,怎么在表单里让它自动更新,变成响应式数据,也是摸索学习了不少时间。最后就是ui.nuxt.com上最后Table的例子的源代码,直接照搬过来,还是没学会怎么用,感觉官方的代码好少,做的时候还多,而且官方的例子源码还不少,这要学起来,那得花多少时间啊......

时间太不够用了......

相关推荐
Amd79419 天前
Nuxt.js 应用中的 afterResponse 事件钩子
安全·日志·nuxt·清理·性能·钩子·响应
Amd79420 天前
Nuxt.js 应用中的 beforeResponse 事件钩子
安全·nuxt·性能·用户·处理·钩子·响应
Amd79422 天前
Nuxt.js 应用中的 request 事件钩子
安全·nuxt·性能·请求·处理·钩子·实践
Amd79423 天前
Nuxt.js 应用中的 close 事件钩子
生命周期·nuxt·清理·钩子·nitro·资源清理·close钩子
Amd79425 天前
Nuxt.js 应用中的 render:island 事件钩子
服务器·渲染·客户端·nuxt·seo·钩子·动态
Amd79425 天前
Nuxt.js 应用中的 render:html 事件钩子
安全·html·渲染·nuxt·seo·钩子·动态
一舍予1 个月前
nuxt3项目搭建相关
开发语言·javascript·vue.js·nuxt
Amd7941 个月前
Nuxt.js 应用中的 webpack:progress 事件钩子
ui·webpack·控制台·nuxt·构建·进度·钩子
Amd7941 个月前
Nuxt.js 应用中的 webpack:error 事件钩子
webpack·编译·nuxt·错误·事件·处理·钩子
Amd7941 个月前
Nuxt.js 应用中的 vite:compiled 事件钩子
自定义·vite·编译·nuxt·热更新·性能·钩子