I am new in xamarin. I am getting difficulty to bind my List in xamarin CollectionView.
I get the bellow json from API
{
"OK": 200,
"status": "success",
"data": [
{
"Category": "Category 1",
"List": [
{
"SubItem": "The A1"
},
{
"SubItem": "The A2"
}
]
},
{
"Category": "Category 2",
"List": [
{
"SubItem": "The C1 sub"
},
{
"SubItem": "The C2 sub"
}
]
}
]
}
You should have two models and a viewModel like this in your project:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new viewModel();
}
public class subModel
{
public string SubItem { get; set; }
}
public class Data
{
public string Category { get; set; }
public IList<subModel> List { get; set; }
}
public class viewModel {
public ObservableCollection<Data> items = new ObservableCollection<Data>();
public viewModel() {
}
}
In the xaml, bind to a grouped CollectionView:
<CollectionView ItemsSource="{Binding items}"
IsGrouped="true">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding SubItem}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.GroupHeaderTemplate>
<DataTemplate>
<Label Text="{Binding Category}" />
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
</CollectionView>
Related
I have my workflow triggered on a signal like so:
public async Task<IActionResult> StartApprovalProcess([FromBody] long requestId)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// Get data object
var payload = await _mainService.GetBudgetReleaseRequestPayload(requestId);
var input = new Variables();
input.SetVariable("Payload", payload);
// Signal the workflow to start
await _workflowInvoker.TriggerSignalAsync("StartApprovalPhase", input);
return Ok("BRR registered");
}
Here is my Payload class:
public class BudgetReleaseRequestApprovalPhasePayloadModel
{
public BudgetReleaseRequestApprovalPhasePayloadModel(BudgetReleaseRequestApprovalPhasePayloadDto model)
{
Id = model.Id;
Description = model.Description;
Amount = model.Amount;
RequesterId = model.RequesterId;
SubmissionDate = model.SubmissionDate;
CostCenterName = model.CostCenterName;
ExpenseTypeName = model.ExpenseTypeName;
RequestTypeName = model.RequestTypeName;
AccountCode = model.AccountCode;
AccountName = model.AccountName;
BpsReferenceNumber = model.BpsReferenceNumber;
ApproversList = new List<BudgetReleaseRequestApproverViewModel>();
foreach (var budgetReleaseRequestApprover in model.ApproversList)
{
ApproversList.Add(new BudgetReleaseRequestApproverViewModel(budgetReleaseRequestApprover));
}
}
public long Id { get; set; }
public string Description { get; set; }
public decimal Amount { get; set; }
public string RequesterId { get; set; }
public DateTime SubmissionDate { get; set; }
public string CostCenterName { get; set; }
public string ExpenseTypeName { get; set; }
public string RequestTypeName { get; set; }
public string AccountCode { get; set; }
public string AccountName { get; set; }
public string BpsReferenceNumber { get; set; }
public string AmountFormatted => $"{Amount:N2} AED";
public string DateFormatted => $"{SubmissionDate:dd-MMM-yyyy}";
public string CostCenterAndType => $"{CostCenterName}/{ExpenseTypeName}";
public string AccountDetail => $"{AccountCode} - {AccountName}";
public int ApproversCount => ApproversList.Count;
public IList<BudgetReleaseRequestApproverViewModel> ApproversList { get; set; }
}
And here is the class that acts as a collection:
public class BudgetReleaseRequestApproverViewModel
{
public BudgetReleaseRequestApproverViewModel(BudgetReleaseRequestApprover model)
{
RequestId = model.RequestId;
RequestApproverId = model.RequestApproverId;
ApproverId = model.ApproverId;
RequesterId = model.RequesterId;
ApproverSequence = model.ApproverSequence;
ActionId = model.ActionId;
RequestActionId = model.RequestActionId;
}
public long RequestId { get; set; }
public byte RequestApproverId { get; set; }
public string ApproverId { get; set; }
public string RequesterId { get; set; }
public byte ApproverSequence { get; set; }
public Guid? ActionId { get; set; }
public byte? RequestActionId { get; set; }
}
I followed the main guide (https://sipkeschoorstra.medium.com/building-workflow-driven-net-core-applications-with-elsa-139523aa4c50) and know that we need to implement a handler in order to have Liquid Expressions within workflow for both these models:
public class LiquidConfigurationHandler : INotificationHandler<EvaluatingLiquidExpression>
{
public Task Handle(EvaluatingLiquidExpression notification, CancellationToken cancellationToken)
{
var context = notification.TemplateContext;
context.MemberAccessStrategy.Register<BudgetReleaseRequestApprovalPhasePayloadModel>();
context.MemberAccessStrategy.Register<BudgetReleaseRequestApproverViewModel>();
return Task.CompletedTask;
}
}
Here's my test workflow:
{
"activities": [{
"id": "abc63216-76e7-42b2-ab7b-5cdb6bbc3ed9",
"type": "Signaled",
"left": 122,
"top": 365,
"state": {
"signal": {
"expression": "StartApprovalPhase",
"syntax": "Literal"
},
"name": "",
"title": "Signal: Start Approval Phase",
"description": "Trigger the workflow when this signal is received."
},
"blocking": false,
"executed": false,
"faulted": false
}, {
"id": "ac7669d6-b7e6-4139-825e-5f2b9c1dbdb8",
"type": "SendEmail",
"left": 553,
"top": 379,
"state": {
"from": {
"expression": "my.email#acme.co",
"syntax": "Literal"
},
"to": {
"expression": "my.email#acme.co",
"syntax": "Literal"
},
"subject": {
"expression": "Workflow Testing",
"syntax": "Literal"
},
"body": {
"expression": "<p>BRR #{{ Input.Payload.Id }}</p>\r\n<p>Name: {{ Input.Payload.Description }}</p>\r\n<p>Amount: {{ Input.Payload.AmountFormatted }}</p>\r\n<p>Date: {{ Input.Payload.DateFormatted }}</p>\r\n<br />\r\n<p>Approvers: {{ Input.Payload.ApproversCount }}</p>",
"syntax": "Liquid"
},
"name": "",
"title": "Email: Test",
"description": ""
},
"blocking": false,
"executed": false,
"faulted": false
}, {
"id": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"type": "ForEach",
"left": 867,
"top": 474,
"state": {
"collectionExpression": {
"expression": "{{ Input.Payload.ApproversList }}",
"syntax": "Liquid"
},
"iteratorName": "",
"name": "",
"title": "",
"description": ""
},
"blocking": false,
"executed": false,
"faulted": false
}, {
"id": "7966b931-f683-4b81-aad4-ad0f6c628191",
"type": "SendEmail",
"left": 1042,
"top": 675,
"state": {
"from": {
"expression": "my.email#acme.co",
"syntax": "Literal"
},
"to": {
"expression": "my.email#acme.co",
"syntax": "Literal"
},
"subject": {
"expression": "Looping #",
"syntax": "Literal"
},
"body": {
"expression": "Loop Details",
"syntax": "Literal"
},
"name": "",
"title": "",
"description": ""
},
"blocking": false,
"executed": false,
"faulted": false
}, {
"id": "5f246eda-271d-46ed-8efe-df0f26d542be",
"type": "SendEmail",
"left": 1163,
"top": 325,
"state": {
"name": "",
"from": {
"expression": "my.email#acme.co",
"syntax": "Literal"
},
"to": {
"expression": "my.email#acme.co",
"syntax": "Literal"
},
"subject": {
"expression": "Loop Over",
"syntax": "Literal"
},
"body": {
"expression": "Loop Finished",
"syntax": "Literal"
},
"title": "",
"description": ""
},
"blocking": false,
"executed": false,
"faulted": false
}
],
"connections": [{
"sourceActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"destinationActivityId": "5f246eda-271d-46ed-8efe-df0f26d542be",
"outcome": "Done"
}, {
"sourceActivityId": "abc63216-76e7-42b2-ab7b-5cdb6bbc3ed9",
"destinationActivityId": "ac7669d6-b7e6-4139-825e-5f2b9c1dbdb8",
"outcome": "Done"
}, {
"sourceActivityId": "ac7669d6-b7e6-4139-825e-5f2b9c1dbdb8",
"destinationActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"outcome": "Done"
}, {
"sourceActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"destinationActivityId": "7966b931-f683-4b81-aad4-ad0f6c628191",
"outcome": "Iterate"
}, {
"sourceActivityId": "7966b931-f683-4b81-aad4-ad0f6c628191",
"destinationActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"outcome": "Done"
}
]
}
Visual:
Here are my results:
Signal: Works
First Email: Works:
ForEach Fails and I catch this in debug:
fail: Elsa.Expressions.WorkflowExpressionEvaluator[0]
Error while evaluating JavaScript expression "{{ Input.Payload.ApproversList }}". Message: Input is not defined
ReferenceError: Input is not defined
fail: Elsa.Services.ActivityInvoker[0]
Error while invoking activity 2efcffa9-8e18-45cf-aac8-fcfdc8846df8 of workflow de8e12d4645e4480abccbbe562b48448
Elsa.Exceptions.WorkflowException: Error while evaluating JavaScript expression "{{ Input.Payload.ApproversList }}". Message: Input is not defined
---> ReferenceError: Input is not defined
--- End of inner exception stack trace ---
at Elsa.Expressions.WorkflowExpressionEvaluator.EvaluateAsync(IWorkflowExpression expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken)
at Elsa.Extensions.WorkflowExpressionEvaluatorExtensions.EvaluateAsync[T](IWorkflowExpressionEvaluator evaluator, IWorkflowExpression1 expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) at Elsa.Activities.ControlFlow.Activities.ForEach.OnExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) at Elsa.Services.ActivityInvoker.InvokeAsync(WorkflowExecutionContext workflowContext, IActivity activity, Func2 invokeAction)
fail: Elsa.Services.WorkflowInvoker[0]
IWorkflowEventHandler thrown from Elsa.WorkflowEventHandlers.PersistenceWorkflowEventHandler by DbUpdateException
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Engine' with type 'Jint.Engine'. Path 'Exception.InnerException.Error.Engine.Global'.
I need to iterate a BudgetReleaseRequestApproverViewModel, send email, wait action, repeat but I cannot figure out the Loop.
This answer is based on my comments provided on the GitHub issue which is a repeat of the OP's question. I'm providing the below for completeness' sake.
Try using the input function for the ForEach activity (make sure that the selected syntax is JavaScript):
input('PayLoad').ApproverList
This will get the input named "PayLoad".
I don't know if Liquid is supposed to work. From a UX point of view, we should either make sure it does, or not even allow that option if it doesn't.
What I want to do (I think) is get the store.id from itemTap event and pass it along with the .navigate() so I can use it to fetch the correct data for the detail page, however I can't figure out how to get the tapped item.
I've got a list-page.xml:
<Page loaded="loaded" xmlns:lv="nativescript-ui-listview">
<ActionBar title="Bars"></ActionBar>
<GridLayout>
<ListView items="{{ storeList }}" row="1" itemTap="showDetail">
<ListView.itemTemplate >
<GridLayout class="grocery-list-item" >
<Label class="p-15" text="{{ name }}" />
</GridLayout>
</ListView.itemTemplate>
</ListView>
</GridLayout>
and list-page.js:
var dialogsModule = require("tns-core-modules/ui/dialogs");
var observableModule = require("tns-core-modules/data/observable");
var ObservableArray = require("tns-core-modules/data/observable-array").ObservableArray;
var page;
var StoreListViewModel = require("../shared/view-models/store-list-view-model");
var frameModule = require("tns-core-modules/ui/frame");
var storeList = new StoreListViewModel([]);
var pageData = new observableModule.fromObject({
storeList:storeList
});
exports.loaded = function (args) {
page = args.object;
page.bindingContext = pageData;
sindex = args.object.bindingContext;
storeList.empty();
storeList.load();
};
exports.showDetail = function() {
frameModule.topmost().navigate("views/detail/detail-page");
};
The storeList comes from my api and successfully sends back a list of stores which are rendered by the listview.
I've looked at a dozen other tutorials/questions, and several seem to mention getting it from either the args.index or args.object.bindingContext, but when I console.log(args.index) from the showDetail function, it's undefined. console.log(args.object.bindingContext) gives me a bunch of data, but it's identical regardless which row I click....
CONSOLE LOG file:///app/bundle.js:359:12: {
"_observers": {
"propertyChange": [
{}
]
},
"_map": {
"storeList": {
"_observers": {
"change": [
{}
]
},
"_array": [
{
"name": "Hipster's Coffee",
"address": "2200 Broadway, Oakland, CA 94612",
"id": 2
},
{
"name": "Suzy's Stationary",
"address": "630 Divisadero St., San Francisco, CA 94117",
"id": 3
}
],
"_addArgs": {
"eventName": "change",
"object": "[Circular]",
"action": "add",
"index": 1,
"removed": [],
"addedCount": 1
},
"_deleteArgs": {
"eventName": "change",
"object": "[Circular]",
"action": "delete",
"index": null,
"removed": null,
"addedCount": 0
}
}
},
"storeList": "[Circular]"
}
I'm very new to NativeScript, so obviously I'm missing something simple here.
<ListView items="{{ storeList }}" row="1" itemTap="{{showDetail}}">
Look at the sample playground here.
The function showDetail needs to access the received arguments. Just change the following in your code-behind file:
exports.showDetail = function(args) {
console.log(args); // Now there are arguments received from the itemTap event
frameModule.topmost().navigate("views/detail/detail-page");
};
Eventually figured out the proper way to extract the item details from the args:
var item = args.view.bindingContext;
and then I could access item.name, item.id, etc. and pass it through like so:
var navigationOptions={
moduleName:'views/detail/detail-page',
context:{
store_id: item.id
}
}
frameModule.topmost().navigate(navigationOptions);
I need to filter my data.but i don't know how to use Linq.
The ClassRoom Model
public class ClassRoom
{
public int RoomID { get; set; }
public string Name { get; set; }
public List<Student> Students { get; set; }
}
The Student Model
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public int StyleId {get;set;}
}
Example JSON DATA
var json=
[
{
"RoomID": 1,
"Name": "A Class",
"Students": [{
"StudentId": 1,
"Name":"Charlie",
"StyleId":"1"
},
{
"StudentId": 2,
"Name":"Tom",
"StyleId":"2"
}
]
},
{
"RoomID": 2,
"Name": "B Class",
"Students": [{
"StudentId": 3,
"Name":"ALLEN",
"StyleId":"2"
},
{
"StudentId": 4,
"Name":"Jeremy",
"StyleId":"2"
},
{
"StudentId": 5,
"Name":"Curry",
"StyleId":"3"
}
]
}
]
If i want get StyleID equals "2", and below is expected a answer.
var json =
[
{
"RoomID": 1,
"Name": "A Class",
"Students": [
{
"StudentId": 2,
"Name":"Tom",
"StyleId":"2"
}
]
},
{
"RoomID": 2,
"Name": "B Class",
"Students": [{
"StudentId": 3,
"Name":"ALLEN",
"StyleId":"2"
},
{
"StudentId": 4,
"Name":"Jeremy",
"StyleId":"2"
}
]
}
]
Here is how i filer, but not correct. how can i get expected data?
json.Select( v => v.Students.where( i => i.StyleId == "2") )
thanks.
Try linq query as below.
var result = json.Where(v => v.Students.Any(y => y.StyleId == "2"))
.Select(v => new ClassRoom() {
RoomID = v.RoomID,
Name = v.Name,
Students = v.Students.Where(y => y.StyleId == "2")
});
I am using RadDataForm in my Nativescript Angular project and when I try to use "MultilineText" it doesn't work on Android, it just appears as a normal "Text" box. I am not able to enter multiple lines. Works fine on iOS.
add-store.model.ts
export class AddStore {
public name: string;
public description: string;
constructor(name: string, description: string,) {
this.name = name;
this.description = description;
}
}
add-store-metadata-validation.json
{
"isReadOnly": false,
"commitMode": "immediate",
"validationMode": "immediate",
"propertyAnnotations":
[
{
"name": "name",
"displayName": "Store Name",
"index": 1,
"editor": "Text"
},
{
"name": "description",
"displayName": "Description",
"index": 2,
"editor": "MultilineText"
}
]
}
add-store.component.ts
import { Component, AfterViewInit } from "#angular/core";
import { RouterExtensions } from "nativescript-angular/router";
import { UtilitiesService } from "~/services/utils.service";
import { AddStore } from "./add-store.model";
#Component({
selector: "add-store",
moduleId: module.id,
templateUrl: "./add-store.component.html"
})
export class AddStoreComponent implements AfterViewInit {
store: AddStore;
metadata;
constructor(private router:RouterExtensions) {
this.store = new AddStore("test name" , "test\nsdfgfsdf");
this.metadata = require("./add-store-metadata-validation.json");
}
ngAfterViewInit(): void {
}
goBack() {
this.router.back();
}
}
add-store.component.html
<ActionBar class="action-bar" title="Store Setup">
<NavigationButton android.systemIcon="ic_menu_back" (tap)="goBack()"></NavigationButton>
</ActionBar>
<GridLayout rows="*">
<StackLayout row="0" class="page page-content" height="100%">
<RadDataForm [source]="store" [metadata]="metadata"></RadDataForm>
</StackLayout>
</GridLayout>
It looks like a issue with plugin, you may report same in the feedback repo.
As a workaround you may use TKPropertyEditor directive and define the editor type.
I have the following classes in my application:
public class Person
{
public int Id { get; set; }
public Guid PublicKey { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public DateTime Date { get; set; }
public virtual Person Person { get; set; }
public int PersonId { get; set; }
}
and the following API controller that just gets a list of all the people (there are always 2, and they always have 3 orders each):
[Route("api/[controller]")]
public class PeopleController : Controller
{
// GET api/people
[HttpGet]
public List<Person> Get()
{
PeopleService people = new PeopleService();
return people.GetAllPeople();
}
}
I should point out that I had issues from the start with the navigation property on the Order class referring back to the Person that owns it, as the Json.NET formatter doesn't like this out of the box and you have to configure it to ignore reference loops. I have done this in the Startup.cs like so:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(o =>
{
o.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.None;
o.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
services.AddSwaggerGen();
}
This works great, and when I make a request I get the following response:
[
{
"orders": [
{
"id": 1,
"date": "2016-10-26T17:16:35.21",
"personId": 1
},
{
"id": 2,
"date": "2016-10-26T17:16:35.21",
"personId": 1
}
],
"id": 1,
"publicKey": "b6a7c21c-86d8-4bb9-9a05-bd394e6ed0c9",
"firstName": "Lauren",
"lastName": "Phillips"
},
{
"orders": [
{
"id": 3,
"date": "2016-10-26T17:16:35.21",
"personId": 2
},
{
"id": 4,
"date": "2016-10-26T17:16:35.21",
"personId": 2
}
],
"id": 2,
"publicKey": "8b5a90b4-a9a2-4a0e-96dd-529962972456",
"firstName": "Robert",
"lastName": "West"
}
]
I am using Swashbuckle to generate Swagger docs for my API. The "Example Value" that is generated by Swashbuckle/Swagger seems to be including the Person again within each order:
[
{
"id": 0,
"publicKey": "string",
"firstName": "string",
"lastName": "string",
"orders": [
{
"id": 0,
"date": "2016-10-27T14:19:52.437Z",
"person": {
"id": 0,
"publicKey": "string",
"firstName": "string",
"lastName": "string",
"orders": [
{}
]
},
"personId": 0
}
]
}
]
I don't want people consuming the API to expect the person to be included again in each order, especially as that isn't what you actually get because I configured it to ignore reference loops above. I imagine that this issue is related to the reference loop, but I am not sure. Does anyone know how to fix this?
Probably, you can use the attribute - [JsonIgnore] to not show that up in the swagger documentation