Kendo UI: How to set the value of a tooltip with a MVVM binding - mvvm

In Kendo UI, I have a tooltip declaratively defined inside a view:
<span data-bind="events: { show: onShow }"
data-role="tooltip"
data-auto-hide="true"
data-position="top">?</span>
Normally the content of the tooltip would be attached via the title attribute, or when attaching the tooltip procedurally, via the content property. But here, the content should be fetched out of the model.
So I'm looking for the equivalent of data-bind="text: contents for the Kendo Tooltip.

Can be done by creating a small custom binder.
kendo.data.binders.widget.tooltip = {
value: kendo.data.Binder.extend({
refresh: function() {
var value = this.bindings["value"].get();
var tooltip = this.element;
tooltip.element.attr("title",value);
}
})
};
Here is a live demo.

Related

Bound content in Popover is not shown

I created a popover which is called on mouseover via addEventDelegate.
The popover has a text element with binding to my Model.
The fragment:
<Popover xmlns="sap.m"
id="myCardPopover"
showHeader="false"
contentWidth="260px"
placement="Bottom">
<Text id="myCardPopoverText" text="{propertyModel>Tooltip}" />
</Popover>
Register the browser events:
attachCardPopoverOnMouseover: function () {
var rootHBox = this.byId("myReportCardContent");
rootHBox.addEventDelegate({
onmouseover: function () {
this.timeId = setTimeout(() => that.onOpenCardPopover(this), 600);
},
onmouseout: function () {
clearTimeout(this.timeId) || that.onCloseCardPopover(this);
}
}, rootHBox);
},
The event listener:
onOpenCardPopover: function (oControl) {
this.oCardTooltipPopover.then(function (oPopover) {
var oContext = oControl.getBindingContext("propertyModel");
oPopover.setBindingContext(oContext);
oPopover.openBy(oControl);
});
},
The Popover itself is a dependent of multiple controls created from an aggregation binding and is created in onAfterRendering.
// Fragment required from "sap/ui/core/Fragment"
createCardPopover: function () {
var oView = this.getView();
var oRootHBox = this.byId("myReportCardContent");
if (!this.oCardTooltipPopover) {
this.oCardTooltipPopover = Fragment.load({
id: oView.getId(),
name: "namespace.view.CardPopup"
}).then(function (oPopover) {
oRootHBox.addDependent(oPopover);
return oPopover;
});
}
},
When I hover over one of my controls, I only get an empty popover. The bound text isn't shown.
However, when I lookup the created popover in the UI5 debug tools, the binding seems correct and also the text is shown up there.
The text's <span> element in the DOM is also empty.
[...] multiple controls created from an aggregation binding
With this.byId("myReportCardContent"), you're accessing the template control rather than the actual rendered ones. A template in aggregation binding is usually a static control (i.e. without propagated models and contexts) that the ManagedObject will clone for each data object. After cloning, the parent models and contexts are propagated to those clones, and not to the internal template control.
So if you add the popover to the dependent aggregation of the template (oRootHBox), there is no context to propagate. The binding paths in the fragment definition stay unresolved.
How the issue can be circumvented is up to you. There are many approaches to show something on mouseover. Just avoid manipulating the template after rendering.

Kendo UI: How to bind Kendo UI hierarchical datagrid detailInit event using MVVM (data-attribute)

I am constructing a hierarchical datagrid using Kendo UI and I am using MVVM methodology for widget binding.
Here is the DEMO of the kind of hierarchical grid I want to make. But the example here uses jQuery and not MVVM.
How can I bind the detailInit event to my viewModel using data attributes using MVVM?
I want to bind the event using the below code but it is not working:
JS:
var viewModel = kendo.observable({
......
..........
dataGridDetailInit: function (e) {
//Here I want to catch the detailInit event of the dataGrid
},
..........
......
});
HTML (Kendo template):
<!-- Datagrid -->
<div data-role="grid"
data-columns="[
{'field':'FullName', 'title':'Full Name'},
{'field':'Email', 'title':'Email'},
{'field':'HomeTel', 'title':'HomeTel'},
{'field':'Mobile', 'title':'MobileTel'},
{'field':'Contact_Type', 'title':'Contact Type'},
]"
data-bind ="source: address_book_datagrid_observable.datasource,
events: {
detailInit: dataGridDetailInit
}"
data-pageable='{
refresh: false,
pageSizes: true,
buttonCount: 5,
}'
data-navigatable = "true"
data-resizable = "true"
data-no-records= "true"
data-messages = '{
noRecords: "There is no data to be displayed"
}'
>
</div>
Ok, so while researching I came across this link.
On this issue, an engineer from Telerik asserted this:
All Kendo widgets can be configured via data attributes. Building a
hierarchical grid declaratively is supported too, however please have
in mind that: detailInit event should not be bound through the events
binding but via data-attribute.
Here is the example of how the event binding could be accomplished.
The right way to bind detailInit event to viewModel using MVVM (data attibute is) using data-detail-init as below:
<!-- Datagrid -->
<div data-role="grid"
data-columns="[
{'field':'FullName', 'title':'Full Name'},
{'field':'Email', 'title':'Email'},
{'field':'HomeTel', 'title':'HomeTel'},
{'field':'Mobile', 'title':'MobileTel'},
{'field':'Contact_Type', 'title':'Contact Type'},
]"
data-bind ="source: viewModel.datasource"
data-detail-init="viewModel.dataGridDetailInit"
data-pageable='{
refresh: false,
pageSizes: true,
buttonCount: 5,
}'
data-navigatable = "true"
data-resizable = "true"
data-no-records= "true"
data-messages = '{
noRecords: "There is no data to be displayed"
}'
>
</div>

