Telerik Forums
UI for ASP.NET Core Forum
1 answer
24 views

Hope I'm asking this question in the right place.

I have this working:

    @(Html.Kendo().Menu()
            .Name("QmsMenu")
            .DataTextField("Title")
            .DataSource(ds => ds.Read("Menu_Read", "Menu")
            .Model(model => model.Children("MenuItems")))
    )

Reference: https://demos.telerik.com/aspnet-core/menu/remote-data-binding

However, I can't figure out how to dynamically add links to each of the items in a Menu binding to remote data.

The payload returned from the server is providing the controller name and controller action method name for each of the menu items. I would like to add these values to the code above enabling a user to click on a menu link and be taken to a different page in the web application.

Would appreciate some help regarding this.

 

Aleksandar
Telerik team
 answered on 07 Jul 2022
1 answer
6 views

Hi I have a requirement to show a popup when a card is moved or dropped to  another column say working to done. in the popup if i say no then the card has to be moved back to where it was before.  Basically can i undo the movement after validation or can i move the object without manual intervention. Does there are methods to move the card from one column to another thru Javascript?

 

Thanks!

Venu

Mihaela
Telerik team
 answered on 06 Jul 2022
0 answers
23 views

Using a controller based off of the sample code and the chunked upload configuration, I find that in local testing I eventually end up with an IO exception claiming another process is trying to access the file when I'm uploading larger files.

I suspect I am somehow having the AppendToFile() call run into the previous AppendToFile() call before it is done syncing to disk or something, but have no proof.

In addition, when the fallback Save() call is used (if I forget to set a chunk size) the files turn out corrupt, being only partially uploaded. 

AV is disabled for the relevant directories and my Git services are not running, and so I'm not sure what else could be accessing the file. Is there a way to solve this issue?

I can go and try using the vanilla version of the sample code if I must but feel that there may be something else going on here regardless.

Thank you!

My Controller:


using Csla;
using Finbuckle.MultiTenant;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Newtonsoft.Json;
using Portal.Library;
using System.Net;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;

namespace WebUI.Controllers
{
    public class FileStore : Csla.Web.Mvc.Controller
    {
        private string UploadBasePath = "wwwroot\\uploads";
        private string UploadStoragePath = "wwwroot\\storage";

        public WebUI.TenantInfo? TenantInfo { get; private set; }

        public FileStore(IMultiTenantContextAccessor<WebUI.TenantInfo> mtaccessor)
        {
            TenantInfo = mtaccessor.MultiTenantContext.TenantInfo;
        }

        public class ChunkMetaData
        {
            public string UploadUid { get; set; }
            public string FileName { get; set; }
            public string RelativePath { get; set; }
            public string ContentType { get; set; }
            public long ChunkIndex { get; set; }
            public long TotalChunks { get; set; }
            public long TotalFileSize { get; set; }
        }

        public class FileResult
        {
            // Because the chunks are sent in a specific order,
            // the server is expected to send back a response
            // with the meta data of the chunk that is uploaded.
            public bool uploaded { get; set; }

            public string fileUid { get; set; }

            public string fileStorageGuid { get; set; } // internal GUID in DB per file not per session

            public string selector { get; set; } // because the UID isn't used or sent for the fallback Save()
        }

