Data Binding in Blazor Kanban Component

15 Dec 202224 minutes to read

The Kanban uses SfDataManager, which supports both RESTFUL JSON data service binding and IEnumerable binding. The DataSource property of Kanban can be assigned either with the instance of DataManager or a list of DataSource collection.

It supports the following types of data binding:

  • Local data
  • Remote data

NOTE

When using DataSource as IEnumerable<T>, component type(TValue) will be inferred from its value. When using SfDataManager for data binding, the TValue must be provided explicitly in the Blazor Kanban component.

Local data

In list binding, you can assign an IEnumerable object to the DataSource property. The list data source can also be provided as an instance of the SfDataManager or by using the SfDataManager component.

@using Syncfusion.Blazor.Kanban

<SfKanban TValue="TasksModel" KeyField="Status" DataSource="@Tasks">
    <KanbanColumns>
        <KanbanColumn HeaderText="To Do" KeyField="@(new List<string>() {"Open"})"></KanbanColumn>
        <KanbanColumn HeaderText="In Progress" KeyField="@(new List<string>() {"InProgress"})"></KanbanColumn>
        <KanbanColumn HeaderText="Testing" KeyField="@(new List<string>() {"Testing"})"></KanbanColumn>
        <KanbanColumn HeaderText="Done" KeyField="@(new List<string>() {"Close"})"></KanbanColumn>
    </KanbanColumns>
    <KanbanCardSettings HeaderField="Id" ContentField="Summary"></KanbanCardSettings>
</SfKanban>

@code {
    public class TasksModel
    {
        public int Id { get; set; }
        public string Status { get; set; }
        public string Summary { get; set; }
    }
    public List<TasksModel> Tasks { get; set; }

    protected override void OnInitialized()
    {
        Tasks = Enumerable.Range(1, 10).Select(x => new TasksModel()
        {
            Id = 1000 + x,
            Status = (new string[] { "Open", "InProgress", "Testing", "Close" })[new Random().Next(4)],
            Summary = (new string[] { "Analyze SQL server 2008 connection.", "Fix the issues reported in Safari browser.", "Improve application performance", "Analyze grid control." })[new Random().Next(4)],
        }).ToList();
    }
}

NOTE

By default, SfDataManager uses BlazorAdaptor for list data-binding.

ExpandoObject binding

Kanban is a generic component that is strongly bound to a model type. In some cases, the model type may be unknown during compile time. In such cases, you can bind data to the Kanban as a list of ExpandoObject.

ExpandoObject can be bound to Kanban by assigning to the DataSource property. Kanban can also perform all kinds of supported data operations and editing in ExpandoObject.

To get start quickly with Blazor Kanban component using ExpandoObject and DynamicObject binding, you can check on this video.

@using Syncfusion.Blazor.Kanban
@using System.Dynamic

<div class="col-lg-12 control-section">
    <SfKanban KeyField="Status" DataSource="@Tasks">
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
<KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField" AllowAdding="true"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="Id" ContentField="Summary"></KanbanCardSettings>
    </SfKanban>
</div>

@code{ 
    public List<ExpandoObject> Tasks { get; set; } = new List<ExpandoObject>();
    private List<ColumnModel> columnData = new List<ColumnModel>() {
        new ColumnModel(){ HeaderText= "To Do", KeyField= new List<string>() { "Open" } },
        new ColumnModel(){ HeaderText= "In Progress", KeyField= new List<string>() { "In Progress" } },
        new ColumnModel(){ HeaderText= "Testing", KeyField= new List<string>() { "Testing" } },
        new ColumnModel(){ HeaderText= "Done", KeyField=new List<string>() { "Close" } }
    };
    protected override void OnInitialized()
    {
        Tasks = Enumerable.Range(1, 20).Select((x) =>
        {
            dynamic d = new ExpandoObject();
            d.Id = "Task 1000" + x;
            d.Status = (new string[] { "Open", "In Progress", "Testing", "Close" })[new Random().Next(4)];
            d.Summary = (new string[] { "Analyze the new requirements gathered from the customer.", "Improve application performance", "Fix the issues reported in the IE browser.", "Validate new requirements", "Test the application in the IE browser." })[new Random().Next(5)];
            d.Assignee = (new string[] { "Nancy Davloio", "Andrew Fuller", "Janet Leverling", "Steven walker", "Margaret hamilt", "Michael Suyama", "Robert King" })[new Random().Next(7)];
            return d;
        }).Cast<ExpandoObject>().ToList<ExpandoObject>();
    } 
}

