SAPUI5 Select first item in list after rendering - javascript

I have a Master-Detail application and I would like the first list item to be selected automatically once the application is loaded. I have tried the following solution but it does not work:
onAfterRendering: function() {
var oList = this.getView().byId("bankList");
var aItems = oList.getItems("listItems");
console.log(aItems);
if (aItems.length) {
aItems[0].setSelected(true);
}
}
The strange thing is that aItems seems to be empty even though it contains the correct details. Below is what is printed in the console when I console.log(aItems):
The result of console.log(aItems)

Guessing that you are using a sap.m.List, You should use the setSelectedItem() function, that receives the Item object as parameter.
Furthermore I recommend you to avoid using the onAfterRendering lifecycle method, to keep the lifecycle clean. There are usually many items that you can use, for example updateFinished for sap.m.List
Here the snippet
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta charset="utf-8">
<title>MVC with XmlView</title>
<!-- Load UI5, select "blue crystal" theme and the "sap.m" control library -->
<script id='sap-ui-bootstrap' src='https://sapui5.hana.ondemand.com/resources/sap-ui-core.js' data-sap-ui-theme='sap_belize_plus' data-sap-ui-libs='sap.m' data-sap-ui-xx-bindingSyntax='complex'></script>
<!-- DEFINE RE-USE COMPONENTS - NORMALLY DONE IN SEPARATE FILES -->
<!-- define a new (simple) View type as an XmlView
- using data binding for the Button text
- binding a controller method to the Button's "press" event
- also mixing in some plain HTML
note: typically this would be a standalone file -->
<script id="view1" type="sapui5/xmlview">
<mvc:View xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" controllerName="my.own.controller">
<List items="{/options}" mode="SingleSelect" updateFinished="onUpdateFinished">
<StandardListItem title="{value}"></StandardListItem>
</List>
</mvc:View>
</script>
<script>
// define a new (simple) Controller type
sap.ui.controller("my.own.controller", {
onUpdateFinished: function(oEvent) {
var oList = oEvent.getSource();
var aItems = oList.getItems();
oList.setSelectedItem(aItems[0])
}
});
/*** THIS IS THE "APPLICATION" CODE ***/
// create some dummy JSON data
var data = {
options: [{
key: '1',
value: 'option 1'
}, {
key: '2',
value: 'option 2'
}, {
key: '3',
value: 'option 3'
}]
};
var oJSONModel = new sap.ui.model.json.JSONModel();
oJSONModel.setData(data);
// instantiate the View
var myView = sap.ui.xmlview({
viewContent: jQuery('#view1').html()
}); // accessing the HTML inside the script tag above
myView.setModel(oJSONModel);
// put the View onto the screen
myView.placeAt('content');
</script>
</head>
<body id='content' class='sapUiBody'>
</body>
</html>

Related

Odoo-12: I created a widget. How do I use it?

I created a custom module that defines a widget. here are the .js file as well as the QWeb template:
bh_sidebar.js
odoo.define('sidebar_widget.bh_sidebar', function (require) {
'use strict';
var Widget = require('web.Widget');
var widgetRegistry = require('web.widget_registry');
var core = require('web.core');
var QWeb = core.qweb;
var _t = core._t;
var bh_sidebar = Widget.extend({
init: function () {
var self = this;
this._super(parent);
console.log('Widget initialized!');
},
events: {},
start: function () {
this.$el.append(QWeb.render('bh_sidebar_template'));
console.log('Widget started!');
}
});
widgetRegistry.add(
'bh_sidebar', bh_sidebar
)
})
bh_sidebar.xml
<?xml version="1.0"?>
<templates id="template" xml:space="preserve">
<t t-name="bh_sidebar_template">
<div style="background-color:red;" class="myClass">
Button 1
Button 2
</div>
</t>
</templates>
I've added both to the backend assets using:
<odoo>
<data>
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="script[last()]" position="after">
<script type="text/javascript" src="/sidebar_widget/static/src/js/bh_sidebar.js"></script>
<!-- <script type="text/javascript" src="/sidebar_widget/static/src/js/renderer.js"></script> -->
<link rel="stylesheet" type="text/scss" href="/sidebar_widget/static/src/scss/widget.scss"></link>
</xpath>
</template>
</data>
</odoo>
I've also added the templates to the manifest file.
This widget is supposed to display a red rectangle with two links, button 1 and button 2. I want this widget to be independent of any type of view, and just show up at the top of the screen beneath Odoo's navigation bar. How do I that? if I try to run my code as is, nothing happens. I've been at this for a week. Nothing I found on the internet helped. The documentation is criminally outdated, the ressources scarce.
I would appreciate any help.
Thank you.
Your widget will not be instantiated and inserted into the DOM automatically. To do this, you can hook into any of the already existing high-level widgets. For example, using the show_application method on the WebClient:
var WebClient = require('web.WebClient');
WebClient.include({
show_application: function () {
var sup = this._super.apply(this, arguments);
// Instantiate
var sidebar = new bh_sidebar();
// Insert in DOM
sidebar.insertAfter(this.$('.o_main'));
return sup;
},
});
I think u should render this view by using controller mechanism this will render this view independent from others view.

