I would like to save in a Object property (my_json) a JSON List loaded from an external file.
With this code my_json properties is always equal to null :{
Thanks in advance for your help :)
#CustomTag('scaffold-toolsbar-element')
class MyCustomTag extends PolymerElement{
void click_menu_item(String label) {
shadowRoot.querySelector('#page_name').text = label;
}
MyCustomTag.created() : super.created(){
var menu_list = new MenuList('menu_items.json');
addElementToMenu(list_value){
var newElement = new Element.tag('core-item');
newElement.setAttribute("icon", list_value["icon"]);
newElement.setAttribute("label", list_value["label"]);
newElement.onClick.listen((e) => click_menu_item(list_value["label"]));
shadowRoot.querySelector('#core_menu_item').children.add(newElement);
};
menu_list.my_json.forEach(addElementToMenu);
}
}
class MenuList {
String path;
List my_json;
MenuList(String path) {
this.path = path;
var httpRequest = new HttpRequest();
httpRequest
..open('GET', path)
..onLoadEnd.listen((e) => requestComplete(httpRequest))
..send('');
}
requestComplete(HttpRequest request) {
// request.status is 200
// request.responseText is
// "[ {"icon": "settings", "label": "Signin", "main_page": "signin-element"}, {"icon": "home", "label": "About", "main_page": "about-page-element"} ]"
if (request.status == 200) {
this.my_json = JSON.decode(request.responseText);
}else{
this.my_json = null;
}
}
}
This is the code you require:
HttpRequest.request(path, responseType: 'json').then((HttpRequest request) {
var json = request.response;
});
Note that there is a bug in Dartium at the moment:
https://code.google.com/p/dart/issues/detail?id=20129
As a workaround you can omit the responseType parameter and use request.responseText.
Regards, Robert
Your MenuList class could look like
class MenuList {
String path;
List my_json;
static Future<MenuList> create(String path) {
return new MenuList()._load(path);
}
Future<MenuList>_load(String path) {
Completer completer = new Completer();
this.path = path;
var httpRequest = new HttpRequest();
httpRequest
..open('GET', path)
..onLoadEnd.listen((e) {
requestComplete(httpRequest);
completer.complete(this);
})
..send('');
return completer.future;
}
requestComplete(HttpRequest request) {
if (request.status == 200) {
this.my_json = JSON.decode(request.responseText);
}else{
this.my_json = null;
}
}
}
and the constructor of MyCustomTag like
void attached() {
super.attached();
var menu_list;
MenuList.create('menu_items.json')
.then((ml) {
menu_list = ml;
addElementToMenu(list_value){
var newElement = new Element.tag('core-item');
newElement.setAttribute("icon", list_value["icon"]);
newElement.setAttribute("label", list_value["label"]);
newElement.onClick.listen((e) => click_menu_item(list_value["label"]));
shadowRoot.querySelector('#core_menu_item').children.add(newElement);
};
menu_list.my_json.forEach(addElementToMenu);
});
}
I haven't actually tested this code but at leas the analyzer was satisfied.
The solution come from Robert. I try to read an Object property before the JSON List result is assigned to. So I have always a null property...
To avoid that, I add an optional parameter async to my HttpRequest.open like that: ..open('GET', path, async:false)
This is the final code.
#CustomTag('scaffold-toolsbar-element')
class MyCustomTag extends PolymerElement{
void click_menu_item(String label) {
shadowRoot.querySelector('#page_name').text = label;
}
MyCustomTag.created() : super.created(){
var menu_list = new MenuList('menu_items.json');
addElementToMenu(list_value){
var newElement = new Element.tag('core-item');
newElement.setAttribute("icon", list_value["icon"]);
newElement.setAttribute("label", list_value["label"]);
newElement.onClick.listen((e) => click_menu_item(list_value["label"]));
shadowRoot.querySelector('#core_menu_item').children.add(newElement);
};
menu_list.my_json.forEach(addElementToMenu);
}
}
class MenuList {
String path;
List my_json;
MenuList(String path) {
this.path = path;
var httpRequest = new HttpRequest();
httpRequest
..open('GET', path, async:false)
..onLoadEnd.listen((e) => requestComplete(httpRequest))
..send('');
}
requestComplete(HttpRequest request) {
if (request.status == 200) {
this.my_json = JSON.decode(request.responseText);
}else{
this.my_json = null;
}
}
}
Related
I declared protobuf message like this.
message request {
repeated sint32 Ids = 1;
}
and I tried to save this message in the form of json in DB.
Serialize worked well but when I deserialize it, 'Ids' field is empty.
What am I missing?
var requrest = new Request;
foreach(Int16 Id in Ids) {
request.Ids.Add(Id);
}
string SerializedReq = JsonSerializer.Serialize(request); // "{\"Ids\":[10,28]}"
var DeserializedReq = JsonSerializer.Deserialize<Request>(SerializedReq); // {{}} empty
You should implement a Converter and a ConverterFactory like this:
public class RepeatedFieldConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}
if (typeToConvert.GetGenericTypeDefinition() != typeof(RepeatedField<>))
{
return false;
}
return true;
}
public override JsonConverter CreateConverter(
Type type,
JsonSerializerOptions options)
{
Type itemType = type.GetGenericArguments()[0];
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
typeof(RepeatedFieldConverter<>).MakeGenericType(
new Type[] { itemType }),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: new object[] { options },
culture: null)!;
return converter;
}
private class RepeatedFieldConverter<TItem> :
JsonConverter<RepeatedField<TItem>>
{
private readonly JsonConverter<TItem> _valueConverter;
private readonly Type _valueType;
public RepeatedFieldConverter(JsonSerializerOptions options)
{
_valueConverter = (JsonConverter<TItem>)options
.GetConverter(typeof(TItem));
_valueType = typeof(TItem);
}
public override RepeatedField<TItem> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
{
throw new JsonException();
}
var repeatedField = new RepeatedField<TItem>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
{
return repeatedField;
}
TItem value = _valueConverter.Read(ref reader, _valueType, options)!;
repeatedField.Add(value);
}
throw new JsonException();
}
public override void Write(
Utf8JsonWriter writer,
RepeatedField<TItem> repatedField,
JsonSerializerOptions options)
{
writer.WriteStartArray();
foreach (TItem value in repatedField)
{
_valueConverter.Write(writer, value, options);
}
writer.WriteEndArray();
}
}
}
And use it like this:
var persons = new RepeatedField<Person>();
persons.Add(new Person()
{
Name = "Joe"
});
//Converter for serializing is not mandatory, James Newton-King supports this out of the box ;-)
var jsonPersons = System.Text.Json.JsonSerializer.Serialize(persons, new System.Text.Json.JsonSerializerOptions()
{
Converters = { new RepeatedFieldConverterFactory() }
});
var deserializedPersons = System.Text.Json.JsonSerializer.Deserialize<RepeatedField<Person>>(jsonPersons, new System.Text.Json.JsonSerializerOptions()
{
Converters = { new RepeatedFieldConverterFactory() }
});
I want to fetch data from the server, then parse json. When they are done, I want to navigate to another view.
void getServerData() async{
WebRequests ws = WebRequests('https://sampleurl');
Map<String, dynamic> map = await ws.getData();
mychamplist = map['mychampionships'];
mychamplist.forEach((f){
mychampionships.add(MyChampionships(
name: f['name'],
id: int.parse(f['id']),
numberOfPlayers: int.parse(f['nofplayers']),
));
});
Navigator
.of(context)
.pushReplacement(new MaterialPageRoute(builder: (BuildContext context) {
return FantasyNbi();
}));
}
It navigates to the FantasyNbi class before the previous code finished.
How could it do in proper way?
I do have a example class for you that you could use:
class API {
static Future getData(String url) {
return http.get('api link' + url);
}
static Future<List<BasicDiskInfo>> fetchAllDisks() async {
final response = await getData('disk');
if (response.statusCode == 200) {
Iterable list = json.decode(response.body);
List<BasicDiskInfo> disks =
list.map((model) => BasicDiskInfo.fromJson(model)).toList();
return disks;
} else {
throw Exception('Failed to load disks');
}
}
static Future<Disk> fetchDisk(int id) async {
final response = await getData('disk/' + id.toString());
if (response.statusCode == 200) {
return Disk.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load disk');
}
}
}
class Disk {
int id;
String name;
String volumeLable;
bool isReady;
String driveType;
String driveFormat;
int totalSize;
int totalFreeSpace;
int availableFreeSpace;
Disk(
{this.id,
this.name,
this.volumeLable,
this.isReady,
this.driveType,
this.driveFormat,
this.totalSize,
this.totalFreeSpace,
this.availableFreeSpace});
factory Disk.fromJson(Map<String, dynamic> json) {
return Disk(
id: json['id'],
name: json['name'],
volumeLable: json['volumeLable'],
isReady: json['isReady'],
driveType: json['driveType'],
driveFormat: json['driveFormat'],
totalSize: json['totalSize'],
totalFreeSpace: json['totalFreeSpace'],
availableFreeSpace: json['availableFreeSpace']);
}
}
And to get the data I can do this:
var data = await API.fetchAllDisks();
// or
API.fetchAllDisks().then((response) => {/* do something */})
I'm trying to load a database in a controller(asp.net MVC) and then using the aurelia fetch client to load the data from the controller into the view but no data is being fetched by aurelia(view tables are empty which isn't the result when manually declaring an array of inputs)
EmployeesController(Controller)
using Microsoft.AspNetCore.Mvc;
using SPAproject.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SPAproject.Controllers
{
[Route("api/[controller]")]
public class EmployeesController : Controller
{
private readonly EmployeesDbContext context;
public EmployeesController(EmployeesDbContext context)
{
this.context = context;
}
[HttpGet]
public IEnumerable<Employee> Get()
{
return context.Employees.ToList();
}
}
}
emp-api(where I'm fetching the data)
import { HttpClient } from 'aurelia-fetch-client';
import { inject } from 'aurelia-framework';
let latency = 200;
let id = 0;
#inject(HttpClient)
export class EmpAPI {
isRequesting = false;
constructor(http) {
this.http = http;
this.http.configure(config =>
config.useStandardConfiguration()
.withDefaults({
mode: 'cors'
}
)
);
this.employees = [];
http.fetch('/api/Employees')
.then(x => x.json())
.then(employees => this.employees = employees);
}
getEmployeeList() {
this.isRequesting = true;
return new Promise(resolve => {
setTimeout(() => {
let results = this.employees.map(x => {
return {
id: x.id,
firstName: x.firstName,
lastName: x.lastName,
email: x.email
}
});
resolve(results);
this.isRequesting = false;
}, latency);
});
}
getEmployeeDetails(id) {
this.isRequesting = true;
return new Promise(resolve => {
setTimeout(() => {
let found = this.employees.filter(x => x.id == id)[0];
resolve(JSON.parse(JSON.stringify(found)));
this.isRequesting = false;
}, latency);
});
}
saveEmployee(employee) {
this.isRequesting = true;
return new Promise(resolve => {
setTimeout(() => {
let instance = JSON.parse(JSON.stringify(employee));
let found = this.employees.filter(x => x.id == employee.id)[0];
if (found) {
let index = this.employees.indexOf(found);
this.employees[index] = instance;
} else {
instance.id = getId();
this.employees.push(instance);
}
this.isRequesting = false;
resolve(instance);
}, latency);
});
}
}
employee-list(where I'm trying to get the data to from API)
import { EventAggregator } from 'aurelia-event-aggregator';
import { EmpAPI } from 'emp-api';
import { inject } from 'aurelia-framework';
import { EmployeeUpdated } from 'employeeUpdated';
import { EmployeeViewed } from 'employeeViewed';
#inject(EmpAPI, EventAggregator)
export class EmployeeList {
constructor(api, ea) {
this.api = api;
this.ea = ea;
this.employees = [];
ea.subscribe(EmployeeViewed, msg => this.select(msg.employee));
ea.subscribe(EmployeeUpdated, msg => {
let id = msg.employee.id;
let found = this.employees.find(x => x.id == id);
Object.assign(found, msg.employee);
});
}
created() {
this.api.getEmployeeList().then(employees => this.employees = employees);
}
select(employee) {
this.selectedId = employee.id;
return true;
}
}
Either you can use one of their wrapper methods like http.get('/api/Employees') in this case.
If you want to use fetch, then you need to specify a method http.fetch('/api/Employees', {method: 'GET'})
The issue you see is because you didn't wait for your data to return. By the time your employee-list element is created, employees property in your EmpAPI is still undefined, as your data fetching call hasn't been returned yet.
I see that you have 200 ms latency to prevent this from happening, but sometimes maybe that is not enough (I suspect this). Maybe you can try a different latency if you want to keep this strategy? There are different ways to do this, like only resolve the getEmployeeList() promise only when the data fetching call has already been resolved, delay the call further, wait for the call etc.
When Saving schedule to calendar it must auto update the activity logs on my notification bar in my Home Controller. It saves the data but only show when notification bar is refreshed. It seems that Hub is not starting when saved.
CalendarController.cs
[HttpPost]
public JsonResult SaveSchedule(Schedule s)
{
var userid = User.Identity.GetUserId();
var profile = _context.Profiles.Single(p => p.Id == userid);
var status = false;
if (s.Schedule_ID > 0)
{
//Update
var v = _context.Schedules.Where(a => a.Schedule_ID == s.Schedule_ID).FirstOrDefault();
if (v != null)
{
v.Shift = s.Shift;
}
}
var activitylog = new ActivityLog
{
UserId = userid,
LogDate = DateTime.Now,
Activity = ActivityHelper.GetActivityLog(4, profile.FirstName)
};
// save to data and must be shown on notification bar
_context.ActivityLogs.Add(activitylog);
_context.SaveChanges();
ActivityHub.StartLogging();
status = true;
return new JsonResult { Data = new { status = status } };
}
HomeController.cs
public JsonResult GetLogs()
{
return Json(ActivityHelper.GetActivityLogs(), JsonRequestBehavior.AllowGet);
}
ActivityHub.cs
public class ActivityHub : Hub
{
public static void StartLogging()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
//calls the signalR client part to execute javascript method
context.Clients.All.displayLog();
}
}
My CSHTML
<script>
$(function () {
var activityFromHub = $.connection.activityHub;
$.connection.hub.start().done(function () {
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
function FetchLogs() {
$.ajax({
type: 'GET',
url: '/Home/GetLogs',
datatype: 'json',
success: function (data) {
$("#logs tr").remove();
data = $.parseJSON(data);
if (data.length > 0) {
.... do some append here
}
},
error: function (error) {
alert("error");
}
});
}
});
</script>
ActivityHelper.cs
static readonly string connString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
public static class ActivityHelper
{
public static string GetActivityLogs()
{
string sqlCommand = #"my select query here";
try
{
var messages = new List<ActivityLog>();
using(var connection = new SqlConnection(connString))
{
connection.Open();
using (SqlConnection con = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(sqlCommand, con);
if(con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
cmd.Notification = null;
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
messages.Add(item: new ActivityLog
{
Activity = reader["Activity"] != DBNull.Value ? (string)reader["Activity"] : "",
LogDate = (DateTime)reader["LogDate"]
});
}
}
}
var jsonSerialiser = new JavaScriptSerializer();
var json = jsonSerialiser.Serialize(messages);
return json;
}
catch(Exception ex)
{
throw;
}
}
public static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= dependency_OnChange;
var activityHub = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
GetActivityLogs();
}
}
}
FIRST METHOD
First Solution change your javascript code like this. If this not works move to the second method:
$(function () {
var activityFromHub = $.connection.ActivityHub;
$.connection.hub.start().done(function () {
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
});
SECOND METHOD:
Each client connecting to a hub passes a unique connection id. You can retrieve this value in the Context.ConnectionId property of the hub context. And i found there is nothing happening like this. You may try this solution.
I think the simplest solution for your question is to use groups.
http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups
Your hub class would contain methods to join a group:
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
and your hub will be look like this:
public static void StartLogging(string groupName)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
context.Clients.Group(groupName).displayLog();
//calls the signalR client part to execute javascript method
//context.Clients.All.displayLog();
}
And change your javascript as like this:
$(function () {
var activityFromHub = $.connection.ActivityHub;
$.connection.hub.start().done(function () {
activityFromHub.server.joinGroup("Group1");
activityFromHub.server.StartLogging("Group1");
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
});
I hope this will resolve your issue. If you are still facing issue. Please leave comments. Thank you.
On ASP.NET MVC, I try to write an async Controller action with the old asynchronous programming model (actually, it is the current one, new one is still a CTP).
Here, I am trying to run 4 operations in parallel and it worked great. Here is the complete code:
public class SampleController : AsyncController {
public void IndexAsync() {
AsyncManager.OutstandingOperations.Increment(4);
var task1 = Task<string>.Factory.StartNew(() => {
return GetReponse1();
});
var task2 = Task<string>.Factory.StartNew(() => {
return GetResponse2();
});
var task3 = Task<string>.Factory.StartNew(() => {
return GetResponse3();
});
var task4 = Task<string>.Factory.StartNew(() => {
return GetResponse4();
});
task1.ContinueWith(t => {
AsyncManager.Parameters["headers1"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task2.ContinueWith(t => {
AsyncManager.Parameters["headers2"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task3.ContinueWith(t => {
AsyncManager.Parameters["headers3"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task4.ContinueWith(t => {
AsyncManager.Parameters["headers4"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task3.ContinueWith(t => {
AsyncManager.OutstandingOperations.Decrement();
}, TaskContinuationOptions.OnlyOnFaulted);
}
public ActionResult IndexCompleted(string headers1, string headers2, string headers3, string headers4) {
ViewBag.Headers = string.Join("<br/><br/>", headers1, headers2, headers3, headers4);
return View();
}
public ActionResult Index2() {
ViewBag.Headers = string.Join("<br/><br/>", GetReponse1(), GetResponse2(), GetResponse3(), GetResponse4());
return View();
}
}
And these below ones are the methods that the async operations are running:
string GetReponse1() {
var req = (HttpWebRequest)WebRequest.Create("http://www.twitter.com");
req.Method = "HEAD";
var resp = (HttpWebResponse)req.GetResponse();
return FormatHeaders(resp.Headers);
}
string GetResponse2() {
var req2 = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com");
req2.Method = "HEAD";
var resp2 = (HttpWebResponse)req2.GetResponse();
return FormatHeaders(resp2.Headers);
}
string GetResponse3() {
var req = (HttpWebRequest)WebRequest.Create("http://google.com");
req.Method = "HEAD";
var resp = (HttpWebResponse)req.GetResponse();
return FormatHeaders(resp.Headers);
}
string GetResponse4() {
var req = (HttpWebRequest)WebRequest.Create("http://github.com");
req.Method = "HEAD";
var resp = (HttpWebResponse)req.GetResponse();
return FormatHeaders(resp.Headers);
}
private static string FormatHeaders(WebHeaderCollection headers) {
var headerStrings = from header in headers.Keys.Cast<string>()
select string.Format("{0}: {1}", header, headers[header]);
return string.Join("<br />", headerStrings.ToArray());
}
I have also Index2 method here which is synchronous and does the same thing.
I compare two operation execution times and there is major difference (approx. 2 seconds)
But I think I am missing lots of things here (exception handling, timeouts, etc). I only implement the exception handling on task3 but I don't think it is the right way of doing that.
What is the healthiest way of handling exceptions for this kind of operations?
Before accessing the result of a task you should test whether it completed successfully. If there was an error you should not try to access the results but instead do something with the exception:
task1.ContinueWith(t =>
{
if (!t.IsFaulted)
{
AsyncManager.Parameters["headers1"] = t.Result;
}
else if (t.IsFaulted && t.Exception != null)
{
AsyncManager.Parameters["error"] = t.Exception;
}
AsyncManager.OutstandingOperations.Decrement();
});