Expando object in Blazor Kanban

DynamicObject binding

DynamicObject can be bound to Kanban by assigning DynamicObject to the DataSource property. Kanban can also perform all kinds of supported data operations and editing in DynamicObject.

NOTE

The GetDynamicMemberNames method of DynamicObject class must be overridden and return the property names to perform data operations and editing while using DynamicObject.

@using Syncfusion.Blazor.Kanban
@using System.Dynamic

<div class="col-lg-12 control-section">
    <SfKanban KeyField="Status" DataSource="@Tasks">
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
<KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField" AllowAdding="true"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="Id" ContentField="Summary"></KanbanCardSettings>
    </SfKanban>
</div>

@code{ 
    private List<ColumnModel> columnData = new List<ColumnModel>() {
        new ColumnModel(){ HeaderText= "To Do", KeyField= new List<string>() { "Open" } },
        new ColumnModel(){ HeaderText= "In Progress", KeyField= new List<string>() { "In Progress" } },
        new ColumnModel(){ HeaderText= "Testing", KeyField= new List<string>() { "Testing" } },
        new ColumnModel(){ HeaderText= "Done", KeyField=new List<string>() { "Close" } }
    };
    public List<DynamicDictionary> Tasks = new List<DynamicDictionary>() { };
    protected override void OnInitialized()
    {
        Tasks = Enumerable.Range(1, 20).Select((x) =>
        {
            dynamic d = new DynamicDictionary();
            d.Id = "Task 1000" + x;
            d.Status = (new string[] { "Open", "In Progress", "Testing", "Close" })[new Random().Next(4)];
            d.Summary = (new string[] { "Analyze the new requirements gathered from the customer.", "Improve application performance", "Fix the issues reported in the IE browser.", "Validate new requirements", "Test the application in the IE browser." })[new Random().Next(5)];
            d.Assignee = (new string[] { "Nancy Davloio", "Andrew Fuller", "Janet Leverling", "Steven walker", "Margaret hamilt", "Michael Suyama", "Robert King" })[new Random().Next(7)];
            return d;
        }).Cast<DynamicDictionary>().ToList<DynamicDictionary>();
    }
    public class DynamicDictionary : System.Dynamic.DynamicObject
    {
        Dictionary<string, object> dictionary = new Dictionary<string, object>();
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            string name = binder.Name;
            return dictionary.TryGetValue(name, out result);
        }
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            dictionary[binder.Name] = value;
            return true;
        }
        //The GetDynamicMemberNames method of DynamicObject class must be overridden and return the property names to perform data operation and editing while using DynamicObject.
        public override System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames()
        {
            return this.dictionary?.Keys;
        }
    } 
}

Dynamic object in Blazor Kanban

Observable collection

This ObservableCollection(dynamic data collection) shows notifications when the items are added, removed, and moved. Implementing the INotifyCollectionChanged will notify when there is any dynamic change (add, remove, move, and clear) in the collection. Implementing the INotifyPropertyChanged will notify when the property value has been changed on the client side.

Here, the Order class implements the interface of INotifyPropertyChanged and it raises the event when the Status property value was changed.

