Как сохранить экземпляры компонентов в Blazor

#c# #asp.net-core #blazor

Вопрос:

У меня есть аналогичный компонент, и я хочу сохранить экземпляр InnerComponent при первом его отображении и каждый раз отображать один и тот же экземпляр без его восстановления.

 @if(isVisible)
{
    <InnerComponent @ref="@_InnerComponent" @key="@("InnerComponentKey")">
        @ChildContent
    </InnerComponent>
}

@code{
    [Parameter] public InnerComponent _InnerComponent { get; set; }
    private bool IsVisible { get; set; }
}
 

Когда внутренний компонент виден, пользователь может управлять его состоянием.
Но если IsVisible будет установлено значение false, а затем снова true, внутренний компонент будет переопределен _InnerComponent повторно, и, таким образом, мы потеряем отслеживание изменений, внесенных пользователем в этот InnerComponent экземпляр.

Добавление @key, похоже, также не помогает сохранить экземпляр. Он просто перезаписывается и перезаписывается :/ Я уверен, что я передаю ему один и тот же ключ оба раза, когда он отображается, но я не знаю, как проверить ключи, с которыми он сравнивается.

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

 @if(isVisible)
{
    @if(_InnerComponent == null)
    {
        <InnerComponent @ref="@_InnerComponent" @key="@("InnerComponentKey")">
            @ChildContent
        </InnerComponent>
    }
    else
    {
        @_InnerComponent.Render()
    }
}
 

Я принимаю критику по своему вопросу, так как я не задавал много вопросов 🙂

Заранее спасибо!

Упрощенный пример:

Допустим , у нас есть следующий компонент, который я собираюсь назвать «Контрконтейнер», где «<Счетчик> » — это компонент счетчика из шаблона проекта Blazor по умолчанию.

 @if(CounterIsVisible)
{
    <Counter @ref="@_Counter" @key="@("CounterKey")" />
}

<button @onclick="() => CounterIsVisible = !CounterIsVisible">
    Show/Hide counter 
</button>

@code{
    [Parameter] public Counter _Counter { get; set; }
    private bool CounterIsVisible { get; set; } = true;
}
 

Я хочу сохранить _Counter экземпляр, чтобы увидеть правильный _Counter.currentCount , на который я рассчитывал. Я мог бы сохранить его, используя метод из этой статьи, но я нахожу их все непрактичными, потому что

  • компонент, который я создаю, содержит гораздо больше данных, чем просто одна переменная
  • Мне нужны данные только до тех пор, пока CounterContainer они существуют, и только для визуализации
  • Это просто слишком сложно для моего варианта использования

У меня уже есть Counter ссылка, сохраненная. Я просто хочу просмотреть его вместо того, чтобы восстанавливать и перезаписывать все это.

Надеюсь, это немного прояснило ситуацию (:

Ответ №1:

Я удивлен, что он компилируется, так как вы неправильно написали свой класс компонентов:

 [Parameter] public **InnerCopmonent** _InnerComponent { get; set; }
 

Почему у вас вообще есть @ref для вашего внутреннего компонента? Что делает внутренний компонент и почему вы хотите ссылаться на него? Можете ли вы поделиться кодом этого компонента?

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

1. Привет @Bennyboy1973, спасибо за ответ 🙂 Я не вставлял никакого реального кода, поэтому я предполагаю, что этот точный пример (теперь отредактированный) не будет компилироваться.

2. Я добавил @ref , хотя это не на 100% относится к вопросу, потому что мне это нужно в моем коде, и я заметил, что @key и @ref иногда странно взаимодействую. Я чувствую, что это не добавляет много шума, но кто-то опытный мог бы заметить, если есть проблемы с использованием обоих.

3. Компонент просто слишком длинный, чтобы вставить его в этот вопрос, но я просто отредактировал дополнительный пример. Надеюсь, это несколько прояснит мой вопрос 🙂

Ответ №2:

  • лучший вариант: отдельное представление и данные. Состояние, которое вы хотите сохранить, должно храниться не внутри компонента, а в отдельном объекте (ViewModel).
  • краткое решение: всегда визуализируйте компонент. Используйте @isVisible для включения/выключения css-класса, чтобы скрыть его.

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

1. Мне нравится твой первый вариант. Второй вариант кажется своего рода анти-Блейзором, и мне кажется, что его худший грех в том, что он сработает, и операция может просто сделать это с этого момента.

2. Да, первый вариант хорош, и у меня уже есть отдельный вид и модель данных. Просто информация, которую я пытаюсь сохранить, относится к модели представления ( Counter компоненту), а не к модели данных. Я не думал об использовании ccs, но я не знаю, то ли это решение, которое я ищу. Тем не менее, спасибо за предложения!

Ответ №3:

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

CounterData.cs

 public class CounterData
    {
        public int counter { get; set; }
        public string Title { get; set; } = "Counter";
        public List<int> CounterHistory { get; set; } = new List<int>();
    }
 

Контрконтейнер.бритва

 @page "/counter"

<h3>Counter Container</h3>

<button @onclick="()=> ShowCounter=!ShowCounter">Toggle</button>

@if (ShowCounter)
{
    <Counter CarryingInstance="PersistentData" />
}

@code {
    CounterData PersistentData = new CounterData();
    bool ShowCounter = false;
}
 

Счетчик.бритва

 <h3>@CarryingInstance.Title</h3>

<input @bind="CarryingInstance.Title" /><br />
<button @onclick="()=> Increment(1)">Add</button>
<button @onclick="()=>Increment(-1)">Sub</button>
@CarryingInstance.counter <br />


History:

@foreach (var item in CarryingInstance.CounterHistory)
{
    <span>amp;nbsp; @item</span>
}

@code {
    [Parameter]
    public CounterData CarryingInstance { get; set; }

    void Increment (int amount)
    {
        CarryingInstance.counter  = amount;
        CarryingInstance.CounterHistory.Add(CarryingInstance.counter);
    }
}
 

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

1. Спасибо за подробный ответ! У меня действительно есть класс , похожий на CounterData , но информация, которую я пытаюсь сохранить, на самом деле принадлежит не CounterData объекту, а скорее Counter компоненту (это просто визуальные данные с той же областью действия, что и «ConterContainer»).

Ответ №4:

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

Другой способ, аналогичный методу экземпляра BennyBoys, заключается в том, что вы, вероятно, могли бы сделать это, используя внедрение зависимостей и зарегистрировав тип в качестве службы с областью действия или одноэлементной службы и внедрив его в компонент.

Вы можете прочитать здесь, в блоге Криса Сейнти, о том, как работают различные регистрации в blazor, и их поведении, и если это будет соответствовать вашим потребностям.

https://chrissainty.com/service-lifetimes-in-blazor/

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

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

1. Да, но я определенно не хочу регистрировать эти данные в сервисе. Это просто слишком сложно для моего варианта использования :/ Спасибо за предложение, хотя!