Не удалось привязать параметр в раскрывающемся списке дочернего компонента в Blazor

#asp.net #asp.net-core #blazor #blazor-server-side #asp.net-blazor

Вопрос:

Я работаю над ASP.NET Основное серверное приложение Blazor (.NET 5.0). Я пытаюсь добавлять/редактировать пользователей в режиме и назначать роли пользователю в другом режиме. Итак, у меня есть родительский компонент и два дочерних компонента.

Родительский компонент:

 @if (ShowPopup)
{    
    <!-- This is the popup to create or edit user -->
    <EditUserModal ClosePopup="ClosePopup" CurrentUserRoles="" DeleteUser="DeleteUser" objUser="objUser" SaveUser="SaveUser" strError="@strError" />
}

@if (ShowUserRolesPopup)
{
    <!-- This is the popup to create or edit user roles -->
    <EditUserRolesModal AddRoleToUser="AddRoleToUser" AllRoles="AllRoles" ClosePopup="ClosePopup" CurrentUserRoles="@CurrentUserRoles" objUser="objUser" RemoveRoleFromUser="RemoveRoleFromUser" SelectedUserRole="@SelectedUserRole" strError="@strError" _allUsers="_allUsers" _userRoles="UserRoles" />
}

@code {
    // Property used to add or edit the currently selected user
    ApplicationUser objUser = new ApplicationUser();

    // Roles to display in the roles dropdown when adding a role to the user
    List<IdentityRole> AllRoles = new List<IdentityRole>();
    
    // Tracks the selected role for the current user
    string SelectedUserRole { get; set; }
    
    // To enable showing the Popup
    bool ShowPopup = false;

    // To enable showing the User Roles Popup
    bool ShowUserRolesPopup = false;

    ......
    
}
 

Родительский компонент содержит объекты , которые содержат объекты данных (состояние), например objUser SelectedUserRole и т.д., и методы, которые используют эти объекты для реализации бизнес-правил, например SaveUser и. AddRoleToUser

Дочерний компонент EditUserModal используется для добавления/редактирования пользователей. Он принимает в objUser качестве параметра родительский компонент. «objUser» содержит значение нового/выбранного пользователя.

 <div class="form-group">
    <input class="form-control" type="text" placeholder="First Name" @bind="objUser.FirstName" />
</div>
<div class="form-group">
    <input class="form-control" type="text" placeholder="Last Name" @bind="objUser.LastName" />
</div>
<div class="form-group">
    <input class="form-control" type="text" placeholder="Email" @bind="objUser.Email" />
</div>
<button class="btn btn-primary" @onclick="SaveUser">Save</button>

@code {

    [Parameter]
    public ApplicationUser objUser { get; set; }
    
    [Parameter]
    public EventCallback SaveUser { get; set; }
    .......

}
 

Обратный вызов objUser объекта и SaveUser события являются параметрами в дочернем компоненте. После ввода данных в виде дочернего компонента и их сохранения SaveUser вызывается родительский компонент, и привязка данных автоматически выполняется с objUser объектом в родительском компоненте. Этот объект содержит введенные в данный момент данные. Этот объект затем используется для вызова служб баз данных для создания/обновления пользовательских данных. Он работает идеально!

Вот как это выглядит:

введите описание изображения здесь

objUser Объект содержит введенные данные, и все это работает нормально.

введите описание изображения здесь

Но проблема заключается в компоненте EditUserRolesModal, где я делаю нечто подобное с переменной. Код компонента EditUserRolesModal:

 <h5 class="my-3">Add Role</h5>
  <div class="row">
     <div class="col-md-10">
         <div class="form-group">
             <input class="form-control" type="text" @bind="objUser.Email" disabled />
         </div>
         <div class="form-group">
             <select class="form-control" @bind="@SelectedUserRole">
                  @foreach (var option in AllRoles)
                  {
                     <option value="@option.Name">@option.Name</option>
                  }
             </select>
         </div>
      <button class="btn btn-primary" @onclick="AddRoleToUser">Assign</button>
    </div>
</div>
    
@code {    
    [Parameter]
    public ApplicationUser objUser { get; set; }

    [Parameter]
    public string SelectedUserRole { get; set; }
    
    [Parameter]
    public List<IdentityRole> AllRoles { get; set; }
    
    [Parameter]
    public EventCallback AddRoleToUser { get; set; }
    
    ........
    
}
 

Здесь параметр SelectedUserRole используется для хранения значения роли, которая должна быть назначена пользователю. Он привязан к выпадающему списку. Когда я изменяю значение раскрывающегося списка, отладка показывает, что значение обновляется в объекте дочернего компонента (параметра). На следующих изображениях это показано:

введите описание изображения здесь

Значение устанавливается для объекта SelectedUserRole параметра в дочернем компоненте.

введите описание изображения здесь

