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

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

0
user1201917 21 Окт 2019 в 04:09

1 ответ

Лучший ответ

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

3
will_wow 21 Окт 2019 в 01:41