I am using custommultifield.js for the custom multifields inside my dialog. Here i am facing one issue with dialogfieldset widget. In JCR value of selectgrid is setting properly under node parsys --> gridslider --> options --> item_1 when i submitted the dialog.
But inside item_1 my other properties are not getting store like color1,position1. Below is my dialog.xml. Any Idea how can this will be get store or what will be the exact path to store them. This issue is coming when i use the items inside dialogfieldset.
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Dialog"
title="Slider"
width="{Long}650"
xtype="dialog">
<items
jcr:primaryType="cq:Widget"
xtype="tabpanel">
<items jcr:primaryType="cq:WidgetCollection">
<tab1
jcr:primaryType="cq:Panel"
title="Slider">
<items jcr:primaryType="cq:WidgetCollection">
<typeconfigs
jcr:primaryType="cq:Widget"
fieldLabel="Select Videos"
name="./options"
xtype="customconfigmultifield">
<fieldConfigs jcr:primaryType="cq:WidgetCollection">
<gridlayout
jcr:primaryType="cq:Widget"
fieldLabel="Select the grid"
itemId="dispotions"
layout="hbox"
name="selectgrid"
type="radio"
xtype="selection">
<options jcr:primaryType="cq:WidgetCollection">
<grid1
jcr:primaryType="nt:unstructured"
text="grid1"
value="grid1"/>
</options>
</gridlayout>
<fieldset
jcr:primaryType="cq:Widget"
id="fieldset0"
title="Optional Video"
width="{Long}300"
xtype="dialogfieldset">
<items jcr:primaryType="cq:WidgetCollection">
<colour
jcr:primaryType="cq:Widget"
fieldLabel="Colour"
name="color1"
type="select"
xtype="selection">
<options jcr:primaryType="cq:WidgetCollection">
<one
jcr:primaryType="nt:unstructured"
text="Tangerine"
value="tangerine"/>
</options>
</colour>
<position
jcr:primaryType="cq:Widget"
fieldLabel="Position"
name="position1"
type="select"
xtype="selection">
<options jcr:primaryType="cq:WidgetCollection">
<one
jcr:primaryType="nt:unstructured"
text="Top Left"
value="top_left"/>
</position>
</items>
</fieldset>
</fieldConfigs>
</typeconfigs>
</items>
</tab1>
</items>
</items>
</jcr:root>
My CustomMultifield.js is
/**
* #class CQ.form.CustomConfigMultiField
* #extends CQ.form.CompositeField
* The CustomConfigMultiField is an editable list of a set of form fields for editing a list of nodes with their properties.
* Unlike the {#link CQ.form.MultiField}, which works on an array of values (multi-value property in JCR),
* this widget works on the list of named objects, each containing the same set of properties (nodes with properties in JCR).
*
* <p>The naming scheme for the nodes will use a baseName + an automatically incremented number,
* e.g. "node_1" where the baseName is "node_".
* Note that if ordering is desired (via {#link #orderable}), it will be managed independently from the numbering, only the used node type must support it.
* Additionally, a prefix can be given for the final field names (just for the submit field names, eg. to support the often required "./" prefix for the Sling POST).
*
* #constructor
* Creates a new CustomConfigMultiField.
* #param {Object} config The config object
*/
CQ.form.CustomConfigMultiField = CQ.Ext.extend(CQ.form.CompositeField, {
/**
* #cfg {Long} maxLimit
* The maximum limitation for the items that can be added, default value is 2147483647.
* Optional.
*/
/**
* #cfg {String} prefix
* A general prefix added to every field name (eg. for "./") for submit.
* Optional.
*/
/**
* #cfg {String} name
* The container node for the list of managed nodes. If this is set, the individual items to load will be taken from the child nodes
* of that container node (eg. "container/node_1", "container/node_2"), otherwise from the "current" node, i.e. on the same level as the
* other fields next to this one (eg. "node_1", "node_2"). In the latter case it is probably desired to set {#link #baseName} and {#link #matchBaseName}
* to filter out the correct nodes (eg. "baseName_1", "baseName_2").
* Optional.
*/
/**
* #cfg {String} baseName
* A baseName for the node names of the individual objects, eg. "file_". Will
* be used to create the names for new nodes. If {#link #matchBaseName} is true,
* it will also be used to filter out the nodes to load. Defaults to "item_".
*/
/**
* #cfg {Boolean} matchBaseName
* Whether nodes must match the {#link #baseName} when loading the items.
* If not, all objects/nodes found are loaded. Defaults to true.
*/
/**
* #cfg {Array} fieldConfigs
* An array of configuration options for the fields. Required.
* <p>Example:
* <pre><code>
[{ xtype: "textfield",
name: "key",
fieldLabel: "Key"
},{ xtype: "pathfield",
name: "value",
fieldLabel: "Value"
}]
</code></pre>
*/
/**
* #cfg {Object} itemPanelConfig
* A config for the panel that holds the fields defined in {#link #fieldConfigs}.
* Can be used to define the layout further. The "items" object will be overwritten.
* Defaults to a simple panel with a from layout.
*/
// private
path: "",
bodyPadding: 0,
// the width of the field
fieldWidth: 0,
constructor: function(config) {
var list = this;
var items = new Array();
if (!config.addItemLabel) {
config.addItemLabel = CQ.I18n.getMessage("Add Item");
}
if(!config.readOnly) {
items.push({
xtype: "toolbar",
cls: "cq-multifield-toolbar",
items: [
"->", {
xtype: "textbutton",
text: config.addItemLabel,
style: "padding-right:6px",
handler:function() {
list.addItem();
list.doLayout();
}
}, {
xtype: "button",
iconCls: "cq-multifield-add",
template: new CQ.Ext.Template('<span><button class="x-btn" type="{0}"></button></span>'),
handler: function() {
list.addItem();
list.doLayout();
}
}
]
});
}
if (config.name) {
this.hiddenDeleteField = new CQ.Ext.form.Hidden({
"name":config.name + CQ.Sling.DELETE_SUFFIX
});
items.push(this.hiddenDeleteField);
}
config = CQ.Util.applyDefaults(config, {
fieldConfigs: [],
itemPanelConfig: {
xtype: "panel",
layout: "form",
border: false
},
orderable: true,
baseName: "item_",
matchBaseName: true,
border: true,
maxLimit:2147483647,
items:[{
xtype: "panel",
border:false,
bodyStyle: "padding:4px;",
items: items
}]
});
CQ.form.CustomConfigMultiField.superclass.constructor.call(this,config);
// typical example: prefix="./", name="items" => "./items/"
this.fieldNamePrefix = config.prefix || "";
if (config.name) {
this.fieldNamePrefix += config.name + "/";
}
},
initComponent: function() {
CQ.form.CustomConfigMultiField.superclass.initComponent.call(this);
},
/**
* Creates the name for a new field. Must take the baseName and append unique number
*/
createName: function() {
for (var i = 1;; i++) {
var name = this.baseName + i;
// check if this name has been used
var item = this.items.find(function(item) {
return item.name == name;
});
if (!item) {
return name;
}
}
return "";
},
/**
* Adds a new field with the specified value to the list.
* #param {String} name name of the object to add
* #param {Object} o The object to add
*/
addItem: function(name, o) {
/* new check for limit */
try{
if(this.limit!=undefined){
var limit=this.limit.maxVal;
if(limit!=0){
if((this.items.getCount()-1)>=limit){
CQ.Ext.Msg.show({title: 'Limit reached', msg: 'You are only allowed to add '+limit+' items to this module',icon:CQ.Ext.MessageBox.WARNING,buttons: CQ.Ext.Msg.OK});
return false;
}
}
}
}catch(e){}
if (this.maxLimit > 0 && this.items.getCount() > this.maxLimit) {
alert("Items have reached the maximum: "+this.maxLimit);
return;
}
if (!name) {
// new item to add
name = this.createName();
}
// What to do with values that couldn't be found? we delete the nodes normally...
var item = this.insert(this.items.getCount()-1, {
xtype: "customconfigmultifielditem",
name: name,
prefix: this.fieldNamePrefix,
orderable: this.orderable,
readOnly: this.readOnly,
fieldConfigs: this.fieldConfigs,
panelConfig: this.itemPanelConfig
});
item.processPath(this.path);
if (o) {
item.setValue(o);
}
this.doLayout();
},
processPath: function(path) {
this.path = path;
},
// overriding CQ.form.CompositeField#getValue
getValue: function() {
var value = new Array();
this.items.each(function(item, index) {
if (item instanceof CQ.form.CustomConfigMultiField.Item) {
value[index] = item.getValue();
index++;
}
}, this);
return value;
},
// private, loads a single object
processItem: function(name, o) {
if (typeof o !== "object") {
return;
}
if (this.baseName && this.matchBaseName !== false) {
// check if o.name starts with the baseName
if (name.indexOf(this.baseName) !== 0) {
return;
}
}
this.addItem(name, o);
},
// overriding CQ.form.CompositeField#processRecord
processRecord: function(record, path) {
if (this.fireEvent('beforeloadcontent', this, record, path) !== false) {
// remove all existing fields
this.items.each(function(item) {
if (item instanceof CQ.form.CustomConfigMultiField.Item) {
this.remove(item, true);
}
}, this);
if (this.name) {
var c = record.get(this.name);
for (var n in c) {
var v = record.get(this.getName());
this.processItem(n, c[n]);
}
} else {
record.fields.each(function(field) {
this.processItem(field.name, record.get(field.name));
}, this);
}
this.doLayout();
this.fireEvent('loadcontent', this, record, path);
}
},
// overriding CQ.form.CompositeField#setValue
setValue: function(value) {
},
// private
afterRender : function(){
CQ.form.CustomConfigMultiField.superclass.afterRender.call(this);
this.doLayout();
}
});
CQ.Ext.reg("customconfigmultifield", CQ.form.CustomConfigMultiField);
/**
* #private
* #class CQ.form.CustomConfigMultiField.Item
* #extends CQ.Ext.Panel
* The CustomConfigMultiField.Item is an item in the {#link CQ.form.CustomConfigMultiField}.
* This class is not intended for direct use.
* #constructor
* Creates a new CustomConfigMultiField.Item.
* #param {Object} config The config object
*/
CQ.form.CustomConfigMultiField.Item = CQ.Ext.extend(CQ.Ext.Panel, {
/**
* #cfg {String} name
* #member CQ.form.CustomConfigMultiField.Item
* Name of this item.
*/
/**
* #cfg {String} prefix
* #member CQ.form.CustomConfigMultiField.Item
* Prefix to add to all field names.
*/
/**
* #cfg {Boolean} readOnly
* #member CQ.form.CustomConfigMultiField.Item
* If the fields should be read only.
*/
/**
* #cfg {Array} fieldConfigs
* #member CQ.form.CustomConfigMultiField.Item
* Array of field configurations.
*/
/**
* #cfg {Object} panelConfig
* #member CQ.form.CustomConfigMultiField.Item
* Config for panel holding fields.
*/
constructor: function(config) {
var item = this;
var fields = CQ.Util.copyObject(config.fieldConfigs);
for (var i = 0; i < fields.length; i++) {
var f = fields[i];
f.rawFieldName = f.name;
f.name = config.prefix + config.name + "/" + f.rawFieldName;
f.readOnly = config.readOnly;
}
config.panelConfig = CQ.Util.copyObject(config.panelConfig);
console.log("config.panelConfig");
console.log(config.panelConfig);
config.panelConfig.items = fields;
config.panelConfig.cellCls = "cq-multifield-itemct";
config.panelConfig.border = true;
var items = new Array();
items.push(config.panelConfig);
if(!config.readOnly) {
if (config.orderable) {
items.push({
xtype: "panel",
border: false,
width:"100%",
items: {
xtype: "button",
"iconCls": "cq-multifield-up",
"template": new CQ.Ext.Template('<span><button class="x-btn" type="{0}"></button></span>'),
handler: function(){
var parent = item.ownerCt;
var index = parent.items.indexOf(item);
if (index > 0) {
item.reorder(parent.items.itemAt(index - 1));
}
}
}
});
items.push({
xtype: "panel",
width:"100%",
border: false,
items: {
xtype: "button",
"iconCls": "cq-multifield-down",
"template": new CQ.Ext.Template('<span><button class="x-btn" type="{0}"></button></span>'),
handler: function(){
var parent = item.ownerCt;
var index = parent.items.indexOf(item);
// note: there is one last item for the add button, must be ignored
if (index < parent.items.getCount() - 2) {
parent.items.itemAt(index + 1).reorder(item);
}
}
}
});
}
items.push({
xtype: "panel",
border: false,
width:"100%",
items: {
xtype: "button",
"iconCls": "cq-multifield-remove",
"template": new CQ.Ext.Template('<span><button class="x-btn" type="{0}"></button></span>'),
handler: function() {
item.ownerCt.remove(item);
}
}
});
}
config = CQ.Util.applyDefaults(config, {
"layout": "table",
"anchor": "100%",
"bodyCssClass": "cq-multifield-item",
"border": false,
"style" : "margin-bottom:-3px",
"layoutConfig": {
"columns": 4
},
"defaults": {
"bodyStyle": "padding:5px; border-top-width:0px; border-bottom-width:0px; border-left-width:0px;"
},
"items": items
});
CQ.form.CustomConfigMultiField.Item.superclass.constructor.call(this, config);
this.fields = new CQ.Ext.util.MixedCollection(false, function(field) {
return field.rawFieldName;
});
this.getFieldPanel().items.each(function(item) {
if (item.rawFieldName) {
this.fields.add(item.rawFieldName, item);
}
}, this);
if (config.value) {
this.setValue(config.value);
}
},
getFieldPanel: function() {
return this.items.get(0);
},
setPanelWidth: function(w) {
this.getFieldPanel().setWidth(w);
},
/**
* Reorders the item above the specified item.
* #param item {CQ.form.CustomConfigMultiField.Item} The item to reorder above
* #member CQ.form.CustomConfigMultiField.Item
*/
reorder: function(item) {
var c = this.ownerCt;
// move this item before the other one in the parent
c.insert(c.items.indexOf(item), this);
// must manually move dom element as well
this.getEl().insertBefore(item.getEl());
c.doLayout();
},
processPath: function(path) {
this.fields.each(function(f) {
if (f.processPath) {
f.processPath(path);
}
});
},
/**
* Returns the data value.
* #return {Object} value The field value
* #member CQ.form.CustomConfigMultiField.Item
*/
getValue: function() {
var o = {};
this.fields.each(function(f) {
o[f.rawFieldName] = f.getValue();
});
return o;
},
/**
* Sets a data value into the field and validates it.
* #param {Object} value The value object to set
* #member CQ.form.CustomConfigMultiField.Item
*/
setValue: function(value) {
this.fields.each(function(f) {
if (value[f.rawFieldName]) {
f.setValue(value[f.rawFieldName]);
}
});
}
});
CQ.Ext.reg("customconfigmultifielditem", CQ.form.CustomConfigMultiField.Item);
I can see my request when I submit the dialog
./options/item_1/selectgrid:grid1
color1:red
position1:top_right
which should be like ./options/item_1/color1:red for color1 etc. I can't hard code name property as item_counter is generated dynamically by multifield when i add new section.
Thanks
Related
I've built a store locator using the following guide but would love to zoom back out to the zoom level I've set on page load when closing the pop-up.
https://docs.mapbox.com/help/tutorials/building-a-store-locator/
Right now nothing happens after closing the poup-up and the user is forced to zoom out themselves to view the full map. Has anyone figured anything out?
mapboxgl.accessToken = 'pk.eyJ1IjoibmNoZXN0ZXI5MTkiLCJhIjoiY2t3NWFqaWliMG9zNzJ3bzhlMWQ3aXEyNyJ9.T_Z0pW3w6tZOftLeoJU-fA';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/nchester919/ckw5pnpyp0tfp14s5xo5trf4b',
center: [ -79.019302, 35.759575],
zoom: 6,
scrollZoom: true
});
map.addControl(new mapboxgl.FullscreenControl({container: document.querySelector('page-template')}));
const nav = new mapboxgl.NavigationControl({
showZoom: true,
showCompass: true,
});
map.addControl(nav, 'top-right');
const popup = new mapboxgl.Popup({
closeButton: true,
});
const stores = {
"type": "FeatureCollection",
"features": [
$lat = get_sub_field('latitude');
$log = get_sub_field('longitude');
$county = get_sub_field('county');
$region = get_sub_field('region') ?: 'central';
$city = get_sub_field('city');
$image = get_sub_field('image') ?: get_template_directory_uri().'/img/retire-home.jpg';
$desc = esc_html(get_sub_field('desc'));
$phone = get_sub_field('phone');
$address = esc_html(get_sub_field('address'));
$url = get_sub_field('url');
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
<?php echo '-'.$log; ?>,
<?php echo $lat; ?>
]
},
"properties": {
"phoneFormatted": "<?php echo $phone; ?>",
"county": "<?php echo $county; ?>",
"desc": "<?php echo $desc; ?>",
"url": "<?php echo $url; ?>",
"urlp": "<?php echo parse_url($url, PHP_URL_HOST); ?>",
"region": "<?php echo $region; ?>",
"city": "<?php echo $city; ?>",
"imageUrl": "<?php echo $image; ?>",
}
},
]
};
/* Assign a unique ID to each store */
stores.features.forEach(function (store, i) {
store.properties.id = i;
});
map.on('load', () => {
addMarkers();
map.addSource('places', {
type: 'geojson',
data: stores
});
buildLocationList(stores);
});
function addMarkers() {
/* For each feature in the GeoJSON object above: */
for (const marker of stores.features) {
/* Create a div element for the marker. */
const el = document.createElement('div');
/* Assign a unique `id` to the marker. */
el.id = `marker-${marker.properties.id}`;
/* Assign the `marker` class to each marker for styling. */
el.className = `marker marker-${marker.properties.region}`;
/**
* Create a marker using the div element
* defined above and add it to the map.
**/
new mapboxgl.Marker(el, { offset: [0, -23] })
.setLngLat(marker.geometry.coordinates)
.addTo(map);
el.addEventListener('click', (e) => {
/* Fly to the point */
flyToStore(marker);
/* Close all other popups and display popup for clicked store */
createPopUp(marker);
/* Highlight listing in sidebar */
const activeItem = document.getElementsByClassName('active');
e.stopPropagation();
if (activeItem[0]) {
activeItem[0].classList.remove('active');
}
const listing = document.getElementById(`listing-${marker.properties.id}`);
listing.classList.add('active');
});
}
}
function buildLocationList(stores) {
for (const store of stores.features) {
/* Add a new listing section to the sidebar. */
const listings = document.getElementById('listings');
const listing = listings.appendChild(document.createElement('div'));
/* Assign a unique `id` to the listing. */
listing.id = `listing-${store.properties.id}`;
/* Assign the `item` class to each listing for styling. */
listing.className = `ccpop_item ccmap-${store.properties.region}`;
/* Add the link to the individual listing created above. */
const link = listing.appendChild(document.createElement('a'));
link.href = '#map';
link.className = 'ccpop_link';
link.id = `link-${store.properties.id}`;
link.innerHTML = `Locate on Map`;
link.addEventListener('click', function () {
for (const feature of stores.features) {
if (this.id === `link-${feature.properties.id}`) {
flyToStore(feature);
createPopUp(feature);
}
}
const activeItem = document.getElementsByClassName('active');
if (activeItem[0]) {
activeItem[0].classList.remove('active');
}
this.parentNode.classList.add('active');
});
/* Add details to the individual listing. */
const details = listing.appendChild(document.createElement('div'));
details.innerHTML = `<div class="ccpop_county">${store.properties.county}</div>`;
if (store.properties.city) {
details.innerHTML += `<div class="ccpop_address">${store.properties.city}</div>`;
}
if (store.properties.url) {
details.innerHTML += `<a class="ccpop_urllist" target="_blank" href="${store.properties.url}">${store.properties.urlp}</a>`;
}
if (store.properties.distance) {
const roundedDistance = Math.round(store.properties.distance * 100) / 100;
details.innerHTML += `<div><strong>${roundedDistance} miles away</strong></div>`;
}
}
}
function flyToStore(currentFeature) {
map.flyTo({
center: currentFeature.geometry.coordinates,
zoom: 15
});
jQuery('.mapboxgl-popup-close-button').on('click', function () {
map.zoomOut({
center: [ -79.019302, 35.759575],
zoom: 6,
scrollZoom: true
});
});
}
function createPopUp(currentFeature) {
const popUps = document.getElementsByClassName('mapboxgl-popup');
/** Check if there is already a popup on the map and if so, remove it */
if (popUps[0]) popUps[0].remove();
const popup = new mapboxgl.Popup({ closeOnClick: true })
.setLngLat(currentFeature.geometry.coordinates)
.setHTML(`<div style="background-image: url('${currentFeature.properties.imageUrl}');" class="ccpop_image"></div><header class="ccpop_heading">${currentFeature.properties.county}</header><span class="ccpop_desc">${currentFeature.properties.desc}</span><span class="ccpop_more">More Information</span><a class="ccpop_url" target="_blank" href="${currentFeature.properties.url}">${currentFeature.properties.urlp}</a>`)
.addTo(map);
}
Grid xml column:
<column name='actions' class='My\Test\Ui\Component\Listing\Columns\Feeds\AdvancedActions'>
<argument name='data' xsi:type='array'>
<item name='config' xsi:type='array'>
<item name='component' xsi:type='string'>My_Test/js/grid/columns/actions</item>
<item name='dataType' xsi:type='string'>text</item>
<item name='label' xsi:type='string' translate='true'>Actions</item>
<item name='sortOrder' xsi:type='number'>90</item>
</item>
</argument>
</column>
Actions.js
define(
[
'jquery',
'underscore',
'mageUtils',
'uiRegistry',
'Magento_Ui/js/grid/columns/actions',
'Magento_Ui/js/modal/confirm'
], function ($, _, utils, registry, Column, confirm) {
'use strict';
return Column.extend(
{
/**
* Applies specified action.
*
* #param {String} actionIndex - Actions' identifier.
* #param {Number} rowIndex - Index of a row.
* #returns {ActionsColumn} Chainable.
*/
applyAction: function (actionIndex, rowIndex) {
var action = this.getAction(rowIndex, actionIndex),
callback = this._getCallback(action);
if (action.confirm) {
this._confirm(action, callback);
} else if (action.popup) {
this._popup(action, callback);
} else {
callback();
}
return this;
},
_popup: function (action, callback) {
var popupData = action.popup;
var dataType = popupData.type;
//Start loader
var body = $('body').loader();
body.loader('show');
if (popupData.file !== undefined && popupData.file !== '') {
$.ajax(
{
url: popupData.file,
async: false,
dataType: "text",
type: 'GET',
showLoader: true, //use for display loader
success: function (data) {
popupData.message = data;
}
}
);
}
//Stop loader
body.loader('hide');
},
});
Used showLoader: true and var body = $('body').loader(); body.loader('show'); but unable to start loader while ajax request.
Need an alternative to start loader during ajax call.
I faced the same issue. In my case, I need to load the 'jquery/ui' dependency.
define(
[
'jquery',
...
'jquery/ui'
Please have a look at the below code which might help.
define([
'jquery',
'Magento_Checkout/js/model/full-screen-loader',
], function ($,fullScreenLoader
) {
//Start Loader
fullScreenLoader.startLoader();
//Your AJax COde here
//Stop Loader
fullScreenLoader.stopLoader();
});
Just add the loader dependency to the end:
define([
'jquery',
...
'loader'
]
showLoader: true and
var body = $('body'); body.loader('hide'); will start working.
Try $('body').trigger('processStart') & $('body').trigger('processStop')
Expected Output
On clicking a row of the table details page should show the details of that row
Issue
Data binding not working.
In details view the controls are not showing the data bind to it.
In details view controller, if I print the context(which i am getting through getSelectedContext() on table)in console it is showing the data, but in view the controls are not showing the data
bind to it
index.html
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
<script src="resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_bluecrystal">
</script>
<!-- only load the mobile lib "sap.m" and the "sap_bluecrystal" theme -->
<script>
//set local resources
sap.ui.localResources("odatacruddemo");
//create app and set the initialPage to be displayed
var app = new sap.m.App({
initialPage: 'masterPage'
});
var masterPage = new sap.ui.view({
id: 'masterPage',
viewName: 'odatacruddemo.Master',
type: sap.ui.core.mvc.ViewType.JS
});
var detailsPage = new sap.ui.view({
id: 'detailsPage',
viewName: 'odatacruddemo.Details',
type: sap.ui.core.mvc.ViewType.JS
});
app.addPage(masterPage);
app.addPage(detailsPage);
app.placeAt('content');
</script>
</head>
<body class="sapUiBody" role="application">
<div id="content"></div>
</body>
</html>
Master.view.js
sap.ui.jsview("odatacruddemo.Master", {
/** Specifies the Controller belonging to this View.
* In the case that it is not implemented, or that "null" is returned, this View does not have a Controller.
* #memberOf odatacruddemo.Master
*/
getControllerName : function() {
return "odatacruddemo.Master";
},
/** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed.
* Since the Controller is given to this method, its event handlers can be attached right away.
* #memberOf odatacruddemo.Master
*/
createContent : function(oController) {
//add columns in a array to be added in table
var aProductTableColumns = [
new sap.m.Column({
header: new sap.m.Text({
text: 'Person ID'
})
}),
new sap.m.Column({
header: new sap.m.Text({
text: 'Name'
})
}),
new sap.m.Column({
header: new sap.m.Text({
text: 'EmployeeID'
})
}),
new sap.m.Column({
header: new sap.m.Text({
text: 'HireDate'
})
}),
new sap.m.Column({
header: new sap.m.Text({
text: 'Salary'
})
}),
new sap.m.Column({
header: new sap.m.Text({
text: 'Operations'
})
})
];
//add the data to the table using cells aggregation by using ColumnListItem
var oTableTemplate = new sap.m.ColumnListItem({
type: "Navigation",
press: [oController.onListPress, oController],
cells: [
new sap.m.ObjectIdentifier({
text: '{ID}'
}),
new sap.m.ObjectIdentifier({
text: '{Name}'
}),
new sap.m.ObjectIdentifier({
text: '{EmployeeID}'
}),
new sap.m.ObjectIdentifier({
text: '{HireDate}'
}),
new sap.m.ObjectIdentifier({
text: '{Salary}',
})
]
}).addStyleClass("sapUiResponsiveMargin");
//oTableTemplate.setType(sap.m.ListType.Active);
//create dialog for updating/editing data
var editPersonDetailsDialog = new sap.m.Dialog({
id: 'editPersonDetailsDialog',
title: 'Update Details'
});
//create table
var oProductTable = new sap.m.Table({
id: 'oProductTable',
columns: aProductTableColumns,
itemPress: [oController.onListPress,oController]
});
//oProductTable.setMode(sap.m.ListMode.SingleSelect);
oProductTable.setMode(sap.m.ListMode.SingleSelectMaster);
//bind the JSON data received from the service with Table
oProductTable.bindItems("/value",oTableTemplate);
var masterPage = new sap.m.Page({
title: "CRUD Operations on Public Odata Service",
content: [
oProductTable
]
});
return masterPage;
}
});
Master.Controller.js
sap.ui.controller("odatacruddemo.Master", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* #memberOf odatacruddemo.Master
*/
onInit: function() {
var serviceURL = 'http://services.odata.org/V4/OData/OData.svc/Persons?$expand=PersonDetail';
var oModel = new sap.ui.model.json.JSONModel(serviceURL);
oModel.attachRequestCompleted(function(oEvent){
//bind a model to a view
var masterView = sap.ui.getCore().byId("masterPage");
masterView.setModel(oModel);
//sap.ui.getCore().setModel(oModel);
});
//sap.ui.getCore().setModel(oModel);
},
/**
* Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
* (NOT before the first rendering! onInit() is used for that one!).
* #memberOf odatacruddemo.Master
*/
// onBeforeRendering: function() {
//
// },
/**
* Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
* This hook is the same one that SAPUI5 controls get after being rendered.
* #memberOf odatacruddemo.Master
*/
// onAfterRendering: function() {
//
// },
/**
* Called when the Controller is destroyed. Use this one to free resources and finalize activities.
* #memberOf odatacruddemo.Master
*/
// onExit: function() {
//
// }
onListPress: function(oEvent){
console.log('List Item pressed');
//Get the Selected Items
//get the row data which has been clicked
var clickedItem = sap.ui.getCore().byId('oProductTable').getSelectedItems();
var cells = clickedItem[0].getCells();
var contexts = sap.ui.getCore().byId("oProductTable").getSelectedContexts();
console.log('Master ctx:');
console.log(contexts);
var items = contexts.map(function(c) {
return c.getObject();
});
console.log(items[0]);
var detailsPage = app.getPage('detailsPage');
detailsPage.setBindingContext(contexts,"data");
app.to(detailsPage);
//var detailsView = sap.ui.getCore().byId("detailsPage");
//detailsView.getModel().setData(items[0]);
//var oContext = oEvent.getSource().getBindingContext();
//console.log(oEvent);
}
});
In above code I am setting the bindingContext to detailsPage and I am able to get that context in detailsPage but in the view it not showing the data.
Below is the DetailPage Code:
Details.view.js
sap.ui.jsview("odatacruddemo.Details", {
/** Specifies the Controller belonging to this View.
* In the case that it is not implemented, or that "null" is returned, this View does not have a Controller.
* #memberOf odatacruddemo.Details
*/
getControllerName : function() {
return "odatacruddemo.Details";
},
/** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed.
* Since the Controller is given to this method, its event handlers can be attached right away.
* #memberOf odatacruddemo.Details
*/
createContent : function(oController) {
var oObjectHeader = new sap.m.ObjectHeader({
title: "{Name}",
attributes:[
new sap.m.ObjectAttribute({
text: "{Name}"
})
]
});
var detailsPage = new sap.m.Page({
title: "DetailsPage",
content: [
oObjectHeader
]
});
return detailsPage;
}
});
Details.controller.js
sap.ui.controller("odatacruddemo.Details", {
/**
* Called when a controller is instantiated and its View controls (if available) are already created.
* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
* #memberOf odatacruddemo.Details
*/
onInit: function() {
console.log('onInit() detailPage called');
},
/**
* Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
* (NOT before the first rendering! onInit() is used for that one!).
* #memberOf odatacruddemo.Details
*/
onBeforeRendering: function() {
var detailsPage = sap.ui.getCore().byId('detailsPage');
var context = detailsPage.getBindingContext("data");
console.log('ctx:=>');
console.log(context);
var items = context.map(function(c) {
return c.getObject();
});
console.log(items[0]);
},
/**
* Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
* This hook is the same one that SAPUI5 controls get after being rendered.
* #memberOf odatacruddemo.Details
*/
// onAfterRendering: function() {
//
// },
/**
* Called when the Controller is destroyed. Use this one to free resources and finalize activities.
* #memberOf odatacruddemo.Details
*/
// onExit: function() {
//
// }
});
In the details View the controls are not showing the data bind to it.
Please help ...
Thanks in Advance...
your tables and controls are not having any data binding at all. You have to bind the items aggregation of your table. When using JS Views take a look at this API https://sapui5.hana.ondemand.com/#/api/sap.m.ListBase/methods/bindItems of sap.m.Table.
First of all you should use XMLViews instead of JsViews.
For your binding problem take a look in the SAPUI5 developer. Take a look at this example from the developer guide and go through it.
https://sapui5.hana.ondemand.com/#/topic/97830de2d7314e93b5c1ee3878a17be9
sap.ui.controller("odatacruddemo.Details", {
onInit: function() {
console.log('onInit() detailPage called');
var oModel2 = new sap.ui.model.json.JSONModel();
var detailsPage = sap.ui.getCore().byId('detailsPage');
detailsPage.setModel(oModel2);
this.getView().addEventDelegate({
onBeforeShow : function(evt) {
var detailsPage = sap.ui.getCore().byId('detailsPage');
var context = detailsPage.getBindingContext("data");
console.log('ctx:=>');
console.log(context);
//sap.ui.getCore().getModel().setData(json);
var items = context.map(function(c) {
return c.getObject();
});
console.log(items[0]);
var dPage = sap.ui.getCore().byId('detailsPage');
dPage.getModel().setData(items[0]);
},
});
},
onNavPress: function(){
app.back();
}});
I would like to display a messager in sapui5 Application, when onSave is printed or at the init function ist started. but i have always a error in the console and the messageToast do not work.
Errormessage in console:
Uncaught (in promise) TypeError: MessageToast.show is not a function
this is my controller.js:
sap.ui.define(['sap/ui/core/mvc/Controller',
'timeTrackertimeTracker/controller/BaseController',
'sap/ui/model/json/JSONModel',
"sap/m/MessageToast",
"sap/ui/model/odata/ODataModel",
"sap/ui/core/routing/History"
],
function(Controller, BaseController, MessageToast, JSONModel, ODataModel, History) {
"use strict";
//Global variables
//var _oController, oModel, oView;
var Calendarcontroller = BaseController.extend("timeTrackertimeTracker.controller.Calendarform", {
/* =========================================================== */
/* lifecycle methods */
/* =========================================================== */
onInit: function() {
//Store controller reference to global variable
this.getRouter().getRoute("Calendarform").attachPatternMatched(this._onRouteMatched, this);
MessageToast.show("init");
},
/* =========================================================== */
/* event handlers */
/* =========================================================== */
_onRouteMatched: function() {
// register for metadata loaded events
var oModel = this.getModel("appointments");
oModel.metadataLoaded().then(this._onMetadataLoaded.bind(this));
},
_onMetadataLoaded: function () {
// create default properties
var oProperties = {
Id: "Id" + parseInt(Math.random() * 1000000000)
/* duration: "",
resttime: "",
title: "",
starttime: "",
endtime: "",
Description: ""*/
};
// create new entry in the model
this._oContext = this.getModel("appointments").createEntry("/appointments", {
properties: oProperties
,
success: this._onCreateSuccess.bind(this)
});
// bind the view to the new entry
this.getView().setBindingContext(this._oContext, "appointments");
},
onSave: function(oEvent) {
// bind the view to the new entry
//this.getView().setBindingContext(this._oContext);
this.getModel("appointments").submitChanges();
},
_onCreateSuccess: function (oEvent) {
// navigate to the new product's object view
this.getRouter().navTo("AppointmentsList", true);
// unbind the view to not show this object again
this.getView().unbindObject();
// show success messge
/* var sMessage = this.getResourceBundle().getText("newObjectCreated", [ oEvent.Id ]);
MessageToast.show(sMessage, {
closeOnBrowserNavigation : false
});*/
},
/* _onCreateSuccess: function (oAppointment) {
// show success messge
var sMessage = this.getResourceBundle().getText("newObjectCreated", [ oAppointment.Title ]);
MessageToast.show(sMessage, {
closeOnBrowserNavigation : false
});
},
*/
onCancel: function() {
this.onNavBack();
//this.getView().getModel("appointments").deleteCreatedEntry(this._oContext);
},
/**
* Event handler for navigating back.
* It checks if there is a history entry. If yes, history.go(-1) will happen.
* If not, it will replace the current entry of the browser history with the worklist route.
* #public
*/
onNavBack : function() {
/* var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("AppointmentsList");*/
var oHistory = History.getInstance(),
sPreviousHash = oHistory.getPreviousHash();
// discard new product from model.
this.getModel("appointments").deleteCreatedEntry(this._oContext);
if (sPreviousHash !== undefined) {
// The history contains a previous entry
history.go(-1);
} else {
// Otherwise we go backwards with a forward history
var bReplace = true;
this.getRouter().navTo("AppointmentsList", {}, bReplace);
}
}
});
return Calendarcontroller;
});
Exchange places of parameters MessageToast and JSONModel in function (line 9): in the dependency list sap/ui/model/json/JSONModel mentioned before sap/m/MessageToast
I can’t connect to the internet using Concept N. I am using jquery with a simple $.get, but it is just not working. Can someone help me with this?
You actually need to use the da.getXHr() function to access external web services. Here is an example of how to access rss data using jquery. First thing to do is download jquery and include it in your index.html file:
<script type="text/javascript" src="app/jquery.min.js"></script>
Now use this code snippet to access a rss url with jquery. Be sure to replace the line "** Enter-rss-2.0-url-here **" with your own rss url
/*
* Copyright 2016 Sony Corporation
*/
var title;
var description;
/**
* The callback to prepare a segment for play.
* #param {string} trigger The trigger type of a segment.
* #param {object} args The input arguments.
*/
da.segment.onpreprocess = function (trigger, args) {
console.log('onpreprocess', { trigger: trigger, args: args });
//da.startSegment(null, null);
$.ajax({
url:"** Enter-rss-2.0-url-here **",
type: "GET",
dataType: 'xml',
xhr: function () { return da.getXhr(); },
error: function (jqXHR, textStatus, errorThrown) {
console.log('ajax error jqXHR.status[' + jqXHR.status + ']');
def.reject("failed with error "+jqXHR.status);
return;
},
success: function (data, textStatus, jqXHR) {
$(data).find("item:first").each(function() {
title = $(this).find("title").text();
description = $(this).find("description").text()
})
da.startSegment(null, null)
}
})
};
/**
* The callback to start a segment.
* #param {string} trigger The trigger type of a segment.
* #param {object} args The input arguments.
*/
da.segment.onstart = function (trigger, args) {
console.log('onstart', { trigger: trigger, args: args });
var synthesis = da.SpeechSynthesis.getInstance();
synthesis.speak("Title "+title+", Content "+description, {
onstart: function () {
console.log('speak start');
},
onend: function () {
console.log('speak onend');
da.stopSegment();
},
onerror: function (error) {
console.log('speak cancel: ' + error.messsage);
da.stopSegment();
}
});
};