Во многом похоже на то, objUser что используется EditUserModal для хранения значений полей FirstName LastName и Email нового/выбранного пользователя , SelectedUserRole используется EditUserRolesModal для хранения значения выбранной роли, которая должна быть назначена пользователю.

Но когда нажата кнопка Назначить и AddRoleToUser событие вызывается в родительском компоненте через дочерний компонент, SelectedUserRole переменная содержит значение null в родительском компоненте.

введите описание изображения здесь

Сценарий в обоих дочерних компонентах одинаков, где цель состоит в том, чтобы при изменении значения параметров внутри дочернего компонента связанные значения должны быть обновлены и отражены в состоянии родителя. Однако он работает в EditUserModal компоненте, а не в EditUserRolesModal компоненте. Почему? Что я делаю не так?

P.S. У меня нет опыта в Blazor или компонентном веб-программировании.

Изменить: Я создал общедоступный репозиторий для этого проекта. Он также содержит файл сценария базы данных. Его можно найти здесь.

Комментарии:

1. Я не очень внимательно проанализировал ваш код, но.. Почему бы вам не поместить свой ApplicationUser логический код (CRUD, роли обновления и т. Д.) В одно место — объект службы данных. Вы можете получить доступ к этой единственной копии истины, хотя инъекция зависимости от всех ваших компонентов. Если вы хотите продолжить, я опубликую в качестве ответа пример кода.

2. @MrCakaShaunCurtis спасибо. Я могу это сделать. Нет необходимости публиковать пример кода для этого. А пока мне нужно знать ответ на свой вопрос.

3. Я добавил комментарий выше в качестве ответа. Если это решит проблему, отметьте это как ответ, чтобы закрыть вопрос.

Ответ №1:

Это и есть виновник:

 <button class="btn btn-primary" @onclick="AddRoleToUser">Assign</button>
 

Когда нажата кнопка «Назначить», вы должны вызвать AddRoleToUser обратный вызов события, передав SelectedUserRole значение AddRoleToUser методу.

Вот полный фрагмент кода, как это сделать:

 [Parameter]
public EventCallback<string> AddRoleToUser { get; set; }
 

Примечание: это EventCallback<string> не EventCallback

Делать…

 <button type='button" class="btn btn-primary" 
    @onclick="OnAddRoleToUser">Assign</button>
 

И

   private async Task OnAddRoleToUser()
  {
     // skipped checking for brevity...
    
    await AddRoleToUser.InvokeAsync(SelectedUserRole);
    
  }
 

Обратите внимание, что OnAddRoleToUser метод вызывает AddRoleToUser «делегат», передавая ему SelectedUserRole

И вот как вы определяете метод, инкапсулирующий «делегат»

 private async Task AddRoleToUser(string SelectedUserRole)
{

}
 

Примечание: Вы должны добавить атрибут type='button" в <button type='button".../> ,
если не ваша кнопка получает значение по умолчанию type='submit"

Важный:

SignInManager<TUser> и UserManager<TUser> не поддерживаются в компонентах Razor.

См. Источник…. Вместо этого вам следует создавать службы для необходимых заданий…

Обновить:

Но я хотел бы знать, как упоминалось в моих вопросах, почему он работает с objUser в первом дочернем компоненте EditUserModal? Основываясь на вашем ответе, мне пришлось бы отправить объект objUser делегату saveUser, но он все равно работает с методом.

Теперь это works … Кажется, это работает так же UserManager<TUser> , как, по-видимому, работает использование в компоненте.

Проблема с вашим кодом заключается в том, что вы изменяете состояние параметра objUser, привязывая его к входному текстовому элементу, например:

 <input class="form-control" type="text" placeholder="First Name" @bind="objUser.FirstName" /> 
 

Эта привязка фактически изменяет состояние параметра, но вы не должны изменять состояние параметра. Только компонент, создавший параметр, родительский компонент, должен изменять параметр. Другими словами, вы должны рассматривать параметр как автоматическое свойство…Не меняйте его значение. Теперь, прежде чем вы начнете волноваться, прочтите это:

Хорошо, я посмотрел более подробно и вижу, что происходит. Ниже приведено объяснение, но прежде чем перейти к этому, я повторю свое утверждение о том, что основная проблема заключается в том, что дочерний компонент перезаписывает свое собственное значение свойства [Параметр]. Избегая этого, вы можете избежать этой проблемы.

