I've created custom treeview field - multiselect treeview.
This field inherited from Sitecore.Shell.Applications.ContentEditor.TreeList with overridden method Add():
public class MultiselectTreeList : TreeList
{
protected new virtual void Add()
{
bool alert = true;
if (this.Disabled) return;
string viewStateString = this.GetViewStateString("ID");
var treeviewEx = this.FindControl(viewStateString + "_all") as TreeviewEx;
Assert.IsNotNull(treeviewEx, typeof (DataTreeview));
var listbox = this.FindControl(viewStateString + "_selected") as Listbox;
Assert.IsNotNull(listbox, typeof (Listbox));
if (treeviewEx == null)
{
SheerResponse.Alert("TreeviewEx control not found..", new string[0]);
}
else
{
Item[] selectionItems = treeviewEx.GetSelectedItems();
if (selectionItems == null)
{
SheerResponse.Alert("Select an item in the Content Tree.", new string[0]);
}
else
{
foreach (Item selectionItem in selectionItems)
{
if (this.HasExcludeTemplateForSelection(selectionItem)) return;
if (this.IsDeniedMultipleSelection(selectionItem, listbox))
{
if (alert)
{
SheerResponse.Alert("You cannot select the same item twice.", new string[0]);
alert = false;
}
}
else
{
if (!this.HasIncludeTemplateForSelection(selectionItem)) return;
SheerResponse.Eval("scForm.browser.getControl('" + viewStateString +
"_selected').selectedIndex=-1");
var listItem = new ListItem {ID = GetUniqueID("L")};
Sitecore.Context.ClientPage.AddControl(listbox, listItem);
listItem.Header = this.GetHeaderValue(selectionItem);
listItem.Value = listItem.ID + (object) "|" + selectionItem.ID;
SheerResponse.Refresh(listbox);
SetModified();
}
}
}
}
}
}
I've registered it in core database: /sitecore/system/Field types/Custom Types/Multiselect Tree List: filled Assembly and Class fields.
Added item with multiselect treelist field. Filled data. Multiple selection works OK.
But when I try to find References using Ribbon->Navigate->Links I don't see references to items (also missed TargetItemId in Links table in master Database).
When I changed it to Sitecore Treeview field - everything works fine.
As far as I understood crawler don't index my field and links not added to database.
Any ideas how to fix this?
Register your field type in App_Config\FieldTypes.config. Once done, future writes to the field will be included in the LinkDatabase.
Try also adding it to the index configuration:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
<sitecore>
<fieldTypes>
<fieldType name="Multiselect Treelist" type="Sitecore.Data.Fields.MultilistField,Sitecore.Kernel" />
</fieldTypes>
<contentSearch>
<indexConfigurations>
<defaultLuceneIndexConfiguration>
<fieldMap type="Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch">
<fieldTypes hint="raw:AddFieldByFieldTypeName">
<fieldType fieldTypeName="multiselect treelist" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
</fieldTypes>
</fieldMap>
<fieldReaders type="Sitecore.ContentSearch.FieldReaders.FieldReaderMap, Sitecore.ContentSearch">
<mapFieldByTypeName>
<fieldReader fieldTypeName="multiselect treelist" fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.MultiListFieldReader, Sitecore.ContentSearch" />
</mapFieldByTypeName>
</fieldReaders>
</defaultLuceneIndexConfiguration>
</indexConfigurations>
</contentSearch>
</sitecore>
</configuration>
Related
I have a parent table with rows.
When they select a row, an AJAX call fires that returns the child details.
I have multiple text boxes showing child properties
<div class="row">
#Html.CheckBoxFor(m => m.Child.Property)
#Html.LabelFor(m => m.Child.Property)
</div>
but I can't see how to update the text boxes with the child I get back in AJAX results.
The best I've been able to do is manually updating each field from the 'complete' method. But I've got about 30 more fields to add and it feels like the wrong approach.
How do I bind the edit boxes to the returned model without using partials and without refreshing the entire page?
I added Child as a property in the #model, and the TextFor appears to bind properly. But of course
#Model.Child = child
does not. So they never show any data.
This question was based on a misunderstanding on my part. At first I deleted the question when I realized my mistake. I'm reinstating it because I think it is an easy mistake for a noobie to fall into and once you do, the answer is hard to sort out.
The problem is that #model no longer exists once the page is rendered. There is no data binding going on behind the scenes as I thought there was.
Your options are
populating the elements manually. (this will need editing to fit your particular elements)
function DisplayMergeSection(data) {
for (var key of Object.keys(data)) {
DisplayElement(data, key, "#Clients_");
}
}
function DisplayElement(data, key, prefix) {
return;
var val = data[key];
var valString = data[key + "String"];
var element = $(prefix + key)[0];
if (element && element.type === 'text') {
if ((val || '').toString().indexOf("Date(") > -1) {
var dtStart = new Date(parseInt(val.substr(6)));
element.value = moment(dtStart).format('MM/DD/YYYY');
} else {
element.value = val;
}
} else if (element && element.type === 'checkbox') {
element.checked = val;
} else if (element && element.type === 'select-one') {
element.value = valString;
} else if (element && element.nodeName === 'DIV') {
if ((val || '').toString().indexOf("Date(") > -1) {
var dtStart = new Date(parseInt(val.substr(6)));
element.innerText = moment(dtStart).format('MM/DD/YYYY');
} else {
element.innerText = val;
}
}
}
create a bunch of observables with knockout and then set up data binding. This is a lot cleaner.
https://knockoutjs.com/documentation/json-data.html
set up a mapping with the knockout plugin.
https://knockoutjs.com/documentation/plugins-mapping.html
I've only really just started on Tornadofx and was having a bit of trouble trying to figure out how to reload a view so the controls in that view are refreshed.
Below is a simplified version of the code I'm working with. I've got a loop to generate radio-button controls based on strings in a list.
class MainView: View("MainView") {
override val root = vbox {
for(x in radioText) {
radiobutton(x, radioGroup) {
action {
radioSelected = this#radiobutton.text
}
}
}
button("Next") {
action {
// Reload View to update radiobuttons with new values
}
}
}
}
In the program I need to go through several sets of these radio buttons, and so the idea was that each time the user presses the "Next" button, the items in the radioText list would be updated to match the next set of radio-buttons. Then I was looking for a way to get the view to update with these new values.
I tried using openWindow() to open a new instance of the view, but then when I used close() to get rid of the previous instance and ended up closing both windows.
button("Next") {
action {
MainView().openWindow()
close()
}
}
Any help with this would be much appreciated,
Thanks.
If I understood correctly, you are trying to have a list of string and generate radiobuttons with it. So, by adding the variables to your example, would be something like this:
class MainView: View("MainView") {
val radioText = ArrayList<String>()
var radioGroup : ToggleGroup by singleAssign()
lateinit var radioSelected : String
override val root = vbox {
radioText.addAll(arrayListOf("One","Two","Three","Four"))
radioGroup = togglegroup(){}
for(x in radioText) {
radiobutton(x,radioGroup) {
action {
radioSelected = text //You don't need this#radiobutton.text
}
}
}
button("Next") {
action {
// Reload View to update radiobuttons with new values
}
}
}
}
I thing is a better idea having your radiobutton created by a listview, wich would be updated by a observable list of string, like I do bellow:
class MainView2: View("MainView") {
// this is a list of observable string, so when the items on his list change
// the listview is updated
val radioText = FXCollections.observableArrayList<String>()
var radioGroup : ToggleGroup by singleAssign()
lateinit var radioSelected : String
override val root = vbox() {
prefWidth = 200.0
prefHeight = 300.0
radioText.setAll("One","Two","Three","Four")
radioGroup = togglegroup(){}
listview<String>(radioText){
// Setting listview height dinamically
fixedCellSize = 25.0
prefHeightProperty().bind(radioText.sizeProperty.multiply(fixedCellSizeProperty().add(2)))
// Generating the radiobutton acording to strings on radioText
cellFormat {
graphic = cache(it){
radiobutton(it,radioGroup){
action {
radioSelected = text
}
}
}
}
}
button("Next") {
action {
radioText.clear()
radioText.setAll("Five","Six","Seven","Eight","Nine","Ten")
}
}
}
}
Please let me know if there is something you don't understand on my aproach.
I have an issue with an Mvvmcross / Xamarin application in droid part.
I have made a "MvxSpinner menu" wich is bind on a list of couple
ViewModel.cs
private List<CoupleIntString> _actions = new List<CoupleIntString>() {
new CoupleIntString(0,"Actions"),
new CoupleIntString(1, "Mail"),
new CoupleIntString(2,"Imprimer"),
new CoupleIntString(3, "Totaux"),
new CoupleIntString(4, "Fiche client")
};
public List<CoupleIntString> Actions {
get { return _actions; }
set {
_actions = value;
RaisePropertyChanged(() => Actions);
}
}
droid.axml
<MvxSpinner
android:id="#+id/action_spinner"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content"
local:MvxItemTemplate="#layout/item_spinner"
local:MvxDropDownItemTemplate="#layout/item_spinnerdropdown"
local:MvxBind="ItemsSource Actions;SelectedItem ActionSelected" />
When I select one item, I set the SelectedAction of my FirstViewModel and show the viewmodel I want to load.
public CoupleIntString ActionSelected {
set {
int xx = value.intPart;
switch (xx) {
case 1: //mail
GoToMailCommand.Execute();
break;
case 2: //impression
GoToImpressionCommand.Execute();
break;
case 3: //totaux
GoToTotauxCommand.Execute();
break;
case 4: //impression
GoToDetailsClientCommand.Execute();
break;
default:
break;
}
}
But, when i come back on the FirstViewModel, it automatically re-set the SelectedAction and go back to the second Viewmodel.
I have tried to set my SelectedAction to none in Init, ReloadState, Start, InitFromBundle and ReloadFromBundle but after all these calls, there is another one with the value I selected before and I don't know where it comes from.
I would propose to add a SelectedItem listener and set SelectedItem to 0.
var spinner = FindViewById<MvxSpinner>(Resource.Id.action_spinner);
spinner.ItemSelected += Spinner_ItemSelected;
MvvmCross has a SelectedItem target binding for MvxSpinner, you are almost right with your code. However, you need to use a command instead of a property for the SelectedItem binding to work:
private MvxCommand<CoupleIntString> _itemSelected;
public ICommand ItemSelected =>
_itemSelected = _itemSelected ??
(_itemSelected = new MvxCommand<CoupleIntString>(DoItemSelected));
private void DoItemSelected(CoupleIntString item)
{
ActionSelected = item;
}
Then make your binding look like:
local:MvxBind="ItemsSource Actions; SelectedItem ItemSelected"
I created my own UserControl, I added Button to it. Also I have Pivot where I added this UserControl. How can I change Pivot selecteditem in Button_Click event in my UserControl.
Binding:
Binding binding = new Binding("SelectedIndex");
binding.Source = historyControls[i].SelectedIndex;
Pivot_Second.SetBinding(Pivot.SelectedIndexProperty, binding);
void Button_Click()
{
SelectedIndex = desiredindex;
}
Make a property
private int _SelectedIndex;
public int SelectedIndex
{
get
{
return _SelectedIndex;
}
set
{
_SelectedIndex = value;
RaisedPropertyChanged("SelectedIndex");
}
}
then bind this property with selected index of your pivot.
I hope it will work.
I have written a multiselect jQuery plugin that can be applied to a normal HTML select element.
However, this plugin will parse the select element and its options and then remove the select element from the DOM and insert a combination of divs and checkboxes instead.
I have created a custom binding handler in Knockout as follows:
ko.bindingHandlers.dropdownlist = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
// Retrieve the value accessor
var value = valueAccessor();
// Get the true value of the property
var unwrappedValue = ko.utils.unwrapObservable(value);
// Check if we have specified the value type of the DropDownList items. Defaults to "int"
var ddlValueType = allBindingsAccessor().dropDownListValueType ? allBindingsAccessor().dropDownListValueType : 'int';
// Check if we have specified the INIMultiSelect options otherwise we will use our defaults.
var elementOptions = allBindingsAccessor().iniMultiSelectOptions ? allBindingsAccessor().iniMultiSelectOptions :
{
multiple: false,
onItemSelectedChanged: function (control, item) {
var val = item.value;
if (ddlValueType === "int") {
value(parseInt(val));
}
else if (ddlValueType == "float") {
value(parseFloat(val));
} else {
value(val);
}
}
};
// Retrieve the attr: {} binding
var attribs = allBindingsAccessor().attr;
// Check if we specified the attr binding
if (attribs != null && attribs != undefined) {
// Check if we specified the attr ID binding
if (attribs.hasOwnProperty('id')) {
var id = attribs.id;
$(element).attr('id', id);
}
if (bindingContext.hasOwnProperty('$index')) {
var idx = bindingContext.$index();
$(element).attr('name', 'ddl' + idx);
}
}
if ($(element).attr('id') == undefined || $(element).attr('id') == '') {
var id = "ko_ddl_id_" + (ko.bindingHandlers['dropdownlist'].currentIndex);
$(element).attr('id', id);
}
if ($(element).attr('name') == undefined || $(element).attr('name') == '') {
var name = "ko_ddl_name_" + (ko.bindingHandlers['dropdownlist'].currentIndex);
$(element).attr('name', name);
}
var options = $('option', element);
$.each(options, function (index) {
if ($(this).val() == unwrappedValue) {
$(this).attr('selected', 'selected');
}
});
if (!$(element).hasClass('INIMultiSelect')) {
$(element).addClass('INIMultiSelect');
}
$(element).iniMultiSelect(elementOptions);
ko.bindingHandlers['dropdownlist'].currentIndex++;
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var unwrappedValue = ko.utils.unwrapObservable(valueAccessor());
var id = $(element).attr('id').replace(/\[/gm, '\\[').replace(/\]/gm, '\\]');
var iniMultiSelect = $('#' + id);
if (iniMultiSelect != null) {
iniMultiSelect.SetValue(unwrappedValue, true);
}
}};
ko.bindingHandlers.dropdownlist.currentIndex = 0;
This will transform the original HTML select element into my custom multiselect.
However, when the update function is called the first time, after the init, the "element" variable will still be the original select element, and not my wrapper div that holds my custom html together.
And after the page has been completely loaded and I change the value of the observable that I am binding to, the update function is not triggered at all!
Somehow I have a feeling that knockout no longer "knows" what to do because the original DOM element that I'm binding to is gone...
Any ideas what might be the issue here?
There is clean up code in Knockout that will dispose of the computed observables that are used to trigger bindings when it determines that the element is no longer part of the document.
You could potentially find a way to just hide the original element, or place the binding on a container of the original select (probably would be a good option), or reapply a binding to one of the new elements.
I ran into a similar problem today, and here's how I solved it. In my update handler, I added the following line:
$(element).attr("dummy-attribute", ko.unwrap(valueAccessor()));
This suffices to prevent the handler from being disposed-of by Knockout's garbage collector.
JSFiddle (broken): http://jsfiddle.net/padfv0u9/
JSFiddle (fixed): http://jsfiddle.net/padfv0u9/2/