To show icons in dropdown in EPiServer CMS edit mode - episerver

I have requirement to show icons as well in dropdown in CMS edit mode as shown below. I'm using EPiServer version 11.15.1.0
In case, if you have any better suggestion/approach , Please advise.

I'm pasting answer here in case if anyone need in future:
define([
"dojo/_base/declare",
"dojo/_base/array",
"dojox/html/entities",
"epi-cms/contentediting/editors/SelectionEditor"
],
function (
declare,
array,
entities,
SelectionEditor
) {
return declare("alloy/editors/SelectionEditorHTML", [SelectionEditor], {
_setSelectionsAttr: function (newSelections) {
this.set("options", array.map(newSelections, function (item) {
let svghtml="<div class='svg_icon'><svg style='width:1.5rem;height:1.5rem'> <use xlink:href='/build/spritemap/demo.spritemap.svg#"+item.value +"'></use></svg></div>";
let html = entities.decode( "<div class='_drpmain'><div class='drptxt'>"+ item.text + "</div>") + entities.decode(svghtml)+"</div>";
return {
label: html,
value: item.value,
selected: item.value === this.value || !item.value && !this.value
};
}, this));
}
});
});
and
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SelectOneWithIconAttribute : Attribute, IMetadataAware
{
public virtual Type SelectionFactoryType { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
if (metadata is ExtendedMetadata extendedMetadata)
{
extendedMetadata.ClientEditingClass = "alloy/editors/SelectionEditorHTML";
extendedMetadata.SelectionFactoryType = SelectionFactoryType;
}
}
}
Once done, simply use attribute
[SelectOneWithIcon(SelectionFactoryType = typeof(IconSelectionFactory))]
[CultureSpecific]
public virtual string Icon1 { get; set; }

Related

How can I retrieve multiple checkbox values from checkboxes added dynamically in Blazor when using EditForm and/or #bind-Value?