В этом случае, как и в других, Blazor полагается на то, что назначение параметров от родителя к потомку безопасно (не перезаписывает информацию, которую вы хотите сохранить) и не имеет побочных эффектов, таких как запуск большего количества визуализаций (потому что тогда вы можете оказаться в бесконечном цикле). Вероятно, нам следует усилить руководство в документах, чтобы не просто сказать «не пишите в свои собственные параметры», а расширить его, чтобы предостеречь от побочных эффектов в таких установщиках свойств. Использование Blazor свойств C# для представления канала связи между родительскими и дочерними компонентами довольно удобно для разработчиков и хорошо смотрится в исходном коде, но возможность побочных эффектов проблематична. У нас уже есть анализатор времени компиляции, который предупреждает, если у вас нет правильных модификаторов доступности для свойств [параметра]-возможно, нам следует расширить его, чтобы выдавать предупреждения/ошибки, если параметр является чем — то иным, чем простое свойство { get; set; } auto.

Стивсандерсонмс

Примечание: Я бы посоветовал вам прочитать весь выпуск целиком, так как это одна из самых важных вещей в Blazor, которую большинство разработчиков не в состоянии переварить и реализовать.

Вот простой фрагмент кода, описывающий, как создать двустороннюю привязку данных между родительским компонентом и его дочерним, как передавать параметры и как использовать переданные параметры.

Два дня привязки.бритва

 <div>InternalValue: @InternalValue</div>

<div class="form-group">
    <input type="text" class="form-control" 
           @bind="InternalValue" 
           @bind:event="oninput" 
     />
</div>

@code {
    
    private string InternalValue
    {
        get => Value;
        set 
        {
            if (value != Value)
            {
                ValueChanged.InvokeAsync(value);        
            }
        }
    }

    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }
      
}
 

Индекс.бритва

 @page "/"

<TwoWayDataBinding @bind-Value="Value"></TwoWayDataBinding>

<p>amp;nbsp;</p>
<div>
    <input type="text" @bind="Value" @bind:event="oninput"/>
</div>
@code
{
    private string Value { get; set; } = "Default value...";
  
}  
 

Как вы можете видеть, я не изменяю параметр Value, переданный дочернему компоненту. Вместо этого я определяю локальную переменную, которую возвращаю родительскому компоненту через обратный вызов события.Метод InvokeAsync.

Следуйте этому шаблону с компонентом EditUserModal…Если у вас не получится, дайте мне знать.

Излишне говорить, что любые изменения, внесенные вами в параметр objUser в компоненте EditUserModal, немедленно отражаются в objUser переменной, определенной в компоненте ManageUsers, поскольку оба указывают на одно и то же место в памяти, и, таким образом, при нажатии на Save кнопку SaveUser вызывается метод в компоненте ManageUsers, и все кажется хорошо и …

Еще раз:

 `SignInManager<TUser>` and `UserManager<TUser>` aren't supported in 
 Razor components.
 

Вы же не хотите обнаружить это при развертывании своего приложения, верно?
Пользуйтесь услугами…

Комментарии:

1. спасибо за ваш ответ. Но я хотел бы знать, как упоминалось в моих вопросах, почему он работает с objUser первым дочерним компонентом EditUserModal ? Основываясь на вашем ответе, мне пришлось бы отправить objUser объект для SaveUser делегирования, но он все равно работает с методом. Вот что меня смущает..

2. Это не должно сработать…Пожалуйста, опубликуйте полный код дочернего и родительского компонентов. Но прежде чем вы это сделаете, добавьте тип=»кнопка» в <класс кнопки=»btn btn-основной» @onclick=»Пользователь сохранения»>Сохраните<класс кнопки=»btn btn-основной» @onclick=»Пользователь сохранения»></кнопка> и посмотрите, работает ли он по-прежнему, как вы сказали.

3. вы можете использовать это приложение самостоятельно. Я создал общедоступный репозиторий с файлом сценария SQL. Пожалуйста, проверьте это здесь: github.com/jaslam94/BlazorRolesMgmt

Ответ №2:

Я не очень внимательно проанализировал ваш код, но.. Почему бы вам не поместить логический код пользователя приложения (CRUD, роли обновления и т. Д.) В одно место — объект службы данных. Вы можете получить доступ к этой единственной копии истины с помощью внедрения зависимостей из всех ваших компонентов.

Обновить

Чтобы ответить на ваш прямой вопрос, зачем objUser обновляться, но нет SelectedUserRole ? Очень простым ObjUser является объект и передается по ссылке. Любые моды, которые вы objUser создаете, будут сделаны на оригинале в родительской версии. С другой стороны SelectedUserRole , это строка, и хотя это объект, он немного похож на дом на полпути, и с ним обращаются как с примитивным типом. При SetParametersAsync запуске в дочернем компоненте дочернее свойство для objUser устанавливается в ссылку, в то время как дочернее свойство для SelectedUserRole устанавливается в копию переданной строки — фактически по значению.

Вот почему я предложил перейти на модель, основанную на услугах, у вас нет подобных проблем.

Комментарии:

1. спасибо, но мой вопрос сосредоточен на том, почему он работает с модальным 1, а не с модальным 2?

2. Смотрите мое обновление выше