        public void AppendToFile(string fullPath, IFormFile content)
        {
            try
            {
                using (FileStream stream = new(fullPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
                {
                    content.CopyTo(stream);
                }
            }
            catch (IOException ex)
            {
                throw ex;
            }
        }

        public ActionResult ChunkSave(IEnumerable<IFormFile> files, string metaData)
        {
            if (metaData == null)
            {
                return Save(files);
            }

            MemoryStream ms = new(Encoding.UTF8.GetBytes(metaData));

            JsonSerializer serializer = new();
            ChunkMetaData chunkData;
            using (StreamReader streamReader = new(ms))
            {
                chunkData = (ChunkMetaData)serializer.Deserialize(streamReader, typeof(ChunkMetaData));
            }

            string path = Path.Combine(UploadBasePath, chunkData.FileName);
            // The Name of the Upload component is "files".
            if (files != null)
            {
                foreach (var file in files)
                {
                    AppendToFile(path, file);
                }
            }

            var isUploaded = chunkData.TotalChunks - 1 <= chunkData.ChunkIndex;
            FileResult fileBlob = new()
            {
                uploaded = isUploaded,
                fileUid = chunkData.UploadUid
            };

            if (isUploaded)
            {
                var bytes = Encoding.UTF8.GetBytes(chunkData.FileName);

                fileBlob.fileStorageGuid = CatalogAndMoveFileAsync(chunkData.FileName, path).ToString();
                fileBlob.selector = Convert.ToBase64String(bytes);
            }

            return Json(fileBlob);
        }

        private Guid CatalogAndMoveFileAsync(string FileName, string FilePath)
        {
            //sanity
            if (System.IO.File.Exists(FilePath))
            {
                // step 1 - generate guid in files table by inserting the requisite information
                var _user = Csla.ApplicationContext.User as ClaimsPrincipal;
                int userID = int.Parse(_user.Claims.FirstOrDefault(S => S.Type == "UserID").Value.ToString());

                var finfo = new FileInfo(FilePath);

                var obj = DataPortal.Create<FileStorage>();
                obj.UserID = userID;
                obj.Extension = Path.GetExtension(FileName);
                obj.OrigName = FileName;
                obj.KBSize = finfo.Length / 1024;
                obj.SiteID = TenantInfo.SiteID;

                // TODO: revisit and see if SaveObjectAsync updates our object v6
                // use the object's own save function to update the object
                obj = obj.Save();

                // do we have to re-fetch or is the object guid updated automatically?
                if (obj.ID != Guid.Empty)
                {
                    // step 2 - determine new name, location, move to it
                    string StorageSubfolders = Path.Combine(UploadStoragePath, obj.RelPath);

                    try
                    {
                        System.IO.Directory.CreateDirectory(StorageSubfolders); // so glad this func does it in one go

                        var newFilePath = Path.Combine(StorageSubfolders, obj.InternalName);
                        System.IO.File.Move(FilePath, newFilePath);

                        if (System.IO.File.Exists(newFilePath))
                        {
                            // step 3 - return the guid
                            return obj.ID;
                        }
                    }
                    catch (Exception ex)
                    {
                        if (obj.ID != Guid.Empty)
                        {
                            DataPortal.Delete<FileStorage>(obj.ID, userID);
                        }

                        throw new FileNotFoundException("FileStorage moving file", ex);
                    }
                }
            }

            return Guid.Empty;
        }

        public ActionResult RemoveAsync(string[] fileNames, string[] FileStorageUIDs)
        {
            // we don't actually care about fileNames, telerik always sends *something* related to that though
            // we rely on our file UID(s) that were sent back and hopefully captured in the onSuccess event

            // The parameter of the Remove action must be called "fileNames"
            if (FileStorageUIDs.Length > 0)
            {
                foreach (var fUID in FileStorageUIDs)
                {
                    // get the current UID to be 100% sure we're deleting only our own file
                    var _user = Csla.ApplicationContext.User as ClaimsPrincipal;
                    int userID = int.Parse(_user.Claims.FirstOrDefault(S => S.Type == "UserID").Value.ToString());

                    var obj = DataPortal.Fetch<FileStorage>(new Guid(fUID), userID);

                    if (obj.ID != Guid.Empty)
                    {
                        var basePath = Path.Combine(UploadStoragePath, obj.RelPath);
                        var physicalPath = Path.Combine(basePath, obj.InternalName);
                        if (System.IO.File.Exists(physicalPath))
                        {
                            System.IO.File.Delete(physicalPath);

                           //TODO: maybe try/catch?
                            if (Directory.EnumerateFiles(basePath).Any() == false)
                            {
                                Directory.Delete(basePath, true);
                            }

                            // we already fetched with UID
                            DataPortal.Delete<FileStorage>(obj.ID, userID);
                        }
                    }
                }
            }

            // Return an empty string to signify success
            return Content("");
        }

        // TODO: while this method fully works, for whatever reason it corrupts files when we fall back to it
        public ActionResult Save(IEnumerable<IFormFile> files)
        {
            List<string> fileNames = new();
            List<FileResult> results = new();
            
            // The Name of the Upload component is "files".
            if (files != null)
            {
                foreach (var file in files)
                {
                    var fileContent = ContentDispositionHeaderValue.Parse(file.ContentDisposition);

                    // Some browsers send file names with full path.
                    // The demo is interested only in the file name.
                    var fileName = Path.GetFileName(fileContent.FileName.ToString().Trim('"'));
                    var physicalPath = Path.Combine(UploadBasePath, fileName);

                    using var fileStream = new FileStream(physicalPath, FileMode.Create);
                    file.CopyToAsync(fileStream);

                    fileNames.Add(fileName);
                }

                // since we can't delete while in the scope of the loop there, we have to do a second one
                foreach (var fileName in fileNames)
                {
                    var physicalPath = Path.Combine(UploadBasePath, fileName);

                    var fileStorageGuid = CatalogAndMoveFileAsync(fileName, physicalPath);
                    string guidString = fileStorageGuid.ToString();

                    var bytes = Encoding.UTF8.GetBytes(fileName);

                    results.Add(new FileResult()
                    {
                        uploaded = true,
                        fileStorageGuid = guidString,
                        selector = Convert.ToBase64String(bytes)
                    });
                }

                return Json(results);
            }

            return Content("");
        }

        public async Task GetFile(string FileStorageUID)
        {
            var _user = Csla.ApplicationContext.User as ClaimsPrincipal;
            int userID = int.Parse(_user.Claims.FirstOrDefault(S => S.Type == "UserID").Value.ToString());

            // TODO: maybe add a func to the user dal to fetch hierarchy?
            // TODO: check role here, if UID doesn't match, don't pass userID

            var obj = await DataPortal.FetchAsync<FileStorage>(new Guid(FileStorageUID), 0); // uid is second param if needed

            if (obj.ID != Guid.Empty)
            {
                var basePath = Path.Combine(UploadStoragePath, obj.RelPath);
                var physicalPath = Path.Combine(basePath, obj.InternalName);
                if (System.IO.File.Exists(physicalPath))
                {
                    var provider = new FileExtensionContentTypeProvider();
                    if (!provider.TryGetContentType(physicalPath, out string contentType))
                    {
                        contentType = "application/octet-stream";
                    }

                    Response.Clear();
                    Response.Headers.Add("Content-Disposition", "inline;filename=" + obj.OrigName);
                    Response.ContentType = contentType;

                    await Response.SendFileAsync(physicalPath);
                }
            }
        }
    }
}

My View:


