SignalR KnockoutJS和AzureTodo OnChangeEvent

时间:2016-11-17 08:36:49

标签: azure knockout.js signalr

我希望在其他地方为我的表插入待办事项时触发CollectionChanged。

我有一个mobileapp,我可以通过TodoItemManager插入项目。 我有一个列出项目的网站。

现在我想不刷新网站,应该添加项目。

一个选项是拥有一个setInterval,但我不喜欢这个选项。

<div class="products">
<div class="row" data-bind="template: { name: 'productTemplate', foreach:     products }">
</div>
<span class="messageClass" style="color: red;"></span>
</div>
<script type="text/html" id="productTemplate">
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<div class="caption">
<h3 data-bind="text: name"></h3>
</div>
</div>
</div>
</script>
<script>
$(function () {
//debugger;
function productViewModel(id, name) {
this.productId = id;
this.name = ko.observable(name);
var self = this;
}

function productListViewModel() {
//debugger;
this.hub = $.connection.myHub;
this.products = ko.observableArray([]);

var products = this.products;

this.init = function () {
this.hub.server.getAllProducts();
}

this.hub.client.getAllProducts = function (allProducts) {
//debugger;
var mappedProducts = $.map(allProducts, function (item) {
//debugger;
return new productViewModel(item.ProductId, item.Name)
});

products(mappedProducts);
}
}

var vm = new productListViewModel();
ko.applyBindings(vm);

$.connection.hub.start(function () {
vm.init();
}).done(function () {

});;

});
</script>

另一种选择是从应用程序触发我的Hub中的GetAllProducts方法,以便触发

await Clients.All.getAllProducts(vm.Products.ToArray());

但我不喜欢这种做法。

我想知道如何在不刷新浏览器的情况下将项目添加到azure表时更新我的​​网页上列出的数据。

这是我的中心

public class MyHub : Hub
{
    private ObservableCollection<TodoItem> persons;
    public MyHub()
    {

    }


    public async Task GetAllProducts()
    {
        var data = await GetLogs();
        VoteViewModel vm = new VoteViewModel();
        vm.Products = data.Select(m => new Products() { Name = m.Name }).ToList();
        //vm.Products = new List<Products>() { new Products() { Name = "Sample", ProductId = 1 } };
        await Clients.All.getAllProducts(vm.Products.ToArray());
    }

    private async Task<List<TodoItem>> GetLogs()
    {
        persons = await TodoItemManager.DefaultManager.GetTodoItemsAsync();
        persons.CollectionChanged += this.OnCollectionChanged;
        return persons.ToList();
    }

    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
    {

    }

    class VoteViewModel
    {
        public List<Products> Products { get; set; }
    }
    public class Products
    {
        public int ProductId { get; set; }
        public string Name { get; set; }
    }
}

看看TodoItemManager:

public partial class TodoItemManager
{
    static TodoItemManager defaultInstance = new TodoItemManager();
    MobileServiceClient client;

#if OFFLINE_SYNC_ENABLED
    IMobileServiceSyncTable<TodoItem> todoTable;
#else
    IMobileServiceTable<TodoItem> todoTable;
#endif

    const string offlineDbPath = @"localstore.db";

    private TodoItemManager()
    {
        this.client = new MobileServiceClient(AzureSettings.ApplicationURL);

#if OFFLINE_SYNC_ENABLED
        var store = new MobileServiceSQLiteStore(offlineDbPath);
        store.DefineTable<TodoItem>();

        //Initializes the SyncContext using the default IMobileServiceSyncHandler.
        this.client.SyncContext.InitializeAsync(store);

        this.todoTable = client.GetSyncTable<TodoItem>();
#else
        this.todoTable = client.GetTable<TodoItem>();
#endif
    }

    public static TodoItemManager DefaultManager
    {
        get
        {
            return defaultInstance;
        }
        private set
        {
            defaultInstance = value;
        }
    }

    public MobileServiceClient CurrentClient
    {
        get { return client; }
    }

    public bool IsOfflineEnabled
    {
        get { return todoTable is Microsoft.WindowsAzure.MobileServices.Sync.IMobileServiceSyncTable<TodoItem>; }
    }


    public async Task<ObservableCollection<TodoItem>> GetTodoItemsAsync(bool syncItems = false)
    {
        try
        {
#if OFFLINE_SYNC_ENABLED
            if (syncItems)
            {
                await this.SyncAsync();
            }
#endif
            IEnumerable<TodoItem> items = await todoTable
                .Where(todoItem => !todoItem.Done)
                .ToEnumerableAsync();

            return new ObservableCollection<TodoItem>(items);
        }
        catch (MobileServiceInvalidOperationException msioe)
        {
            Debug.WriteLine(@"Invalid sync operation: {0}", msioe.Message);
        }
        catch (Exception e)
        {
            Debug.WriteLine(@"Sync error: {0}", e.Message);
        }
        return null;
    }

    public async Task SaveTaskAsync(TodoItem item)
    {
        if (item.Id == null)
        {
            await todoTable.InsertAsync(item);
        }
        else
        {
            await todoTable.UpdateAsync(item);
        }
    }

#if OFFLINE_SYNC_ENABLED
    public async Task SyncAsync()
    {
        ReadOnlyCollection<MobileServiceTableOperationError> syncErrors = null;

        try
        {
            await this.client.SyncContext.PushAsync();

            await this.todoTable.PullAsync(
                //The first parameter is a query name that is used internally by the client SDK to implement incremental sync.
                //Use a different query name for each unique query in your program
                "allTodoItems",
                this.todoTable.CreateQuery());
        }
        catch (MobileServicePushFailedException exc)
        {
            if (exc.PushResult != null)
            {
                syncErrors = exc.PushResult.Errors;
            }
        }

        // Simple error/conflict handling. A real application would handle the various errors like network conditions,
        // server conflicts and others via the IMobileServiceSyncHandler.
        if (syncErrors != null)
        {
            foreach (var error in syncErrors)
            {
                if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
                {
                    //Update failed, reverting to server's copy.
                    await error.CancelAndUpdateItemAsync(error.Result);
                }
                else
                {
                    // Discard local change.
                    await error.CancelAndDiscardItemAsync();
                }

                Debug.WriteLine(@"Error executing sync operation. Item: {0} ({1}). Operation discarded.", error.TableName, error.Item["id"]);
            }
        }
    }
#endif
}

你可以在我的集线器中尝试使用

this.OnCollectionChanged

但只有在

时才会改变
private ObservableCollection<TodoItem> persons;

更改,这不会发生,所以我需要调用

GetTodoItemsAsync

再次获取数据。

如果在不刷新整个网站的情况下将新项目添加到azure表中,如何在客户端上更新我的列表?

希望你明白我的目标。 BR

1 个答案:

答案 0 :(得分:0)

另一种选择是在服务器而不是客户端设置间隔。

像:

public class MyHub : Hub
{
    private ObservableCollection<TodoItem> persons;
    public MyHub()
    {
        Intervalla();
    }

    public async Task Intervalla()
    {
        EasyTimer.SetInterval(async () =>
        {
            var data = await GetLogs();
            VoteViewModel vm = new VoteViewModel();
            vm.Products = data.Select(m => new Products() { Name = m.Name }).ToList();
            //vm.Products = new List<Products>() { new Products() { Name = "Sample", ProductId = 1 } };
            await Clients.All.getAllProducts(vm.Products.ToArray());

        }, 1000);
    }

这可能是智能/呃选项吗?