首页 > 解决方案 > 使用 API (Rails) 中的 Svelte 正确处理错误

问题描述

我正在开发一个与 Rails API 服务器通信的 Svelte 前端应用程序。我喜欢 Rails 的抽象方式(尤其是 simple_form gem),我想为 Svelte 带来一些想法。我遇到的特殊问题是如何将来自 rails 服务器的错误与苗条端的输入相关联

首先,我调整了来自服务器的错误响应;一般来说,它们看起来像这样:

{ errors: 
  { generic: [<messages without association with particular field>], 
    email: ["Already taken", <msg_2>, ...], 
    password: ["Too short", ...],
    field_N: [...]
  }
}

一般错误可以是“无效的登录凭据”、“您的帐户被锁定”、“内部服务器错误”等。其他错误必须与表单字段相关,并且必须显示在它们旁边。

这是我目前的方法signup.svelte

<script>
let email;
let password;
function registerRequest() {
  let regURL = baseURL+'/api/users/';
  let data = {"user": {"email": email, "password": password}};
  fetch(regURL, {
    method: 'POST',
    body: JSON.stringify(data),
    headers: {
      'Content-Type': 'application/json'
    }
  })
  .then(function(response) {
    if (!response.ok) {
      return response.json()
    } 
  })
  // if errors present then iterate through errors object
  .then(function(error) {
    // find id of the message field and insert actual messages
    for (let [key, value] of Object.entries(error.errors)) {
 document.getElementById(key).insertAdjacentHTML('beforeend',value);
    }
  });
}
</script>

<style> .errormessage { color: red; } </style>

<form class="col-sm-12 col-lg-6" on:submit|preventDefault={registerRequest}>
  <!-- here I need to create errormessage container for each field. -->
  <!-- first one is generic to display generic errors -->
  <div class="errormessage" id="generic"></div>

  <label for="UserEmail">Email address</label>
  <input type="email" class="form-control" id="UserEmail" aria-describedby="emailHelp" placeholder="Enter your email here..." bind:value={email}>
  <div class="errormessage" id="email"></div>

  <label for="UserPassword">Password</label>
  <input type="password" class="form-control" id="UserPassword" placeholder="...and your password here" bind:value={password}>
  <div class="errormessage" id="password"></div>

  <button type="submit" class="btn btn-primary">Register</button>
</form>

我想将上面的代码与前端验证统一起来。如果errors对象出现 - 在有错误的字段旁边显示消息。在我看来,使用getElementById是矫枉过正。如果我的 DOM 从不重新加载,我为什么要使用它?每次input它都会搜索ID。也许应该是监听器来监听错误对象的变化?我试图将自定义event与 Svelte 绑定import { createEventDispatcher } from 'svelte';,但没有运气。

请帮助并分享您的想法。

标签: javascriptruby-on-railserror-handlingapi-designsvelte

解决方案


您的直觉是正确的,getElementById在 Svelte 组件中使用会让人感觉不舒服 - 通常 Svelte 希望您让您的代码具有声明性,其中 DOM 是状态的函数,而不是您手动更改 DOM 的命令。

对于错误消息,我建议errors在您的组件中添加一个变量。当服务器错误再次出现时,您可以将它们分配给errors,这将导致响应式更新并重新渲染组件。然后,标记可以知道在错误存在时呈现错误,而无需显式更改 DOM。像这样的东西:

<script>
let email;
let password;
// New `errors` variable.
let errors = {};

function registerRequest() {
  // Reset the errors when the user submits a new request.
  errors = {};  

  let regURL = baseURL+'/api/users/';
  let data = {"user": {"email": email, "password": password}};
  fetch(regURL, {
    method: 'POST',
    body: JSON.stringify(data),
    headers: {
      'Content-Type': 'application/json'
    }
  })
  .then(function(response) {
    if (!response.ok) {
      return response.json()
    } 
  })
  .then(function(error) {
    // Just save the errors 
    errors = e.errors;
  });
}
</script>

<style> .errormessage { color: red; } </style>

<form class="col-sm-12 col-lg-6" on:submit|preventDefault={registerRequest}>
    <!-- first one is generic to display generic errors -->
    <!-- note the `|| []` part, which makes sure that
         `#each` won't throw an error when `errors.generic` doesn't exist -->
    {#each (errors.generic || []) as error}
    <div class="errormessage" id="generic">{error}</div>
    {/each}

  <label for="UserEmail">Email address</label>
  <input type="email" class="form-control" id="UserEmail" aria-describedby="emailHelp" placeholder="Enter your email here..." bind:value={email}>
    <!-- email errors -->
    {#each (errors.email || []) as error}
  <div class="errormessage" id="email">{error}</div>
    {/each}

  <label for="UserPassword">Password</label>
  <input type="password" class="form-control" id="UserPassword" placeholder="...and your password here" bind:value={password}>
    <!-- password errors -->
    {#each (errors.password || []) as error}
  <div class="errormessage" id="password">{error}</div>
    {/each}

  <button type="submit" class="btn btn-primary">Register</button>
</form>

这是一个Svelte REPL,该代码可以正常工作。希望有帮助!


推荐阅读