How to Set Width of sap.m.MessagePopover?

The control sap.m.MessagePopover has an attribute _oPopover (containing sap.m.Popover inside).
Using this attribute, I could set the popover width:
messagePopover._oPopover.setContentWidth("450px");
However, as SAP attributes starting from _ should not be used, does anybody know a cleaner way?
As of UI5 version 1.46, a more flexible control sap.m.MessageView can be used instead of the old sap.m.MessagePopover.
There is no need to access internal properties or apply custom CSS style classes to manipulate the width as you can put MessageView anywhere you want (Still, Fiori Guideline recommends to use it only within a responsive popover or a dialog).
const popover = new ResponsivePopover({
contentWidth: "450px",
contentHeight: "450px",
content: [
/*myMessageView*/
],
});
// ...
popover.openBy(...);
Compared to MessagePopover, MessageView can group items and more.
Internally, MessagePopover uses MessageView too.
Another solution would be to use CSS class. However, there is a catch. As you can see from below generated DOM of the message popover, inline styling has been used :( .
Only way to override inline-style is by using !important in CSS which is again not recommended approach. However, considering inline CSS has been used, I would go with using !important keyword. Below is the working code:
XML Code ( for adding Class):
<MessagePopover id='myMP' class='myPopoverClass'>
<items>
<MessagePopoverItem title='Title' subTitle='SubTitle'></MessagePopoverItem>
</items>
</MessagePopover>
CSS:
.myPopoverClass {
width:100rem;
}
.myPopoverClass .sapMPopoverCont {
width:100% !important;
}
You can play around with how much width you need for message Popover.
EDIT: This is from the source code:
MessagePopover.prototype.init = function () {
var that = this;
var oPopupControl;
this._oResourceBundle = sap.ui.getCore().getLibraryResourceBundle("sap.m");
this._oPopover = new ResponsivePopover(this.getId() + "-messagePopover", {
showHeader: false,
contentWidth: "440px",
placement: this.getPlacement(),
showCloseButton: false,
modal: false,
afterOpen: function (oEvent) {
that.fireAfterOpen({openBy: oEvent.getParameter("openBy")});
},
afterClose: function (oEvent) {
that._navContainer.backToTop();
that.fireAfterClose({openBy: oEvent.getParameter("openBy")});
},
beforeOpen: function (oEvent) {
that.fireBeforeOpen({openBy: oEvent.getParameter("openBy")});
},
beforeClose: function (oEvent) {
that.fireBeforeClose({openBy: oEvent.getParameter("openBy")});
}
}).addStyleClass(CSS_CLASS);

Kendo UI Grid popup editing with MVVM

I am using Kendo UI Grid and I have configured it to use popup editing with custom template
<script id="popup_editor" type="text/x-kendo-template">
<div id="editor">
<div class="k-edit-label">
<label for="Type">Type</label>
</div>
<select data-role="dropdownlist" data-value-field="Type" data-text-field="Type"
data-bind="source: typeSource, value: selectedProduct"></select>
<div class="k-edit-label">
<label for="Type">Option</label>
</div>
<select data-role="dropdownlist" data-value-field="Option" data-text-field="Option"
data-bind="source: productsSource.Options, value: selectedOption"></select>
</div>
</script>
This is my ViewModel:
function ViewModel() {
var getTypesUrl = "/Controller/Action";
var viewModel = kendo.observable({
typeSource: new kendo.data.DataSource({
transport: {
read: {
url: getConditionTypesUrl,
dataType: "json"
},
},
batch: true,
schema: {
model: {
id: "Type"
}
}
}),
selectedType: null,
selectedOption: null
});
kendo.bind($("#editor"), viewModel);
}
ViewModel();
My action returns JSON.
The problem is that when I click on the "Add new record" button, there is no call to the getTypesUrl and the dropdownlist is not populated. The general idea is to have different options for different types and to populate the Option dropdownlist depending on the selected type. I believe, that the problem occurs because the editor is showed only when the button is clicked and the kendo can not create the bindings.
If the Drop down list is the same for each row get its values from the Data Source and store these in a variable in the page in JavaScript and point the Drop Down list at this new Data Source. Use some JavaScript to associate the id and name.
Alternatively if this is loaded based on some other logic implement a separate call to populate the Data source for the drop down list in your view model.
http://www.aspnetwiki.com/page:introduction-to-kendo-mvvm
http://www.aspnetwiki.com/page:kendo-mvvm-ui-grid
Further note your can write your template purely in JavaScript and bind this to the html, advantage of which is you can debug it and it can still be loaded by an ajax call later and it is likely going to be smaller.

ExtJS4: Add field to form panel but not want it to be rendered by panel

I have a static html form layout where i add extjs form fields using the "renderTo" config. In order to have form validation and simple submit methods i want to add the fields to a form panel. As the layout is managed by the html frame i don't want the form to be rendered by the panel (panel has html frame as contentEl and this should be used as is).
In extjs3 i could achieve this by adding the field not to the panel but to the BasicForm (formpanel.getForm().add(...)) but in extjs4 this method seems to be gone.
How can i do this using extjs4?
Thanks in advance.
Since you already have a Panel that uses the contentEl to render HTML into its body, I recommend to stick with this approach:
Replace the panel with an Ext.form.Panel instance - the configuration, particularly the contentEl config - can remain unchanged.
The code provided here will override a standard Ext class (Ext.layout.Layout) and introduce support for a 'renderItemTo' config property on child items of any Ext container or panel instance (including Ext.form.Panel).
The value of the config property should be the ID of an already rendered DOM node, e.g. a DIV element that is part of the HTML fragment used in as the contentEl of the parent container's body.
Ext.require(['Ext.layout.Layout'], function() {
Ext.override(Ext.layout.Layout, {
renderItem: function (item, target, position) {
if(item.renderItemTo) {
// render 'renderItemTo' components into the specified DOM element
item.render(item.renderItemTo, 1);
// don't allow container layout to seize the component
item.layoutManagedHeight = 2;
item.layoutManagedWidth = 2;
} else {
// just use standard Ext code for non-renderItemTo components
this.callOverridden(arguments);
}
},
isValidParent: function(item, target, position) {
// signal Ext that we are OK with were our 'renderItemTo' component is right now
// otherwise it would get moved during the layout process
return item.renderItemTo ? true : this.callOverridden(arguments);
}
});
});
Usage:
var panel = Ext.create('Ext.form.Panel', {
contentEl: 'form', // the DOM element ID that holds the HTML fragment for the body
title: 'My FormPanel with special FX',
items: [
{
xtype: 'textfield',
renderItemTo: 'text1', // the ID of a DOM element inside the HTML fragment
fieldLabel: 'Label 1',
},
{
xtype: 'textfield',
renderItemTo: 'text2', // the ID of a DOM element inside the HTML fragment
fieldLabel: 'Label 2'
}
]
});
I uploaded a working example to JSFiddle (note: resize the window if you experience a render problem - this is related to JSFiddle, not my override).
After digging through the layout system of ExtJS 4.1 i implemented a custom layout which moves the items after rendering to the desired position in the fixed markup. The result is the same as for the ExtJS 4.0.7 version from this thread. It seams to work for the ExtJS standard fields. I have some problems with my custom fields though.
Ext.define('Ext.ux.layout.Fixed', {
extend: 'Ext.layout.container.Auto',
alias: 'layout.uxfixed',
afterRenderItem: function(item) {
// move items with renderToFixedMarkup to desired position
if (item.renderToFixedMarkup) {
var target = Ext.getDom(item.renderToFixedMarkup);
this.moveItem(item, target);
}
},
isValidParent: function(item, target, position) {
// items with renderToFixedMarkup property are always positioned correctly
return (item.renderToFixedMarkup) ? true : this.callOverridden(arguments);
}
});
It can be used by setting "layout: 'uxfixed'" on the panel and the "renderToFixedMarkup" config on the items.