Правильная обработка ошибок с Svelte из API (Rails) - PullRequest
0 голосов
/ 21 октября 2019

Я разрабатываю приложение внешнего интерфейса Svelte, которое взаимодействует с сервером API rails. Мне нравится рельсовый способ абстракции (особенно с гемом simple_form), и я хотел бы донести некоторые идеи до Svelte. Конкретная проблема, с которой я столкнулся, заключается в том, как связать ошибки с сервера rails со входами на стороне svelte

Сначала я настроил ответы об ошибках с сервера;в общем случае они выглядят так:

{ 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 он будет искать идентификаторы. Может быть, это должен быть слушатель, который слушает изменения объекта ошибок? Я пытался связать пользовательский event с Svelte import { createEventDispatcher } from 'svelte';, но безуспешно.

Пожалуйста, помогите и поделитесь своими мыслями.

1 Ответ

2 голосов
/ 21 октября 2019

Ваша интуиция права, что использование 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 с этим работающим кодом. Надеюсь, это поможет!

...