To get start quickly with Blazor Kanban component using Observable collection, you can check on this video.

  • CSHTML
  • @using Syncfusion.Blazor.Kanban
    @using Syncfusion.Blazor.Buttons
    @using Syncfusion.Blazor.Notifications
    @using System.Collections.ObjectModel;
    @using System.ComponentModel;
    
    <div class="col-lg-12 control-section">
        <div class="content-wrapper" id="toast-kanban-observable">
            <div class="row">
                <div class="btn" style="margin: 0 0 7px 7px;">
                    <SfButton @onclick="AddRecord">Add Card</SfButton>
                    <SfButton @onclick="DeleteRecord">Delete Card</SfButton>
                    <SfButton @onclick="UpdateRecord">Update Card</SfButton>
                </div>
                <SfKanban KeyField="Status" DataSource="@ObservableData">
                    <KanbanColumns>
                        @foreach (ColumnModel item in columnData)
                        {
                            <KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField" AllowAdding="true" />}
                    </KanbanColumns>
                    <KanbanCardSettings HeaderField="Id" ContentField="Summary" />
                </SfKanban>
                <SfToast @ref="ToastObj" ID="toast_type" Content="@ToastContent" Timeout=2000 Target="@ToastTarget">
                    <ToastPosition X="Right" Y="Top" />
                </SfToast>
            </div>
        </div>
    </div>
    
    @code{ 
    SfToast ToastObj;
        private List<ColumnModel> columnData = new List<ColumnModel>() {
            new ColumnModel(){ HeaderText= "To Do", KeyField= new List<string>() { "Open" } },
            new ColumnModel(){ HeaderText= "In Progress", KeyField= new List<string>() { "In Progress" } },
            new ColumnModel(){ HeaderText= "Testing", KeyField= new List<string>() { "Testing" } },
            new ColumnModel(){ HeaderText= "Done", KeyField=new List<string>() { "Close" } }
        };
        public ObservableCollection<ObservableDatas> ObservableData { get; set; }
        private string ToastContent { get; set; }
        List<ObservableDatas> Tasks = new List<ObservableDatas>();
        private int AddUniqueId { get; set; }
        private int UpdateUniqueId { get; set; }
        private string ToastTarget { get; set; } = "#toast-kanban-observable";
        protected override void OnInitialized()
        {
            Tasks = Enumerable.Range(1, 20).Select(x => new ObservableDatas()
            {
                Id = "Task 1000" + x,
                Status = (new string[] { "Open", "In Progress", "Testing", "Close" })[new Random().Next(4)],
                Summary = (new string[] { "Analyze the new requirements gathered from the customer.", "Improve application performance", "Fix the issues reported in the IE browser.", "Validate new requirements", "Test the application in the IE browser." })[new Random().Next(5)],
                Assignee = (new string[] { "Nancy Davloio", "Andrew Fuller", "Janet Leverling", "Steven walker", "Margaret hamilt", "Michael Suyama", "Robert King" })[new Random().Next(7)],
            }).ToList();
            ObservableData = new ObservableCollection<ObservableDatas>(Tasks);
        }
        public async Task AddRecord()
        {
            var TaskId = "Task 10000" + ++AddUniqueId;
            this.ToastContent = "<b>Open</b> Column, <b>" + TaskId + "</b> Card has been added";
            await Task.Delay(100);
            ObservableData.Add(new ObservableDatas() { Id = TaskId, Status = "Open", Summary = "Improve application performance", Assignee = "Janet Leverling" });
            await this.ToastObj.ShowAsync();
        }
        public async Task DeleteRecord()
        {
            if (ObservableData.Count() != 0)
            {
                this.ToastContent = "<b>" + ObservableData.First().Status + "</b> Column, <b>" + ObservableData.First().Id + "</b> Card has been deleted";
                await Task.Delay(100);
                ObservableData.Remove(ObservableData.First());
                await this.ToastObj.ShowAsync();
            }
        }
        public async Task UpdateRecord()
        {
            if (ObservableData.Count() != 0)
            {
                var updateId = ++UpdateUniqueId;
                var data = ObservableData[updateId];
                this.ToastContent = "<b>" + data.Status + "</b> Column, <b>" + data.Id + "</b> Card has been updated";
                await Task.Delay(100);
                data.Summary = "Card Updated";
                await this.ToastObj.ShowAsync();
            }
        }
        public class ObservableDatas : INotifyPropertyChanged
        {
            public string Id { get; set; }
            private string status { get; set; }
            public string Status
            {
                get { return status; }
                set
                {
                    this.status = value;
                    NotifyPropertyChanged("Status");
                }
            }
            public string Summary { get; set; }
            public string Assignee { get; set; }
            public event PropertyChangedEventHandler PropertyChanged;
            private void NotifyPropertyChanged(string propertyName)
            {
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        } 
    }

    Observable object binding in Blazor Kanban

    Remote data

    Bind the remote data services to Kanban component by assigning service data as an instance of SfDataManager to the DataSource property or by using SfDataManager component.

    NOTE

    By default, SfDataManager uses ODataAdaptor for remote data-binding.

    TValue must be provided in the Kanban component when using SfDataManager.

    Binding with OData services

    OData is a standardized protocol for creating and consuming data. You can retrieve data from the OData service using the SfDataManager.

    @using Syncfusion.Blazor.Data
    @using Syncfusion.Blazor.Kanban
    
    <div class="col-lg-12 control-section">
    <SfKanban TValue="Order" KeyField="ShipCountry" AllowDragAndDrop="false">
        <SfDataManager Url="https://js.syncfusion.com/ejServices/Wcf/Northwind.svc/Orders" Adaptor="@Syncfusion.Blazor.Adaptors.ODataAdaptor"></SfDataManager>
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
                <KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="OrderID" ContentField="ShipName"></KanbanCardSettings>
        <KanbanEvents TValue="Order" DialogOpen="@((args)=> { args.Cancel = true; })"></KanbanEvents>
    </SfKanban>
    </div>
    
    @code {  
        public class Order
        {
            public int? OrderID { get; set; }
            public string ShipName { get; set; }
            public string ShipCountry { get; set; }
        }
        private List<ColumnModel> columnData = new List<ColumnModel>() {
            new ColumnModel(){ HeaderText= "Denmark", KeyField= new List<string>() { "Denmark" } },
            new ColumnModel(){ HeaderText= "Brazil", KeyField= new List<string>() { "Brazil" } },
            new ColumnModel(){ HeaderText= "Switzerland", KeyField= new List<string>() { "Switzerland" } },
            new ColumnModel(){ HeaderText= "Germany", KeyField=new List<string>() { "Germany" } }
        }; 
    }

    Binding with OData v4 services

    The ODataV4 is an improved version of OData protocols to retrieve and consume OData V4 services. For more details on OData V4 services, refer to the OData Documentation. To bind OData V4 service, use the ODataV4Adaptor.

    @using Syncfusion.Blazor.Data
    @using Syncfusion.Blazor.Kanban
    
    <SfKanban TValue="Order" KeyField="ShipCountry" AllowDragAndDrop="false">
        <SfDataManager Url="https://services.odata.org/V4/Northwind/Northwind.svc/Orders/" Adaptor="@Syncfusion.Blazor.Adaptors.ODataV4Adaptor"></SfDataManager>
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
                <KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="OrderID" ContentField="ShipName"></KanbanCardSettings>
        <KanbanEvents TValue="Order" DialogOpen="@((args)=> { args.Cancel = true; })"></KanbanEvents>
    </SfKanban>
    
    
    @code {  
        public class Order
        {
            public int? OrderID { get; set; }
            public string ShipName { get; set; }
            public string ShipCountry { get; set; }
        }
        private List<ColumnModel> columnData = new List<ColumnModel>() {
            new ColumnModel(){ HeaderText= "Denmark", KeyField= new List<string>() { "Denmark" } },
            new ColumnModel(){ HeaderText= "Brazil", KeyField= new List<string>() { "Brazil" } },
            new ColumnModel(){ HeaderText= "Switzerland", KeyField= new List<string>() { "Switzerland" } },
            new ColumnModel(){ HeaderText= "Germany", KeyField=new List<string>() { "Germany" } }
        }; 
    }

    Web API

    You can use WebApiAdaptor to bind Kanban with Web API created using OData endpoint.

    @using Syncfusion.Blazor.Data
    @using Syncfusion.Blazor.Kanban
    
    <SfKanban TValue="TasksModel" KeyField="Status" AllowDragAndDrop="false">
        <SfDataManager Url="https://ej2services.syncfusion.com/production/web-services/api/Kanban" Adaptor="@Syncfusion.Blazor.Adaptors.WebApiAdaptor"></SfDataManager>
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
                <KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="Id" ContentField="Summary"></KanbanCardSettings>
        <KanbanEvents TValue="TasksModel" DialogOpen="@((args) => { args.Cancel = true; })"></KanbanEvents>
    </SfKanban>
    
    @code {
        public class TasksModel
        {
            public int Id { get; set; }
            public string Status { get; set; }
            public string Assignee { get; set; }
            public string Summary { get; set; }
        }
    
        private List<ColumnModel> columnData = new List<ColumnModel>() {
            new ColumnModel(){ HeaderText= "To Do", KeyField= new List<string>() { "Open" } },
            new ColumnModel(){ HeaderText= "In Progress", KeyField= new List<string>() { "InProgress" } },
            new ColumnModel(){ HeaderText= "Testing", KeyField= new List<string>() { "Testing" } },
            new ColumnModel(){ HeaderText= "Done", KeyField=new List<string>() { "Close" } }
        };
    }

    Enable Data Manager after Initial Rendering

    It is possible to render the data source in Kanban after initial rendering. This can be achieved by conditionally enabling the SfDataManager component after Kanban rendering.

    The following sample code demonstrates enabling data manager condition in the Kanban component on button click.

    @using Syncfusion.Blazor.Buttons
    @using Syncfusion.Blazor.Data
    @using Syncfusion.Blazor.Kanban
    
    <SfButton OnClick="Enable" CssClass="e-primary" IsPrimary="true" Content="Enable data manager"></SfButton>
    <SfKanban TValue="TasksModel" KeyField="Status" AllowDragAndDrop="false">
        @if (IsInitialRender)
        {
            <SfDataManager Url="https://ej2services.syncfusion.com/production/web-services/api/Kanban" Adaptor="@Syncfusion.Blazor.Adaptors.WebApiAdaptor"></SfDataManager>
        }
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
                <KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="Id" ContentField="Summary"></KanbanCardSettings>
        <KanbanEvents TValue="TasksModel" DialogOpen="@((args) => { args.Cancel = true; })"></KanbanEvents>
    </SfKanban>
    
    @code{
        public bool IsInitialRender = false;
    
        public class TasksModel
        {
            public int Id { get; set; }
            public string Status { get; set; }
            public string Assignee { get; set; }
            public string Summary { get; set; }
        }
    
        private List<ColumnModel> columnData = new List<ColumnModel>() {
            new ColumnModel(){ HeaderText= "To Do", KeyField= new List<string>() { "Open" } },
            new ColumnModel(){ HeaderText= "In Progress", KeyField= new List<string>() { "InProgress" } },
            new ColumnModel(){ HeaderText= "Testing", KeyField= new List<string>() { "Testing" } },
            new ColumnModel(){ HeaderText= "Done", KeyField=new List<string>() { "Close" } }
        };
    
        public void Enable()
        {
            // Enabling condition to render the data manager
            this.IsInitialRender = true;
        }
    }

    Before Button click
    Dynamic data manager before button click in Blazor Kanban

    After Button click
    Dynamic data manager after button click in Blazor Kanban

    Sending additional parameters to the server

    To add a custom parameter to the data request, use the addParams method of Query class. Assign the Query object with additional parameters to the Kanban Query property.

    The following sample code demonstrates sending additional parameters using the Query property,

    @using Syncfusion.Blazor.Data
    @using Syncfusion.Blazor.Kanban
    
    <SfKanban TValue="TasksModel" KeyField="Status" AllowDragAndDrop="false" Query=@KanbanQuery>
        <SfDataManager Url="https://ej2services.syncfusion.com/production/web-services/api/Kanban" Adaptor="@Syncfusion.Blazor.Adaptors.WebApiAdaptor"></SfDataManager>
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
                <KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="Id" ContentField="Summary"></KanbanCardSettings>
        <KanbanEvents TValue="TasksModel" DialogOpen="@((args) => { args.Cancel = true; })"></KanbanEvents>
    </SfKanban>
    
    @code{
        public string ParamValue = "true";
        public Query KanbanQuery { get; set; }
        
        protected override void OnInitialized()
        {
            KanbanQuery = new Query().AddParams("BlazorKanban", ParamValue);
        }
    
        public class TasksModel
        {
            public int Id { get; set; }
            public string Status { get; set; }
            public string Assignee { get; set; }
            public string Summary { get; set; }
        }
    
        private List<ColumnModel> columnData = new List<ColumnModel>() {
            new ColumnModel(){ HeaderText= "To Do", KeyField= new List<string>() { "Open" } },
            new ColumnModel(){ HeaderText= "In Progress", KeyField= new List<string>() { "InProgress" } },
            new ColumnModel(){ HeaderText= "Testing", KeyField= new List<string>() { "Testing" } },
            new ColumnModel(){ HeaderText= "Done", KeyField=new List<string>() { "Close" } }
        };
    }

    Change Query parameter value dynamically

    It is possible to dynamically modify Kanban Query property value.

    @using Syncfusion.Blazor.Data
    @using Syncfusion.Blazor.Kanban
    @using Syncfusion.Blazor.Buttons
    
    <SfButton Content="Modify query data" OnClick="BtnClick"></SfButton>
    <SfKanban TValue="Order" @ref="KanbanObj" KeyField="ShipCountry" AllowDragAndDrop="false" Query=@QueryData>
        <SfDataManager Url="https://services.odata.org/V4/Northwind/Northwind.svc/Orders/" Adaptor="@Syncfusion.Blazor.Adaptors.ODataV4Adaptor"></SfDataManager>
        <KanbanColumns>
            @foreach (ColumnModel item in columnData)
            {
                <KanbanColumn HeaderText="@item.HeaderText" KeyField="@item.KeyField"></KanbanColumn>}
        </KanbanColumns>
        <KanbanCardSettings HeaderField="OrderID" ContentField="ShipName"></KanbanCardSettings>
        <KanbanEvents TValue="Order" DialogOpen="@((args) => { args.Cancel = true; })"></KanbanEvents>
    </SfKanban>
    
    @code {
        public SfKanban<Order> KanbanObj;
        private Query QueryData = new Query().Where("CustomerID", "equal", "VINET");
        private Query UpdatedQueryData = new Query().Where("CustomerID", "equal", "HANAR");
    
        public class Order
        {
            public int? OrderID { get; set; }
            public string CustomerID { get; set; }
            public string ShipName { get; set; }
            public string ShipCountry { get; set; }
        }
        private List<ColumnModel> columnData = new List<ColumnModel>() {
            new ColumnModel(){ HeaderText= "Denmark", KeyField= new List<string>() { "Denmark" } },
            new ColumnModel(){ HeaderText= "Brazil", KeyField= new List<string>() { "Brazil" } },
            new ColumnModel(){ HeaderText= "Switzerland", KeyField= new List<string>() { "Switzerland" } },
            new ColumnModel(){ HeaderText= "Germany", KeyField=new List<string>() { "Germany" } }
        };
        public void BtnClick()
        {
            QueryData = UpdatedQueryData;
        }
    }

    Before button Click
    Dynamic query before button click in Blazor Kanban

    After button Click
    Dynamic query after button click in Blazor Kanban

    Using Custom Adaptor

    It is possible to create your own CustomAdaptor by extending the built-in available adaptors.

    The following example demonstrates the custom adaptor usage and how to bind the data with custom service and the CRUD operations for custom bound data are performed using the methods of DataAdaptor abstract class.

    @using Syncfusion.Blazor.Data
    @using Syncfusion.Blazor
    @using Syncfusion.Blazor.Kanban
    @using System.Collections
    
    @inject OrderService _craftService;
    <SfKanban ID="Kanban" TValue="Order" KeyField="ShipCity">
        <SfDataManager AdaptorInstance="@typeof(CustomAdaptor)" Adaptor="Adaptors.CustomAdaptor"></SfDataManager>
        <KanbanColumns>
            <KanbanColumn HeaderText="Moscow" KeyField=@(new List<string>{"Moscow"}) AllowAdding=true></KanbanColumn>
            <KanbanColumn HeaderText="London" KeyField=@(new List<string>{"London"})></KanbanColumn>
            <KanbanColumn HeaderText="Singapore" KeyField=@(new List<string>{"Singapore"})></KanbanColumn>
        </KanbanColumns>
        <KanbanCardSettings HeaderField="EmployeeID" ContentField="ShipName"></KanbanCardSettings>
    </SfKanban>
    
    @code {
        public class CustomAdaptor : DataAdaptor
        {
            OrderContext db = new OrderContext();
            public CustomAdaptor()
            {
            }
            public async override Task<object> ReadAsync(DataManagerRequest dm, string key = null)
            {
                OrderService _craftService = new OrderService();
                IEnumerable DataSource = await _craftService.GetOrdersAsync();
                int count = DataSource.Cast<Order>().Count();
                return dm.RequiresCounts ? new DataResult() { Result = DataSource, Count = count } : (object)DataSource;
            }
    
            public override Task<object> InsertAsync(DataManager dm, object value, string key)
            {
                (value as Order).EmployeeID = db.Orders.Select(x => x.EmployeeID).Max() + 1;
                db.Orders.Add((Order)value);
                db.SaveChangesAsync();
                return Task.Run(() =>
                {
                    return value;
                });
            }
    
            public override async Task<object> UpdateAsync(DataManager dataManager, object value, string keyField, string key)
            {
                var data = db.Orders.Where(or => or.EmployeeID == (value as Order).EmployeeID).FirstOrDefault();
                if (data != null)
                {
                    data.EmployeeID = (value as Order).EmployeeID;
                    data.ShipCity = (value as Order).ShipCity;
                    data.ShipName = (value as Order).ShipName;
                }
                db.SaveChangesAsync();
                return Task.Run(() =>
                {
                    return value;
                });
            }
    
            public override async Task<object> RemoveAsync(DataManager dataManager, object value, string keyField, string key)
            {
                Order ord = db.Orders.Find(Convert.ToInt32(value));
                db.Orders.Remove(ord);
                db.SaveChangesAsync();
                return Task.Run(() =>
                {
                    return value;
                });
            }
    
            // Performs BatchUpdate operation
            public override object BatchUpdate(DataManager dm, object Changed, object Added, object Deleted, string KeyField, string Key, int? dropIndex)
            {
                if (Changed != null)
                {
                    foreach (var rec in (IEnumerable<Order>)Changed)
                    {
                        Order val = db.Orders.Where(or => or.EmployeeID == rec.EmployeeID).FirstOrDefault();
                        val.EmployeeID = (rec as Order).EmployeeID;
                        val.ShipCity = (rec as Order).ShipCity;
                        val.ShipName = (rec as Order).ShipName;
                    }
                }
                if (Added != null)
                {
                    foreach (var rec in (IEnumerable<Order>)Added)
                    {
                        db.Orders.Add(rec);
                    }
                }
                if (Deleted != null)
                {
                    foreach (var rec in (IEnumerable<Order>)Deleted)
                    {
                        db.Orders.Remove(db.Orders.Where(or => or.EmployeeID == rec.EmployeeID).FirstOrDefault());
                    }
                }
                db.SaveChanges();
                return db.Orders;
            }
        }
    }

    You can find the fully working sample here.

    Complex data binding

    Kanban supports to map the complex properties to fields of KanbanCardSettings, KanbanSwimlaneSettings and KanbanSortSettings. Kanban supports to map complex properties when using ExpandoObject and DynamicObject also.

    In the following sample, Kanban fields are mapped with complex data binding.

  • CSHTML
  • @using Syncfusion.Blazor.Kanban
    @using Syncfusion.Blazor.DropDowns
    @using Syncfusion.Blazor.Inputs
    <SfKanban TValue="SwimlaneTasksModel" KeyField="Status.KeyField" DataSource="KanbanSwimlaneTasks">
        <KanbanColumns>
            <KanbanColumn HeaderText="Backlog" KeyField="@(new List<string>() {"Open"})" AllowAdding="true" />
            <KanbanColumn HeaderText="In Progress" KeyField="@(new List<string>() {"InProgress"})" />
            <KanbanColumn HeaderText="Done" KeyField="@(new List<string>() {"Close"})" />
        </KanbanColumns>
        <KanbanCardSettings HeaderField="Id.HeaderId" ContentField="Summary.Content" />
        <KanbanSwimlaneSettings KeyField="AssigneeName.Name" AllowDragAndDrop="true" />
        <KanbanSortSettings SortBy="SortOrderBy.Index" Field="SortingFields.Index" Direction="SortDirection.Descending" />
        <KanbanEvents TValue="SwimlaneTasksModel" DialogOpen="onDialogOpen" />
        <KanbanDialogSettings>
            <Template>
                @{
                    SwimlaneTasksModel data = (SwimlaneTasksModel)context;
                    <table>
                        <tbody>
                            <tr>
                                <td class="e-label">ID</td>
                                <td>
                                    <SfNumericTextBox CssClass="e-field" @bind-Value="@(data.Id.HeaderId)" Enabled="@Check" Placeholder="ID" />
                                </td>
                            </tr>
                            <tr>
                                <td class="e-label">Status</td>
                                <td>
                                    <SfDropDownList TValue="string" TItem="DropDownModel" CssClass="e-field" DataSource="@StatusData" @bind-Value="@data.Status.KeyField">
                                        <DropDownListFieldSettings Text="Value" Value="Value" />
                                    </SfDropDownList>
                                </td>
                            </tr>
                            <tr>
                                <td class="e-label">Assignee</td>
                                <td>
                                    <SfDropDownList TValue="string" TItem="DropDownModel" CssClass="e-field" DataSource="@AssigneeData" @bind-Value="@data.AssigneeName.Name">
                                        <DropDownListFieldSettings Text="Value" Value="Value" />
                                    </SfDropDownList>
                                </td>
                            </tr>
                            <tr>
                                <td class="e-label">Index</td>
                                <td>
                                    <SfTextBox CssClass="e-field" Value="@data.SortingFields.Index.ToString()" />
                                </td>
                            </tr>
                            <tr>
                                <td class="e-label">Summary</td>
                                <td>
                                    <SfTextBox CssClass="e-field" Multiline="true" @bind-Value="@data.Summary.Content" />
                                </td>
                            </tr>
                        </tbody>
                    </table>
                }
            </Template>
        </KanbanDialogSettings>
    </SfKanban>
    
    @code {
        private Boolean Check = false;
        public class SwimlaneTasksModel
        {
            public HeaderModel Id { get; set; }
            public string Title { get; set; }
            public StatusModel Status { get; set; }
            public ContentModel Summary { get; set; }
            public SwimlaneAssignee AssigneeName { get; set; }
            public SortingModel SortingFields { get; set; }
        }
        public class SwimlaneAssignee
        {
            public string Name { get; set; }
            public string Text { get; set; }
        }
        public class StatusModel
        {
            public string KeyField { get; set; }
        }
        public class HeaderModel
        {
            public int HeaderId { get; set; }
        }
        public class ContentModel
        {
            public string Content { get; set; }
        }
        public class SortingModel
        {
            public int Index { get; set; }
        }
        public List<SwimlaneTasksModel> KanbanSwimlaneTasks = new List<SwimlaneTasksModel>()
        {
            new SwimlaneTasksModel { Id = new HeaderModel() { HeaderId = 1 }, Title = "BLAZ-29001", Status = new StatusModel() { KeyField = "Open" }, Summary = new ContentModel() { Content = "Analyze the new requirements gathered from the customer." }, AssigneeName = new SwimlaneAssignee() { Name = "Nancy Davloio", Text = "Nancy" }, SortingFields = new SortingModel() { Index = 2 } },
            new SwimlaneTasksModel { Id = new HeaderModel() { HeaderId = 2 }, Title = "BLAZ-29002", Status = new StatusModel() { KeyField = "InProgress" }, Summary = new ContentModel() { Content = "Improve application performance" }, AssigneeName = new SwimlaneAssignee() { Name = "Andrew Fuller", Text = "Andrew" }, SortingFields = new SortingModel() { Index = 1 } },
            new SwimlaneTasksModel { Id = new HeaderModel() { HeaderId = 3 }, Title = "BLAZ-29003", Status = new StatusModel() { KeyField = "Open" }, Summary = new ContentModel() { Content = "Arrange a web meeting with the customer to get new requirements." }, AssigneeName = new SwimlaneAssignee() { Name = "Janet Leverling", Text = "Janet" }, SortingFields = new SortingModel() { Index = 1 } },
            new SwimlaneTasksModel { Id = new HeaderModel() { HeaderId = 4 }, Title = "BLAZ-29004", Status = new StatusModel() { KeyField = "Open" }, Summary = new ContentModel() { Content = "Fix the issues reported in the IE browser." }, AssigneeName = new SwimlaneAssignee() { Name = "Janet Leverling", Text = "Janet" }, SortingFields = new SortingModel() { Index = 3 } },
            new SwimlaneTasksModel { Id = new HeaderModel() { HeaderId = 5 }, Title = "BLAZ-29005", Status = new StatusModel() { KeyField = "Testing" }, Summary = new ContentModel() { Content = "Fix the issues reported by the customer." }, AssigneeName = new SwimlaneAssignee() { Name = "Andrew Fuller", Text = "Andrew" }, SortingFields = new SortingModel() { Index = 1 } },
            new SwimlaneTasksModel { Id = new HeaderModel() { HeaderId = 6 }, Title = "BLAZ-29006", Status = new StatusModel() { KeyField = "InProgress" }, Summary = new ContentModel() { Content = "Fix the issues reported in Safari browser." }, AssigneeName = new SwimlaneAssignee() { Name = "Nancy Davloio", Text = "Nancy" }, SortingFields = new SortingModel() { Index = 2 }  },
            new SwimlaneTasksModel { Id = new HeaderModel() { HeaderId = 7 }, Title = "BLAZ-29007", Status = new StatusModel() { KeyField = "Close" }, Summary = new ContentModel() { Content = "Test the application in the IE browser." }, AssigneeName = new SwimlaneAssignee() { Name = "Andrew Fuller", Text = "Andrew" }, SortingFields = new SortingModel() { Index = 1 } }
        };
        private class DropDownModel
        {
            public int Id { get; set; }
            public string Value { get; set; }
        }
        private List<DropDownModel> StatusData = new List<DropDownModel>() {
            new DropDownModel { Id = 0, Value = "Open" },
            new DropDownModel { Id = 1, Value = "InProgress" },
            new DropDownModel { Id = 3, Value = "Close" }
        };
        private List<DropDownModel> AssigneeData = new List<DropDownModel>() {
            new DropDownModel { Id = 0, Value = "Nancy Davloio" },
            new DropDownModel { Id = 1, Value = "Andrew Fuller" },
            new DropDownModel { Id = 2, Value = "Janet Leverling" },
            new DropDownModel { Id = 3, Value = "Steven walker" },
            new DropDownModel { Id = 4, Value = "Robert King" },
            new DropDownModel { Id = 5, Value = "Margaret hamilt" },
            new DropDownModel { Id = 6, Value = "Michael Suyama" }
        };
        public void onDialogOpen(DialogOpenEventArgs<SwimlaneTasksModel> args)
        {
            if (args.RequestType.ToString() == "Add")
            {
                Check = true;
            }
            else
            {
                Check = false;
            }
        }
    }

    Complex Data Binding in Blazor Kanban