                        @(Html.Kendo().Upload()
                            .Name("files")
                            .Async(a => a
                                .Save("ChunkSave", "FileStore")
                                .Remove("Remove", "FileStore")
                                .AutoUpload(true)
                                .ChunkSize(10240) 
                            )
                            .Validation(validation => validation
                                //.AllowedExtensions(new string[] { ".gif", ".jpg", ".png" })
                                .MaxFileSize(200000000) // 200 megs
                               //.MinFileSize(512)
                            )
                            .Events(events => events
                                .Success("onSuccess")
                                .Remove("onRemove")
                                .Upload("onUpload")
                            ).Multiple(false) // one file and one file only
                        ) 

Matt
Top achievements
Rank 1
 updated question on 05 Jul 2022
0 answers
19 views

Hi!

I have the following setup:

CsHtml:

	<div class="row mt-3">
		<div class="col-lg-4">
			@(Html.Kendo().DropDownListFor(m => m.CategoryHeadId)
			      .Size(ComponentSize.Medium)
				  .Rounded(Rounded.Medium)
				  .FillMode(FillMode.Solid)
				  .OptionLabel("Select head category...")
				  .HtmlAttributes(new { style = "width: 100%" })
				  .DataTextField("Name")
				  .DataValueField("Id")
				  .DataSource(source =>
				  {
					  source.Read(read =>
					  {
						  read.Action("GetLookupCategoriesHead", "Api");
					  });
				  })
				)
		</div>
		<div class="col-lg-4">
			@(Html.Kendo().DropDownListFor(m => m.CategoryMainId)
			      .Size(ComponentSize.Medium)
				.Rounded(Rounded.Medium)
				.FillMode(FillMode.Solid)
				.OptionLabel("Select main category...")
				.HtmlAttributes(new { style = "width: 100%" })
				.DataTextField("Name")
				.DataValueField("Id")
				.DataSource(source =>
				{
					source.Read(read =>
					{
						read.Action("GetLookupCategoriesMain", "Api")
						    .Data("filterMainCategories");
					})
					.ServerFiltering(true);
				})
				.Enable(false)
				.AutoBind(false)
				.CascadeFrom("CategoryHeadId")
				)
		</div>
		<div class="col-lg-4">
			@(Html.Kendo().DropDownListFor(m => m.CategorySubId)
				.Size(ComponentSize.Medium)
				.Rounded(Rounded.Medium)
				.FillMode(FillMode.Solid)
				.OptionLabel("Select sub-category...")
				.HtmlAttributes(new { style = "width: 100%" })
				.DataTextField("Name")
				.DataValueField("Id")
				.DataSource(source =>
				{
					source.Read(read =>
					{
						read.Action("GetLookupCategoriesSub", "Api")
						    .Data("filterSubCategories");
					})
					.ServerFiltering(true);
				})
				.Enable(false)
				.AutoBind(false)
				.CascadeFrom("CategoryMainId")
				)
		</div>
	</div>

Script:

@section Scripts {
	<script>
		function filterMainCategories() {
			return {
				headId: $("#CategoryHeadId").val()
			};
		}

		function filterSubCategories() {
			return {
				headId: $("#CategoryHeadId").val(),
				mainId: $("#CategoryMainId").val()
			};
		}
	</script>
}

Controller:

