Integrate Blazor with Chart.js: how to pass an object - chart.js

I want to display in my Blazor WebAssembly application, some graphs with Chart.js. I tried to use Chartjs.Blazor.Fork but I have few errors, for example I have opened another post about here.
So, after a day without results, I decided to start my own component. I follow the instruction I found in a blog. Basically, I have my Razor component called Chart.razor with the following code
#inject IJSRuntime JSRuntime
<canvas id="#Id"></canvas>
#code {
public enum ChartType
{
Pie,
Bar
}
[Parameter]
public string Id { get; set; }
[Parameter]
public ChartType Type { get; set; }
[Parameter]
public string[] Data { get; set; }
[Parameter]
public string[] BackgroundColor { get; set; }
[Parameter]
public string[] Labels { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Here we create an anonymous type with all the options
// that need to be sent to Chart.js
var config = new
{
Type = Type.ToString().ToLower(),
Options = new
{
Responsive = true,
Scales = new
{
YAxes = new[]
{
new { Ticks = new {
BeginAtZero=true
} }
}
}
},
Data = new
{
Datasets = new[]
{
new { Data = Data, BackgroundColor = BackgroundColor}
},
Labels = Labels
}
};
await JSRuntime.InvokeVoidAsync("setup", Id, config);
}
}
then I have my own mychart.js script to update the chart
window.setup = (id,config) => {
var ctx = document.getElementById(id).getContext('2d');
new Chart(ctx, config);
}
So, I use this code
<Chart Id="bar1" Type="#Chart.ChartType.Bar"
Data="#(new[] { " 10", "9" } )"
BackgroundColor="#(new[] { " yellow","red"} )"
Labels="#(new[] { " Fail","Ok" } )">
</Chart>
Ugly code but it is working. Now, I can display a graph in my page. Cool! What I want to display is something more complex, because I have to show a stacked bar graph with groups and the configuration is quite complicated.
I want to replace the config you can see in the page with for example a class. In this class I want to collect all configuration, like Type, Options, Data, Labels and so on, and pass them in the await JSRuntime.InvokeVoidAsync("setup", Id, config);`
For starting I created my base class like
public abstract class ConfigBase
{
protected ConfigBase(ChartType chartType)
{
Type = chartType;
}
public ChartType Type { get; }
public string CanvasId { get; } = Guid.NewGuid().ToString();
}
My problem is how to transform this class to obtain a valid object for the JavaScript to execute correctly new Chart(ctx, config);.

OK, it was quite easy. I saw a lot of different technics for that but there is a very basic simple way
await JSRuntime.InvokeVoidAsync("setup", Config.CanvasId, Config);

Related

How to add multiple custom columns to IDataView in ML.NET

I'd like to add two custom columns after loading my IDataView from file. In each row, the new column values should be natural logarithm of the values.
My environment is:
Windows 10 Pro, Version 10.0.19043 Build 19043
ML.NET version 1.6.0
Microsoft Visual Studio Professional 2022 Preview (64-bit), Version 17.0.0 Preview 3.1
Here is what I have done so far. This is a standalone, reproducible program that demonstrates the problem.
using Microsoft.ML;
using Microsoft.ML.Transforms;
namespace TestLog {
public static class Program {
private class InputData {
public double Velocity { get; set; }
public double Thrust { get; set; }
}
private class CustomMappingOutput {
public double LogVelocity { get; set; }
public double LogThrust { get; set; }
}
private class TransformedData : InputData {
public double LogVelocity { get; set; }
public double LogThrust { get; set; }
}
[CustomMappingFactoryAttribute("LogVelocity")]
private class LogVelocityCustomAction : CustomMappingFactory<InputData, CustomMappingOutput> {
public static void CustomAction(InputData input, CustomMappingOutput output)
=> output.LogVelocity = (float) Math.Log(input.Velocity);
public override Action<InputData, CustomMappingOutput> GetMapping() => CustomAction;
}
[CustomMappingFactoryAttribute("LogThrust")]
private class LogThrustCustomAction : CustomMappingFactory<InputData, CustomMappingOutput> {
public static void CustomAction(InputData input, CustomMappingOutput output)
=> output.LogThrust = (float)Math.Log(input.Thrust);
public override Action<InputData, CustomMappingOutput> GetMapping() => CustomAction;
}
public static void Run() {
var mlContext = new MLContext();
var samples = new List<InputData> {
new InputData { Velocity= 0.006467, Thrust = 1.614237 },
new InputData { Velocity= 0.53451, Thrust = 1.068356 },
new InputData { Velocity= 0.278578, Thrust = 0.216861 },
new InputData { Velocity= 0.014179, Thrust = 0.119712 },
new InputData { Velocity= 0.392814, Thrust = 3.915486 }
};
var data = mlContext.Data.LoadFromEnumerable(samples);
var pipeline = mlContext.Transforms.CustomMapping(new LogVelocityCustomAction().GetMapping(),
contractName: "LogVelocity")
.Append(mlContext.Transforms.CustomMapping(new LogThrustCustomAction().GetMapping(),
contractName: "LogThrust"));
var transformer = pipeline.Fit(data);
// Now save the transform pipeline so that it can be reloaded by another process.
mlContext.Model.Save(transformer, data.Schema, "customTransform.zip");
// We load the saved transform and use it, as if it was in another program.
var loadedTransform = mlContext.Model.Load("customTransform.zip", out _);
// Now we can transform the data.
var transformedIDataView = loadedTransform.Transform(data);
var newDataEnumerable = mlContext.Data.CreateEnumerable<TransformedData>(transformedIDataView,
reuseRowObject: true);
var newIDataView = mlContext.Data.LoadFromEnumerable(newDataEnumerable);
// Save newIDataView as a CSV file so we can verify the transformations.
var path = #"../../../transformed.csv";
var mlContextSave = new MLContext();
using var stream = File.Create(path);
mlContextSave.Data.SaveAsText(newIDataView,
stream,
separatorChar: ',',
headerRow: true,
schema: false);
}
static void Main() {
Program.Run();
Console.WriteLine("Hit return to exit");
Console.ReadLine();
}
}
}
At end of the program, the transformed IDataView is saved as a “.csv” file.
It looks like:
As you can see, the “Velocity” column was not transformed.
Any help or suggestions will be greatly appreciated.
Charles
You're casting the log to float, then assigning it to a variable of type double. Have you tried casting to double?
I'm not sure why it's working for thrust but not velocity. Perhaps the very small number in the first line of velocity is causing an exception.

How to display grouped list of values in Xamarin?

I am trying to display grouped list of songs, which I get from server. Songs are grouped in sets according to their first letter. I've created two classes in this purpose SongSet and Song.
public class SongSet
{
public string FirstCharacter { get; set; }
public List<Song> ListOfSongs { get; set; } = new List<Song>();
public SongSet(string firstChar)
{
this.FirstCharacter = firstChar;
}
}
public class Song
{
public string SongTitle { get; set; }
public Song(string title)
{
this.SongTitle = title;
}
}
After received list from server I put it to list of SongSet objects.
public static List<SongSet> ListOfSongsFromServer { get; set; } = new List<SongSet>();
And here my code to display a whole list.
ListView listView = new ListView
{
IsGroupingEnabled = true,
GroupDisplayBinding = new Binding("FirstCharacter"),
ItemsSource = PlayerPageVM.ListOfSongsFromServer,
ItemTemplate = new DataTemplate(() =>
{
Label titleLabel = new Label();
titleLabel.SetBinding(Label.TextProperty, "SongTitle");
return new ViewCell
{
View = new StackLayout
{
Padding = new Thickness(0, 5),
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
VerticalOptions=LayoutOptions.Center,
Spacing=0,
Children =
{
titleLabel
}
}
}
}
};
})
};
this.Content = new StackLayout
{
Children =
{
listView
}
};
After build application I've received only list with first letters but without song titles uder them. Can anyone help me how to achievie this?
You want to display grouped list of songs, you could use CollectionView. Because it provides the easy way to show Group data.
Display grouped data:
<CollectionView ItemsSource="{Binding Animals}"
IsGrouped="true">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
...
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
And the problem of mode data could refer to document to make SongSet inherit from Lsit<Song> to generate a grouped data.
public class SongSet : List<Song>
{
public string FirstCharacter { get; set; }
public SongSet(string firstChar,List<Song> ListOfSongs): base(ListOfSongs)
{
this.FirstCharacter = firstChar;
}
}

readable substitution of IEnumerable of Interfaces

I have the following interfaces
public interface IRibbonCommandsProvider
{
IEnumerable<IRibbonCommand> GetRibbonCommands();
}
public interface IRibbonCommand
{
string Group { get; }
string Tab { get; }
string Name { get; }
string Image { get; }
void Execute();
}
And the follwing substitution code:
public class TabsViewModelTests
{
[Fact]
public void Initialize_BuildsCorrectRibbonTree()
{
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(
new[]
{
new RibbonCommand { Tab = "Tab1", Group = "Group1", Name = "Name1" },
new RibbonCommand { Tab = "Tab1", Group = "Group1", Name = "Name2" },
new RibbonCommand { Tab = "Tab2", Group = "Group1", Name = "Name3" },
new RibbonCommand { Tab = "Tab2", Group = "Group2", Name = "Name3" }
});
...
}
private class RibbonCommand : IRibbonCommand
{
public string Group { get; set; }
public string Tab { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public void Execute() {}
}
}
Using NSubstitute, is there a clever way to get rid of the stub RibbonCommand class (that is nothing but a fake IRibbonCommand implementation - and that's NSubstitute's job) and still have list of fake ribbon commands that is as easily readable as the above?.
I can't come up with a readable way using NSubsitute's .Returns() fluent method without ending with a lot more (and unreadable) code.
Update:
A cool NSubstitute extension method could look like this. I just don't know if and how this can be built:
public static ConfiguredCall ReturnsMany<T>(
this IEnumerable<T> value,
Action<T> configureThis,
params Action<T>[] configureThese)
{
...
}
It would be used like this:
commands.GetRibbonCommands().ReturnsMany(
subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name1");
},
subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name2");
},
subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name3");
},
subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name3");
});
I think what you've got is very good — quite succinct and clear.
If you really want to get rid of the class you can use a substitute creation method for IRibbonCommand:
private IRibbonCommand Create(string tab, string group, string name)
{
var cmd = Substitute.For<IRibbonCommand>();
cmd.Tab.Returns(tab);
cmd.Group.Returns(group);
cmd.Name.Returns(name);
return cmd;
}
[Fact]
public void Initialize_BuildsCorrectRibbonTree()
{
var ribbonCommands = new[] {
Create("tab1", "group1", "name1"),
Create("tab1", "group1", "name2"),
Create("tab2", "group1", "name3"),
Create("tab2", "group1", "name4")
};
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(ribbonCommands);
// ...
}
This doesn't buy you much, although it does mean your test code will be more protected from changes to the IRibbonCommand interface (e.g. an additional property will not require changing your test code), and means you can check received calls and stub other calls on individual items.
Aside: Can use argument names if you want to more closely match the original code:
Create(tab: "tab1", group: "group1", name: "name1"),
As alternative you may setup Command inside test. Then move config func out of the test and optionally generalize for other types as you go. Yagni it.
UPDATED to working test
[Test]
public void Test()
{
Func<Action<IRibbonCommand>, IRibbonCommand> cmd = config =>
{
var c = Substitute.For<IRibbonCommand>();
config(c);
return c;
};
var ribbonCommands = new[]
{
cmd(c => { c.Tab.Returns("Tab1"); c.Group.Returns("Group1"); c.Name.Returns("Name1"); }),
cmd(c => { c.Tab.Returns("Tab1"); c.Group.Returns("Group1"); c.Name.Returns("Name2"); }),
cmd(c => { c.Tab.Returns("Tab2"); c.Group.Returns("Group1"); c.Name.Returns("Name3"); }),
cmd(c => { c.Tab.Returns("Tab2"); c.Group.Returns("Group1"); c.Name.Returns("Name4"); })
};
var commandsProvider = Substitute.For<IRibbonCommandsProvider>();
commandsProvider.GetRibbonCommands().Returns(ribbonCommands);
}
I don't see anything out of the box that's going to do what you're after. One option might be for you to write your own extension method to make the construction easier. So, something like this:
public static class ReadOnlySubstitute {
static public T For<T>(object source) where T : class {
var sub = Substitute.For<T>();
foreach (var prop in source.GetType().GetProperties()) {
sub.GetType().GetProperty(prop.Name).GetValue(sub).Returns(prop.GetValue(source));
}
return sub;
}
}
The above code essentially creates a substitute for the given interface and then sets up a return on each of properties specified in the supplied object.
This could then be used in your test like this to supply anonymous objects with the parameters:
[Test]
public void Initialize_BuildsCorrectRibbonTree() {
var ribbonCommands = new[]
{
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab1", Group="Grp1", Name="Nam1"}),
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab1", Group="Grp1", Name="Nam2"}),
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab2", Group="Grp1", Name="Nam3"}),
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab2", Group="Grp2", Name="Nam3"})
};
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(ribbonCommands);
....
}
It's not quite as concise as using the RibbonCommand class, since you have to construct the array before passing it into the Returns method because NSubstitute gets confused if you try to setup the Returns on the elements at the same time as on the GetRibbonCommands, but I think it's fairly close.
This is really an enhancement (subjective) of #dadhi's answer, combined with an answer from #David Tchepak to a different question.
So, rather than having to create a new Func for each interface your want to use, as described by #dadhi, you can instead create a generic method that takes an Action. You could be this in a shared class, something like this:
static class ConfiguredSub {
public static T For<T>(Action<T> config) where T : class {
var c = Substitute.For<T>();
config(c);
return c;
}
}
The problem that I encountered with my other answer was that if you have nested Returns, NSubstitute gets confused and starts throwing exceptions. It turns out that as described by #David here, you can pass a Func to defer the execution and get round this issue. If you combine these two things, then you get something pretty close to what you're after.
[Test]
public void Initialize_BuildsCorrectRibbonTree() {
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(x => new[] {
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name1");
}),
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name2");
}),
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name3");
}),
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name4");
})
});
// ...
}

Unit Testing a RenderMvcController even possible?

So I'm working with Umbraco 6.12 and having great difficulty been able to test a RenderMvcController.
I have implemented IApplicationEventHandler in my Global.ascx and Ninject is working fine and as expected when running the application - all good.
However, unit testing these controllers is a different matter. I found this, and have added the latest reply:
http://issues.umbraco.org/issue/U4-1717
I now have this lovely hack in my SetUp:
Umbraco.Web.UmbracoContext.EnsureContext(new HttpContextWrapper(new HttpContext(new HttpRequest("", "http://www.myserver.com", ""), new HttpResponse(null))), ApplicationContext.Current);
Which has got around the original UmbracoContext cannot be null, but is now throwing:
Current has not been initialized on Umbraco.Web.PublishedCache.PublishedCachesResolver. You must initialize Current before trying to read it.
The published caches resolver also seems to be hidden behind internal and protected stuff, which I can't use reflection to hack at as I can't init anything to pass into SetProperty reflection.
It's really frustrating, I'm loving v6, and using uMapper is very nice. I can inject a repo, service, command or query at will into the controllers and life is good - I just can't cover the controllers!
Any help on this would be greatly appreciated.
Thanks.
To unit test a Umbraco RenderMvcController, you need to grab the source code from github, compile the solution yourself, and get the Umbraco.Tests.dll and reference it on your test project.
In addition to that, you need to reference the SQLCE4Umbraco.dll which is distributed with the Umbraco packages, and Rhino.Mocks.dll which is internally for mocking.
To help you with this, I have compiled put the Umbraco.Tests.dll for Umbraco 6.1.5 and put it together with the Rhino.Mocks.dll and put it on this zip file.
Finally, derive your test from BaseRoutingTest, override the DatabaseTestBehavior to
NoDatabasePerFixture, and get the UmbracoContext and HttpBaseContext by calling the GetRoutingContext method, as in the code below:
using System;
using Moq;
using NUnit.Framework;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace UnitTests.Controllers
{
public class Entry
{
public int Id { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public string[] Tags { get; set; }
public DateTime Date { get; set; }
}
public interface IBlogService
{
Entry GetBlogEntry(int id);
}
public class BlogEntryController : RenderMvcController
{
private readonly IBlogService _blogService;
public BlogEntryController(IBlogService blogService, UmbracoContext ctx)
: base(ctx)
{
_blogService = blogService;
}
public BlogEntryController(IBlogService blogService)
: this(blogService, UmbracoContext.Current)
{
}
public override ActionResult Index(RenderModel model)
{
var entry = _blogService.GetBlogEntry(model.Content.Id);
// Test will fail if we return CurrentTemplate(model) as is expecting
// the action from ControllerContext.RouteData.Values["action"]
return View("BlogEntry", entry);
}
}
[TestFixture]
public class RenderMvcControllerTests : BaseRoutingTest
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
[Test]
public void CanGetIndex()
{
const int id = 1234;
var content = new Mock<IPublishedContent>();
content.Setup(c => c.Id).Returns(id);
var model = new RenderModel(content.Object, CultureInfo.InvariantCulture);
var blogService = new Mock<IBlogService>();
var entry = new Entry { Id = id };
blogService.Setup(s => s.GetBlogEntry(id)).Returns(entry);
var controller = GetBlogEntryController(blogService.Object);
var result = (ViewResult)controller.Index(model);
blogService.Verify(s => s.GetBlogEntry(id), Times.Once());
Assert.IsNotNull(result);
Assert.IsAssignableFrom<Entry>(result.Model);
}
private BlogEntryController GetBlogEntryController(IBlogService blogService)
{
var routingContext = GetRoutingContext("/test");
var umbracoContext = routingContext.UmbracoContext;
var contextBase = umbracoContext.HttpContext;
var controller = new BlogEntryController(blogService, umbracoContext);
controller.ControllerContext = new ControllerContext(contextBase, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(contextBase, new RouteData()), new RouteCollection());
return controller;
}
}
}
This code has only been tested in Umbraco 6.1.5.
According to the core team, you should include the Umbraco.Tests library and inherit your test from BaseUmbracoApplicationTest. That will setup a valid UmbracoApplication and UmbracoContext.
https://groups.google.com/forum/?fromgroups=#!topic/umbraco-dev/vEjdzjqmtsU
I have raised this on the Umbraco forums and there are several replies which may help you.
See here:
http://our.umbraco.org/forum/developers/api-questions/37255-How-can-I-unit-test-a-class-inheriting-from-SurfaceController
Essentially, you can .. just ... but requires some reflection because some of the key classes and interfaces are internal. As Luke's last post points out, this is because the functionality is currently a bit of a moving target.

using a viewmodel to assign values from a soap web service to a dropdownlist

I need some advice on getting values from a soap web service to display in a dropdownlist using a viewmodel, I currently receive the data for the various dropdownlists from a service class found in a service class library project (n-tier application).
The code for the dropdownlist service follows a similar format to the code below:
public IEnumerable<SelectListItem> getValuesAsSelectItems(string selected)
{
var items = new List<SelectListItem>();
items.Add(new SelectListItem { Text = "Please Select", Value = string.Empty, Selected = (selected == string.Empty) });
foreach (var value in this.getValues())
{
items.Add(new SelectListItem { Text = value.Value, Value = value.Value, Selected = (selected == value.Value) });
}
return new SelectList(items, "Value", "Text");
}
I need a way of passing the values from this service to the viewmodel I have then created 7 controllers for each of the dropdownlist which will all link to partial views that I can reuse throughout the application, dropdownlists include titles, countries, states and others.
An approach you could take is to extract the drop down list values into a viewmodel of their own. So:
Step 1: Create a view model (ItemsViewModel) that encapsulates the drop down list items:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Models
{
public class DropDownListItem
{
public string Text { get; set; }
public string Value { get; set; }
}
public class ItemsViewModel
{
private readonly List<DropDownListItem> _items;
// The selected item:
public string SelectedItem { get; set; }
// The items:
public IEnumerable<SelectListItem> Items
{
get
{
var allItems = _items.Select(i => new SelectListItem
{
Value = i.Value,
Text = i.Text
});
return DefaultItem.Concat(allItems);
}
}
// Default item (i.e. the "select" text) if none selected
public IEnumerable<SelectListItem> DefaultItem
{
get
{
return Enumerable.Repeat(new SelectListItem
{
Value = "-1",
Text = "Select an item"
}, count: 1);
}
}
public ItemsViewModel()
{
}
// Constructor taking in items from service and selected item string:
public ItemsViewModel(List<DropDownListItem> items, string selected)
{
_items = items;
SelectedItem = selected;
}
}
}
Step 2: Create a partial view in the Views folder that binds to the ItemsViewModel:
#model Models.ItemsViewModel
#Html.DropDownListFor(m => m.SelectedItem, Model.Items)
Step 3: In the appropriate controller (e.g. HomeController), place the child action that pulls the data from the service, the view model and the partial view together:
[ChildActionOnly]
public ActionResult DropDownList(string type, string selected)
{
// If you need to call different services based on the type (e.g. Country), then pass through "type" and base the call on that
var items = new ItemsViewModel(
(from g in _service.getTitles() select new DropDownListItem { Text = g.Text, Value = g.Value }).ToList(),
selected);
return PartialView("DropDownPartial", items);
}
Step 4: Drop this line of code into the view where you need the drop down list:
#Html.Action("DropDownList", "Home", new { selected = "2", type = "country" })
Note that selected and type are to be determined whichever way you see fit and are optional.
Hopefully this gives you some inspiration.