Обновить свойство асинхронно - PullRequest
0 голосов
/ 18 ноября 2018

Когда свойство моего ViewModel обновляется, другое свойство обновляется асинхронно.

Todo.cshtml:

@page "/todo"

<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>

<ul>
    @foreach (var todo in todos)
    {
        <li>
            <input type="checkbox" bind="@todo.IsDone" />
            <input bind="@todo.Title" />
        </li>
    }
</ul>

<input placeholder="Something todo" bind="@newTodo"/>
<button onclick="@AddTodo">Add todo</button>

@functions {
    IList<TodoItem> todos = new List<TodoItem>();


    string newTodo;
    void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo });
            newTodo = string.Empty;
        }
    }
}

TodoItem.cs:

public class TodoItem
{
    private bool _isDone;

    public string Title { get; set; }
    public bool IsDone
    {
        get => _isDone;
        set
        {
            _isDone = value;
            Task.Run(() =>
            {
                //Simulate work
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
                //Update property
                Title = Title + " - Done";
            });
        }
    }
}

В синхронном (без Task.Run) это работает нормально, но в асинхронном пользовательский интерфейс не обновляется.

Мне нужно объяснить интерфейс для обновления с помощью StateHasChanged(): https://github.com/aspnet/Blazor/issues/1413

Но я не могу вызвать этот метод в TodoItem (и я не хочу, чтобы TodoItem знал компонент Blazor).

У вас есть решение для обновления пользовательского интерфейса?

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Простой ответ: "просто выстрелить StateHasChanged(); после изменения вашей переменной" :

        Task.Run(() =>
        {
            //Simulate work
            System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
            //Update property
            Title = Title + " - Done";
            StateHasChanged();
        });

Поскольку ваш метод асинхронный, переписать как:

        Task.Run(async () =>  //<--here async
        {
            //Simulate async work
            Task.Run( async () => {
                await Task.Run( () => {} ); //<--- await
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
            });
            //Update property
            Title = Title + " - Done";
            StateHasChanged();
        });

Чтобы избежать анти-паттернов и писать чистый код, ваш ModelView может иметь публичное событие, сообщающее UI, что оно изменилось, просто подключите это событие в UI к StateHasChanged();.

Я пишу здесь пример счетчика Блазора, модифицированный для этого:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" onclick="@IncrementCount">
    Click me @s <!-- here var -->
</button>

@functions {
    int currentCount = 0;

    string s = "";

    void IncrementCount()
    {
        currentCount++;
        Task.Run(() =>
            {
                //Simulate work
                Task.Run( async () => {
                    await Task.Run( () => {} );
                    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));}
                        );
                //Update property
                s = s + " - Done";
                StateHasChanged();
            });
        }
    }
}

Отредактировано

Вызов StateHasChanged из потока больше не поддерживается. Просто измените:

    Task.Run(() =>
    {
        //Simulate work
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
        //Update property
        Title = Title + " - Done";
        StateHasChanged();
    });

К

    Invoke( () =>
    {
       ...
0 голосов
/ 19 ноября 2018

Вы должны сделать следующее:

  1. Определить делегат действия в вашем классе:

    public event Action OnChange;
    
  2. В этом классе определитьметод NotifyStateChanged() выглядит следующим образом:

    private void NotifyStateChanged() => OnChange?.Invoke();
    

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

  3. В компоненте todo добавьте метод StateHasChanged к делегату события, используемому в вашем классе TodoItem, таким образом:

    @functions
    {
        protected override void OnInit()
        {
            state.OnChange += StateHasChanged;
        }
    }
    

Надеюсь, это поможет.Если да, пожалуйста, не принимайте это как ответ

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...