    public async Task<JsonResult> GetLookupCategoriesHead()
    {
        var result = await serviceLookUps.GetAllCategoriesHeadAsync();
        return new JsonResult(result);
    }

    public async Task<JsonResult> GetLookupCategoriesMain(int headId)
    {
        var result = await serviceLookUps.GetAllCategoriesMainAsync(headId);
        return new JsonResult(result);
    }

    public async Task<JsonResult> GetLookupCategoriesSub(int headId, int mainId)
    {
        var result = await serviceLookUps.GetAllCategoriesSubAsync(headId, mainId);
        return new JsonResult(result);
    }

Model:

[DebuggerDisplay($"{nameof(Id)}: {{{nameof(Id)},nq}}, {nameof(Name)}: {{{nameof(Name)}}}, {nameof(Active)}: {{{nameof(Active)}}}")]
public class LookupCategoryHeadModel
{
    public int    Id     { get; set; }
    public string Name   { get; set; } = string.Empty;
    public bool   Active { get; set; }
}

[DebuggerDisplay($"{nameof(Id)}: {{{nameof(Id)},nq}}, {nameof(Name)}: {{{nameof(Name)}}}, {nameof(Active)}: {{{nameof(Active)}}}")]
public class LookupCategoryMainModel
{
    public int    Id     { get; set; }
    public int    HeadId { get; set; }
    public string Name   { get; set; } = string.Empty;
    public bool   Active { get; set; }
}

[DebuggerDisplay($"{nameof(Id)}: {{{nameof(Id)},nq}}, {nameof(Name)}: {{{nameof(Name)}}}, {nameof(Active)}: {{{nameof(Active)}}}")]
public class LookupCategorySubModel
{
    public int    Id     { get; set; }
    public int    HeadId { get; set; }
    public int    MainId { get; set; }
    public string Name   { get; set; } = string.Empty;
    public bool   Active { get; set; }
}
Issue:

I have tested the API methods separately through tests and ensured they are returning values. The only issue is, when I make a selection in the second dropdown, on the API, the second parameter is received as 0 instead of a correct selected value. This doesn't cause any console errors so the UI continues to work. I can cascade from dropdown one to dropdown two but dropdown two sends a 0 for mainId from its filtering operation in filterSubCategories()

DoomerDGR8
Top achievements
Rank 2
Iron
Iron
Veteran
 asked on 04 Jul 2022
1 answer
10 views

Hello,

can be hidden Saturdays and Sundays from the scheduler control in all views and how?

Many thanks!

 

Stoyan
Telerik team
 answered on 01 Jul 2022
1 answer
28 views

In Kendo UI for ASP.Net MVC, you could bind a grid directly to a DataTable, as in

@Html.Kendo().Grid(MyDataTable)...

where MyDataTable is a DataTable object populated with data. 

However, using the ASP.Net Core kendo libraries, there is no support for this.

Why not? Are Telerik planning to add this?

Thanks.

Aleksandar
Telerik team
 answered on 01 Jul 2022
1 answer
33 views

So I just created a .NET 6 ASP.NET Core Web App (Razor Pages), added all telerik stuff according to the instructions (NuGet Packages, scripts and styles in _Layout.cshtml, @addTagHelper-s in _ViewImports.cshtml), and HTMLHelpers are rendering correctly. However, when I provide a data source to the DropDownList or ComboBox, all options show up as undefined.

Telerik version: 2022.2.621.   Bootstrap: V5.


Here's the code for DropDownList:

Index.cshtml

@page
@model IndexModel

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@Html.AntiForgeryToken()
@{
    ViewData["Title"] = "Home page";
}

--------------------------------------------------------------------------------

 

 

<div class="text-center"> <h1 class="display-4">Welcome</h1> @(Html.Kendo().DropDownList() .Name("MyDropdownn") .DataTextField("Text") .DataValueField("Value") .HtmlAttributes(new { style = "width:300px;" }) .AutoBind(false) .Filter(FilterType.Contains) .DataSource(ds => ds .Custom() .Transport(transport => transport .Read(r => r .Url("/Index?handler=Sports") )) .ServerFiltering(false) ) ) </div>

 

Index.cshtml.cs