Data context on JsViews' include tag

I made a snippet to show the issue I'm facing:
<html>
<head>
<title>Demo 1 JsViews</title>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.90/jsviews.min.js"></script>
</head>
<body>
<script id="template" type="text/x-jsrender">
<p>{{:property}}</p>
{^{on ~root.testFunction}}PUSH ME!{{/on}}
{{for nestedObjects}}
{{include #data tmpl="#template-2"/}}
{{/for}}
</script>
<div id="container"></div>
<script id="template-2" type="text/x-jsrender">
<p>{{:~root.property}}</p>
{^{on ~root.testFunction}}PUSH ME!{{/on}}
</script>
<div id="container-2"></div>
<script>
data = {
property : "PARENT",
testFunction : function(){ alert(this.property); },
nestedObjects : [
{
id: 0,
property: "CHILD1",
testFunction : function(){ alert(this.property);}
},
{
id: 1,
property: "CHILD2",
testFunction : function(){ alert(this.property);}
}]
};
/**
* Funciones
*/
$(document).ready(function(){
var tmpl = $.templates("#template");
tmpl.link("#container", data);
});
</script>
</body>
</html>
As you can see, in '#template-2' it's taking the properties from main data object, not from each nestedObjects' item. I know this would be the normal behaviour.
Is there a way to make include tag to take each nestedObjects' item and not the whole data object as context?
I know that if I remove the '~root' modifiers in '#template-2' it will work as expected, but I need it to work with '~root' modififer if possible.
Thanks in advance :)
After trying many things, I managed to get it working in another way using a helper object, if someone has a similar issue feel free to contact me :).
~root is a built-in helper that points to the top-level data that you passed to the link() or render() method. See
http://www.jsviews.com/#contextualparams#root.
So you can't change it to have it point somewhere else. But you can create your own helpers (not using the reserved root name), such as:
{{include #data ~myroot=#data tmpl="#template-2"/}}
and in template-2 write
{{:~myroot.property}}

Showing Array items in sap -select and adding an anchor tag to items

I am currently learning SAP UI5 and I couldn't find enough information about how to show items in sap.m.Select , what I would like to do is showing the items from my own array as a dropdown and add an anchor link to all of them like :
var myMenuItems = ["foo","bar","baz"];
I managed to build a Select Menu but didnt find a way to add items, so it looks empty now.
My code for my select is :
new sap.m.Select(this.createId("selector"), {
name:"selectName",
selectedKey: "foo",
icon: "sap-icon://navigation-down-arrow",
valueState:"Success",
valueStateText:"random text idk what for",
forceSelection:false
})
How can I push items of myMenuItems and show them in this Select?
The sap.m.Select is a quite powerful control. You need a model as Ash mentioned.
Here is a working example for your case:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_belize"></script>
<script>
var oData = {
MenuItems: [{id: 1,name: "foo"},{id: 2,name: "bar"},{id: 3,name: "baz"}]
};
var oModel = new sap.ui.model.json.JSONModel(oData);
var oSelect = new sap.m.Select({
id: "selector",
});
var oItemSelectTemplate = new sap.ui.core.Item({
key: "{id}",
text: "{name}"
}); //Define the template for select items
oSelect.setModel(oModel);// set model to Select element
oSelect.bindAggregation("items", "/MenuItems", oItemSelectTemplate); //bind aggregation with the template to items
oSelect.placeAt('content');
</script>
</head>
<body id="content" class="sapUiBody">
</body>
</html>
Ash Kander is right, you have to create a model to contain the data.
The data binding section of the SAP UI5 SDK should show you how to do that and how to bind it to your select:
https://sapui5.hana.ondemand.com/#/topic/e5310932a71f42daa41f3a6143efca9c
As a quick summary, you can create the model and set it on the Select in the onInit method of the controller associated with the view the Select is in:
var oMenuItemsModel = new sap.ui.model.json.JSONModel({
"menuItems": [
{"item": "foo"},
{"item": "bar"},
{"item": "baz"}
]
});
this.getView().setModel(oMenuItemsModel, "myMenuItems");
<Select items="{myMenuItems>/menuItems}">
<core:Item key="{myMenuItems>item}" text="{myMenuItems>item}" />
</Select>
Then it should work.
This is basically all taken from the code sample on the SDK:
https://sapui5.hana.ondemand.com/#/sample/sap.m.sample.Select/code

No communication between custom Polymer components

I've written a single custom Polymer component that has multiple smaller custom components designed to one specific step of an entire process.
The elements are supposed to request specific input parameters and each step builds upon the previous steps. The final component contains the results retrieved from an API that accepts the input gathered from the first steps.
My problem is that I can't get info from one component to the next. I have this issue in practically every component I use, so I suspect the way I want to do it is the problem.
The first problem is that I need to retrieve a list of possibilities from the API, and display this list in a dropdown in multiple child components.
I put this list in a Polymer property, but the auto binding does not fire and the data change is not properly propagated down to the child components (if at all). I define a few iron-ajax elements that fetch the data OK, and have a template with child components that need the output of these iron-ajax elements.
<template>
<iron-ajax auto url="{{typesURL}}" handle-as="json"
on-response="handleTypesResponse"></iron-ajax>
<!-- more iron-ajax elements -->
<section id="activities">
<template is="dom-repeat" items="{{activities}}" as="activity">
<my-activity activity="{{activity}}"
types="{{types}}" />
</template>
</section>
<!-- more classic html elements -->
</template>
My url is straightforward and it casts into an Array just fine, but the template in activities is not reloaded and edited if I add an item to the activity array. How could I accomplish this?
A second issue that I'm facing is getting the 'result' of a component.
I have a form where I want the user to specify times. For this, I'm using paper-time-picker. When choosing a time however, and returning to the underlying web component, the time is not changed.
This is the code that defines the dialog:
<paper-dialog id="timeDialog" modal class="paper-time-picker-dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation">
<h2>Start time <span>{{activity.name}}</span></h2>
<paper-time-picker id="startTimePicker"></paper-time-picker>
<div class="buttons">
<paper-button dialog-dismiss>Cancel</paper-button>
<paper-button dialog-confirm autofocus on-tap="confirmTime">OK</paper-button>
</div>
</paper-dialog>
And these functions defined below the Polymer properties both show and retrieve the dialog and result:
showAttachedDialog: function (id) {
var button = this.$$(id);
if (!button.hasAttribute('data-dialog')) {
return;
}
var dialogId = '#' + button.getAttribute('data-dialog');
var dialog = this.$$(dialogId);
if (dialog) {
dialog.open();
}
},
confirmTime: function () {
this.activity['time'] = this.$$('#timePicker').time;
this.notifyPath('activity.time', this.activity.time);
console.log(this.activity);
console.log("time activity: " + this.activity.time);
},
The console output turns up empty (as in 'time activity: '). Does anyone see what I'm doing wrong? Do ask for more information if you require any to see what I may be missing here.
Thank you.
regarding activities thingy - you should use Polymer's push, pop etc, see https://www.polymer-project.org/1.0/docs/devguide/templates.html#dom-repeat. Or manualy invoke notifyPath. This works:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>xxz</title>
<script src="./bower_components/webcomponentsjs/webcomponents.js"></script>
<link rel="import" href="./bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/paper-time-picker/paper-time-picker.html">
<link rel="import" href="bower_components/paper-input/paper-input.html">
<link rel="import" href="bower_components/paper-button/paper-button.html">
<link rel="import" href="bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="bower_components/neon-animation/animations/scale-up-animation.html">
<link rel="import" href="bower_components/neon-animation/animations/fade-out-animation.html">
<dom-module id="my-activity">
<style>
:host{
display: block;
}
</style>
<template>
Activity <span>{{activity.name}}</span>
<paper-button on-click=showAttachedDialog>Time dialog <span>{{activity.time}}</span></paper-button>
<paper-dialog id="timeDialog" modal class="paper-time-picker-dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation">
<h2>Activity name <span>{{activity.name}}</span></h2>
<!-- from https://github.com/bendavis78/paper-time-picker -->
<paper-time-picker id="startTimePicker"></paper-time-picker>
<!--unfortunately paper-time-picker is not a iron-input (is="iron-input") so you can't jut bind to its value-->
<!--start time: <paper-input id="time" value="{{activity.time}}"></paper-input>-->
<div class="buttons">
<paper-button dialog-dismiss>Cancel</paper-button>
<paper-button dialog-confirm autofocus on-tap="confirmTime">OK</paper-button>
</div>
</paper-dialog>
</template>
<script>
HTMLImports.whenReady(function () {
Polymer({
is: "my-activity",
activity: Object,
listeners: {
'startTimePicker.time-changed': '_onTimeChanged'
},
//this is not needed if time is copied in confirmTime
_onTimeChanged: function(){
if (this.activity) {//first event is fired before activity is set
console.log("time activity: " + this.activity.time+' startTimePicker.time='+ this.$.startTimePicker.time);
this.activity.time = this.$.startTimePicker.time;
}
},
showAttachedDialog: function (event /*id*/) {
// that's I can't comprehend
// var button = this.$$(id);
// if (!button.hasAttribute('data-dialog')) {
// return;
// }
//
// var dialogId = '#' + button.getAttribute('data-dialog');
// var dialog = this.$$(dialogId);
// if (dialog) {
// dialog.open();
// }
this.$.timeDialog.open();
},
confirmTime: function () {
// this.activity['time'] = this.$$('#timePicker').time;
// this.notifyPath('activity.time', this.activity.time);
this.activity.time = this.$.startTimePicker.time;
console.log("time activity: " + this.activity.time+' startTimePicker.time='+ this.$.startTimePicker.time);
}
})
});
</script>
</dom-module>
<dom-module id="my-element">
<template>
<paper-button on-click=handleTypesResponse>Kinda call ajax</paper-button>
<section>
<template is="dom-repeat" items="{{activities}}" as="activity">
<my-activity activity="{{activity}}" />
</template>
</section>
</template>
<script>
HTMLImports.whenReady(function () {
Polymer({
is: "my-element",
handleTypesResponse: function () {
this.splice('activities', 0, 3);
for (var i = 0; i < 10; i++) {
console.log('i=' + i);
this.push('activities', {name: 'name'+i, time: new Date()});
}
},
ready: function() {
this.activities = [];
this.push('activities', {name: 'name XXX', time: new Date()});
this.push('activities', {name: 'name YY', time: new Date()});
this.push('activities', {name: 'name ZZ', time: new Date()});
}
});
});
</script>
</dom-module>
</head>
<body>
<my-element></my-element>
</body>
</html>

Yet another jQuery autocomplete thread

I am trying to figure out what's wrong with my code (Laravel5 project).
So far I have:
master.blade.php
<!-- jQuery Version 1.11.0 and jQuery UI-->
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="{{asset("patho/to/jquery/jquery-1.11.0.js")}}"></script>
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
the master view is preloaded in any view, it serves as a header and footer wrapper. Note that I obfuscated the local path to jQuery script, it's real and it's loaded and it works.
Included in one of my CRUD views:
create.blade.php
{!! Form::select('league_id',[], Request::old('league_id'), array('class' => 'form-control', 'id' => 'league_name')) !!}
<script src="{{ asset('js/tips.js') }}">
And in my tips.js file:
$(document).ready(function() {
var dataSource = ['test1', 'test2'];
$('#league_name').autocomplete(
{
source: dataSource
}
);
});
I also tried with:
var dataSource = [{value: 'test1', name: 'test1'}]
Yet it still does not work at all, no console errors displayed either.
Any clues?
Edit: following michaelbahr's suggestion:
the autocomplete widget doesn't execute at all, the resulting select dropdown I get is just an empty one. No items, no search box from the widget. A plain, empty select:
<select class="form-control ui-autocomplete-input" id="league_name" name="league_id" autocomplete="off"></select>
It won't work that way. Autocomplete needs an input (text) field to work. If you wanna have a select, you can do something like this.
But, if you already have an array, you don't need autocomplete, you can simply use jQuery like so:
$(document).ready(function() {
var dataSource = ['test1', 'test2'];
var league = $('#league_name');
$.each(dataSource, function(index, value){
league.append("<option>" + value + "</option>");
})
});

Categories