I am trying to find out how to get checkbox values when checked in a form in Blazor.
I can see from this post (.NET Core Blazor: How to get the Checkbox value if it is checked? ), how to do it for a single value:
a solution to the above post gave me something like this:
<InputCheckbox #bind-Value="#b.myBool" checked="#(#b.myBool?"checked":null)" />CheckMe
and that works ok. I just create a class with a boolean property (say myBool) and declare an instance of the class with that property set to false. Then when the form is submitted, I have access to b.myBool, it is changed by the user.
However, I need to do it for multiple Checkboxes that will be added dynamically (the text next to the checkbox, here CheckMe[z], would also change but that's not an issue).
So, I figured a for/foreach loop with something like:
<InputCheckbox #bind-Value="#b.myBool[i]" checked="#(#b.myBool[i]?"checked":null)" />CheckMe[z]
and in the class, I'd just need to replace with a list property e.g.: List myBools.
Unfortunately this and lots of other variations I've tried don't work, for many many different reasons.
Thanks in advance for advice/suggestions/links.
Edit to my question (thanks Lex).
The latest error I receive is:
"ArgumentException: The provided expression contains a InstanceMethodCallExpression1 which is not supported. FieldIdentifier only supports simple member accessors (fields, properties) of an object."
That is after trying trying:
**<InputCheckbox #bind-Value="#e.myBools[i]" checked="#(#e.myBools[i]?"checked":null)" />**
and the #code:
**public class editFormModel
{
public List<bool> myBools { get; set; }
}
editFormModel e = new() {
myBools = new List<bool> { true, true, true, true},
};**
nb the angle brackets seem to disappear in this post but I hope it's clear what I mean.
Additional
Thanks for your answers.
Following on from that, I really want now to include an array of Values per Option and then one checkbox for each. So I figured just a nested loop and I could just adjust the EditFormModel to allow for a bool[] Values array. I tried with the below code and got the error:
ArgumentException: The provided expression contains a SimpleBinaryExpression which is not supported. FieldIdentifier only supports simple member accessors (fields, properties) of an object.
Any help would again be much appreciated. Apologies for not explaining entire task at hand from beginning but wasnt sure how far I would get.
#page "/counter"
<PageTitle>Counter</PageTitle>
<div>
<EditForm Model="this.e">
#for (int i = 0; i < this.e.Options.Count; i++)
{
{
<div>
#for (int j = 0; j < 1; j++)
{
<label>
<InputCheckbox #bind-`
`Value="this.e.Options[i].Values[j] " />
#*#option.Name*#
</label>
}
</div>
}
}
</EditForm>
</div>
<div>
Gives access to results like:
#e.Options[1].Values[0]
#e.Options[1].Values[1]
#e.Options[1].Values[0]
#e.Options[1].Values[1]
</div>
#code
{
public class EditFormModel
{
public List<Option> Options { get; set; }
}
public class Option
{
public string? Name { get; set; }
public bool[] Values { get; set; }
}
public EditFormModel e = new()
{
Options = new List<Option>
{
new()
{
Name = "Option 1",
Values = new bool[] {
true, true
}
},
new()
{
Name = "Option 2",
Values = new bool[] {
true, true,
}
}
}
};
}
Here's some code that should work - I just tried it in a small sample app. It's very contrived, but it does illustrate the general approach that you're using should be fine in theory.
Test.razor
#page "/test"
<div>
<EditForm Model="this.e">
#foreach (var option in this.e.Options)
{
<div>
<label>
<InputCheckbox #bind-Value="option.Value" />
#option.Name
</label>
</div>
}
</EditForm>
</div>
<div>
Options selected: #this.e.Options.Count(x => x.Value)
</div>
#code
{
public class EditFormModel
{
public List<Option> Options { get; set; }
}
public class Option
{
public string Name { get; set; }
public bool Value { get; set; }
}
private EditFormModel e = new()
{
Options = new List<Option>
{
new()
{
Name = "Option 1",
Value = true,
},
new()
{
Name = "Option 2",
Value = false,
},
new()
{
Name = "Option 3",
Value = false,
},
new()
{
Name = "Option 4",
Value = true,
},
},
};
}
According to these posts - [1],[2], you can't use an index to bind to a collection of primitive values because Blazor will have trouble tracking these values in EditContext. An index is, apparently, not a reliable tracker because a collection may be modified in various ways where an object at the beginning of the collection could end up elsewhere or be deleted.
So in order for this to work, you will need to create an object e.g.
public class CheckTracker{
public bool IsChecked {get;set;}
}
and change your collection to -
public class EditFormModel
{
public List<CheckTracker> Trackers { get; set; }
}
EditFormModel e = new() {
Trackers = new List<CheckTracker> { new CheckTracker{IsChecked:true}, new CheckTracker{IsChecked:true}, new CheckTracker{IsChecked:true}, new CheckTracker{IsChecked:true}}
};
Finally, your binding should look like this -
<InputCheckbox #bind-Value="#e.Trackers[i].IsChecked" checked="#(#e.Trackers[i].IsChecked? "checked" :null)" />

How to call Grpahql with .Net core from a React component using Axios?

I am new to graphql and trying to implement Graphql with dot net core using graphql-dotnet library.
We do not have a dedicated database in this application. The high level flow of the application is
Front End(React)
(Calls) > GraphQlController (.Net core)
(Calls) > Sales force api
Send data back to front end.
Graphql Setup.
public class GraphQLController : ControllerBase
{
private readonly IOptions<ApplicationConfiguration> _configuration;
public GraphQLController(IOptions<ApplicationConfiguration> config)
{
this._configuration = config;
}
public async Task<IActionResult> Post([FromBody] GraphQLQuery query)
{
var inputs = query.Variables.ToInputs();
var schema = new Schema()
{
Query = new OrderQuery(_configuration)
};
var result = await new DocumentExecuter().ExecuteAsync(_ =>
{
_.Schema = schema;
_.Query = query.Query;
_.OperationName = query.OperationName;
_.Inputs = inputs;
}).ConfigureAwait(false);
if (result.Errors?.Count > 0)
{
return BadRequest();
}
return Ok(result);
}
}
Query class
public class GraphQLQuery
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; }
public JObject Variables { get; set; }
}
Model Class which used for the de-serialization
public class OrderModel
{
public string Id { get; set; }
public string Name { get; set; }
}
Equivalent type in Graphql
public class OrderType : ObjectGraphType<OrderModel>
{
public OrderType()
{
Name = "Order";
Field(x => x.Id).Description("The ID of the order.");
Field(x => x.Name).Description("The name of the order");
}
}
The Query class to call the sales force service
public class OrderQuery : ObjectGraphType
{
public OrderQuery(IOptions<ApplicationConfiguration> config)
{
Field<OrderType>(
"Order",
arguments: new QueryArguments(
new QueryArgument<IdGraphType> { Name = "id" }),
resolve: context =>
{
var id = context.GetArgument<object>("id");
var service = new SalesForceService(config);
var data = service.GetAccountByAccountID(id.ToString());
return data;
});
}
}
The application compiles fine in visual studio. when i press f5 and run this in the browser. I get this response
http://localhost:61625/api/graphql
{"":["The input was not valid."]}
When i try to run in postman by passing the following parameters in the body
{
OperationName:"test",
NamedQuery: "Orders",
Query:{},
Variables:{id:"123"}
}
i get this response ""A non-empty request body is required."
Can some one explain to me how do you make a request to graphql end point and what values should be passed in the below parms in postman.
{
OperationName:
NamedQuery:
Query:,
Variables:
}
How do you make a similar call from react , We are using axios:.
like below example how are parameters set for the call.
doRestCall = (id) => {
const model = {
variable: id
};
const headers = {
'Content-Type': 'application/json'
}
Axios.post('http://localhost:49776/api/graphql', model, headers)
.then(result => {
debugger;
console.log(result);
});
console.log(this.state);
};
Many thanks for the help.
It appears you're trying to use "named queries" with the use of NamedQuery, which is a design pattern with GraphQL. That design pattern is implemented by having well known queries that are pre-defined and cached on the server. Looking at your Controller you do not have named queries implemented. You will need to do a regular GraphQL query.
This is what the JavaScript would look like:
{
query: "query MyOrderQuery($id: ID) { order(id: $id) { id name } }",
variables: {
id: "123"
}
}
This would be the JSON:
{
"query": "query MyOrderQuery($id: ID) { order(id: $id) { id name } }",
"variables": {
"id": "123"
}
}
See https://graphql-dotnet.github.io/docs/getting-started/variables
I also suggest to use Apollo with your React components.
https://www.apollographql.com/docs/react/essentials/get-started.html
https://www.apollographql.com/docs/react/essentials/get-started.html#request

Episerver 6 property with condition

This get; How do I adjust this get set to work in a class that inherits from TypedPageData (Episerver 6)
[PageTypeProperty(
EditCaption = "Caption",
HelpText = "Hint here",
Type = typeof(PropertyUrl),
Tab = typeof(ComposerTab))]
public virtual string Property1 {
get
{
if (String.IsNullOrWhiteSpace(Property1.ToString()))
return "http://www.blabla.com";
return "sdfsdf";
}
set { Property1 = value; }
}
At the moment it has no value.
Probably to do with how properties are defined in Epi? Thanks
You can do the following:
public virtual string Property1 {
get
{
if (String.IsNullOrWhiteSpace(this.GetPropertyValue(page => page.Property1)))
return "http://www.blabla.com";
return "sdfsdf";
}
set { this.SetPropertyValue(page => page.Property1, value); }
}
`

Breeze error: Unable to locate a 'Type' by the name: '' - call fetchMetadata first

I'm trying to save new item to my database using Breeze and web api.
Here is my model:
public class MuscleGroup
{
#region Properties
public int MuscleGroupId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
#endregion
#region Navigational properties
public virtual ICollection<Muscle> Muscles { get; set; }
public virtual ICollection<Exercise> Exercises { get; set; }
#endregion
}
Here is my API:
[BreezeController]
public class MuscleGroupController : ApiController
{
private readonly EFContextProvider<MadBarzDatabaseContext> _contextProvider =
new EFContextProvider<MadBarzDatabaseContext>();
// GET api/<controller>
[HttpGet]
public IQueryable<MuscleGroup> Get()
{
return _contextProvider.Context.MuscleGroups;
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
}
Here are parts of my dataService.js:
app.angularModule.service('muscleGroupService', function(breeze, logger) {
breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);
var mbservice = new breeze.DataService({
serviceName: "http://localhost:23758/api/MuscleGroup",
hasServerMetadata: false,
});
var manager = new breeze.EntityManager({ dataService: mbservice });
manager.enableSaveQueuing(true);
var removeItem = breeze.core.arrayRemoveItem;
var items = [];
var muscleGroupService =
{
getAll: getAll,
getSucceeded: getSucceeded,
getFailed : getFailed,
addItem: addItem,
// deleteItem: deleteItem,
// updateItem : updateItem,
};
return muscleGroupService;
//#region addItem
function addItem(initialValues) {
var item = manager.createEntity("MuscleGroup", initialValues);
saveChanges().fail(addFailed);
items.push(item);
return item;
function addFailed() {
removeItem(items, item);
}
}
//#endregion
//#region SaveChanges
function saveChanges() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
}
function saveSucceeded(saveResult) {
logger.success("Saved :D");
logger.log(saveResult);
}
function saveFailed(error) {
logger.error(error);
logger.log(error);
}
//#endregion
}
Here is part of my controller:
$scope.addNewItem = function() {
var newItem = muscleGroupService.addItem({ Id: 42, Name: $scope.Name, Description: "ho", ImageUrl: "hey"});
$scope.items.push(newItem);
};
And this is error I get:
Error: Unable to locate a 'Type' by the name: 'MuscleGroup'. Be sure to execute a query or call fetchMetadata first.
at proto._getEntityType (http://localhost:7122/Scripts/breeze.debug.js:6056:19)
at proto.getEntityType (http://localhost:7122/Scripts/breeze.debug.js:6047:21)
at Object.addItem (http://localhost:7122/Scripts/app/AdminMuscleGroup/MuscleGroupService.js:61:45)
at Object.$scope.addNewItem (http://localhost:7122/Scripts/app/AdminMuscleGroup/MuscleGroupController.js:16:42)
at http://localhost:7122/Scripts/angular/angular.js:6365:19
at http://localhost:7122/Scripts/angular/angular.js:12987:13
at Object.Scope.$eval (http://localhost:7122/Scripts/angular/angular.js:8057:28)
at Object.Scope.$apply (http://localhost:7122/Scripts/angular/angular.js:8137:23)
at HTMLButtonElement.<anonymous> (http://localhost:7122/Scripts/angular/angular.js:12986:17)
at HTMLButtonElement.jQuery.event.dispatch (http://localhost:7122/Scripts/jquery-1.8.2.js:3063:9) angular.js:5754
I query All Muslce groups before I try to add antoher.
PW Kad is giving you the clues.
When you wrote hasServerMetadata: false, you told Breeze not to get metadata from the server; you told Breeze that you would provide the metadata on the client. You aren't providing metadata on the client.
You're controller doesn't have a Metadata endpoint ... and couldn't offer such an endpoint AND ALSO be structured as the controller-per-type API controller that you seem determined to devise.
You're deviating from the standard Breeze productivity path while pursuing controller-per-type. That's fine ... after you have a little experience, know what you're doing, and know why you're doing it. The hasServerMetadata flag is true by default for a reason; you can't just set it false and expect everything to work.
I suggest that you back up, follow the Breeze guidance, understand it, then depart from that guidance incrementally as you discover solid, business reasons for doing so.

Build and load a tree with data from database with ExtJs

I'm trying to build a treepanel (or just a simple tree I just need it to work) and load it with data from database
I've been trying and trying and trying ..but cant do it.
Can someone show me how can I do this please?
My JSON:
{
{Title:'yahoo Website',adress:'www.yahoo.com',Description:'Serveur yahoo'},
{Title:'skype',adress:'skype.com',Description:'skype.com'},
{Title:'bing',adress:'www.bing.com',Description:'microsoft bing'},
{Title:'facebook',adress:'www.facebook.com',Description:'social network'},
{Title:'Google',adress:'Google.com',Description:'Google';},
{Title:'\' or 1=1--',adress:'\' or 1=1--',Description:'\' or 1=1--'}
]
My C# code:
public class Interact : JsonRpcHandler {
[JsonRpcMethod()]
public string ReadAssets() {
clsDBInteract objDBInteract = new clsDBInteract();
string result;
try {
result = objDBInteract.FetchAssetsJSON();
} catch (Exception ex) {
throw ex;
}
return result;
}
firstly look at this simple example. This tree have a store wich can read infromation from url by json scructure. You can write there http://youdomain.com/yourscript.php. At yourscript.php you have to read information from database, encode it to JSON and run echo your_json;
That's all.
P.S. json example
i solved this by creating my own type (no jayrock)
my tree model and store:
Ext.define('TreeModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'text' },
{ name: 'id' },
{ name: 'descr' }
]
});
window.TreeStore = Ext.create('Ext.data.TreeStore', {
model: 'TreeModel',
root: Ext.decode(obj.TreeToJson()),
proxy: {
type: 'ajax'
},
sorters: [{
property: 'leaf',
direction: 'ASC'
}, {
property: 'text',
direction: 'ASC'
}]
});
my class:
public class TreeItem
{
public string text { get; set; }
public int id { get; set; }
public string descr { get; set; }
public string expanded { get; set; }
public string leaf { get; set; }
public List<TreeItem> children { get; set; }
}
then i get my data and fill my tree like this
public string TreeToJson()
{
List<TreeItem> child = new List<TreeItem>();
for (int i = 0; i < n; i++)
{
child.Add(new TreeItem() { text = t.AssetTree()[i].Item1, id = t.AssetTree()[i].Item2, ip = t.AssetTree()[i].Item3, descr = t.AssetTree()[i].Item4, expanded = "false", leaf = "true" });
}
TreeItem tree = new TreeItem() { text = "my root", id = 0, expanded = "true", leaf = "false", children = child };
}
hope it helps someone

Resources