        public JsonResult OnGetSports()
        {
            var allSports1 = db.Sports.Where(x => x.SportID > 0).Select(x => new DropDownModel
            {
                Text = x.Name,
                Value = x.SportID 
            }).ToList();

            return new JsonResult(allSports1);
        }

Proof that the data isn't empty/undefined

DropDownModel.cs (DropDownModel class)

    public class DropDownModel
    {
        public int Value { get; set; }
        public string Text { get; set; } = String.Empty;
    }

 

I've tried restarting everything, double-checking everything but nothing helped. Is there a bug with this version of telerik, is something incompatible with ,NET 6 or Bootstrap 5? Or am I doing something wrong? 

Alexander
Telerik team
 answered on 29 Jun 2022
1 answer
19 views
I have a grid with a custom popup editor.   All of the fields in the popup are automatically set to "Required" when the pop up opens.  There are no annotations in the model that should be causing them to be validated.  I only want a couple of them to be required.  How do I change this behavior?  Where is Kendo getting the idea that every field must be required?
Alexander
Telerik team
 answered on 29 Jun 2022
1 answer
22 views

Hello,

I'm migrating an application from UI For ASP.NET MVC to UI For ASP.NET Core and mobile components are not found, what are the equivalents components of MobileApplication, MobileListView, etc.?

Stoyan
Telerik team
 answered on 29 Jun 2022
1 answer
17 views
I'm trying to add a new filterview option to an existing grid filter with six other options that currently work fine. What's different about option 7 is that it needs to use two "or" values to filter the grid with. All the others have only one. Here is the code I've tried (along with one of the working filterview options), but it returns nothing and doesn't even generate any SQL in the console window. There is data in the database for both values.

                //this code works:
                new FilterView("CashR")
                {
                    Filters = new List<DataFilterValue>
                    {
                        new DataFilterValue
                        {
                            Field = nameof(InvoiceVM.InvoiceStatus),
                            Value = "CashR",
                            Operator = ExtendedFilterOperator.IsEqualTo
                        }
                    }
                },
                //this code doesn't work:
                new FilterView("Select Checks")
                {
                    Filters = new List<DataFilterValue>
                    {
                        new DataFilterValue
                        {
                            Field = nameof(InvoiceVM.InvoiceStatus),
                            Value = new string[] { "Verified", "Partial" },
                            Operator = ExtendedFilterOperator.IsInListEqualTo
                        }
                    }
                }
Aleksandar
Telerik team
 answered on 29 Jun 2022
Top users last month
Toby
Top achievements
Rank 3
Iron
Iron
Benjamin
Top achievements
Rank 2
Iron
Veteran
Bernd
Top achievements
Rank 4
Bronze
Iron
Iron
minh
Top achievements
Rank 2
Iron
Iron
Iron
Sebastien
Top achievements
Rank 2
Iron
Iron
Iron
Want to show your ninja superpower to fellow developers?
Want to show your ninja superpower to fellow developers?