SAPUI5 List Bind Aggregation get Value of the list - javascript

I have list and I am using bindaggregation to bind my model data.
below is my code.
Controller
this.oList = this.byId("list");
this.oListItem = this.byId("MAIN_LIST_ITEM").clone();
this.oList.bindAggregation("items", {path: '/myListSet',
template: this.oListItem,
filters: this.searchFilters,
});
this.registerMasterListBind(this.oList);
console.log("firststatus"+this.oListItem.getFirstStatus().getText());
}
My requirement is to manipulate the value of the firststatus, Fristly I am trying to get the value with the below line and unfortunately it is diplaying null value. Can someone advise how to get the value of the firststatus?
this.oListItem.getFirstStatus().getText()
Update - View
<List id="list" growing="true" growingThreshold="20" growingScrollToLoad="true" showNoData="true" mode="{device>/listMode}"
select="_handleSelect">
<ObjectListItem id="MAIN_LIST_ITEM" type="{device>/listItemType}" press="_handleItemPress" title="{sName}">
<markers>
<ObjectMarker type="Flagged"/>
</markers>
<firstStatus>
<ObjectStatus text="{Status1Txt}"/>
</firstStatus>
<attributes>
<ObjectAttribute id="ATTR1" text="{SNumber}"/>
<ObjectAttribute id="ATTR2" text="{PTxt}"/>
</attributes>
<secondStatus>
<ObjectStatus text="{Status2Txt}"/>
</secondStatus>
</ObjectListItem>
</List>

So if I understand correctly, you're trying to retrieve the status text of an entry, but what you're actually doing is retrieving the default template of the ObjectListItem.
If you want the correct value for an entry, you can try this. This returns a filled clone of the default template. You can't clone() an empty template and expect it to have values in it.
Hope this helps, lampstand.
this.byId("list").getItems()[0].getFirstStatus().getText();

Related

ember-power-select Custom Search Action and 'selected' with external data

Overview
I'm using ember-power-select in a Ember.js 3.8 project - and it half works and half doesn't !
To make the question more readable I've put all the code at the bottom of the question.
Situation
The select is configured to fetch data from an API endpoint and provide the user with a set of possible options to select from.
The route (routes/guest/new-using-ember-power-select.js) involved does a createRecord of the model (models/guest.js) and then, ideally, changes made to both of the form elements (templates/guests/new-using-ember-power-select.js and templates/components/guest-form-ember-power-select.hbs) are reflected back into that record in the data store.
Issue
This works fine for the text input but I can't make it work for the ember-power-select.
In the current configuration (shown below) the user may :
search for options to select;
select an option and;
have that selection reflected back into the guest instance in the data store. However the choice made is not reflected in the user interface - there appears to have been no selection made.
I would really appreciate someone pointing out what I'm doing wrong here. I feel like it might be quite a small thing but it did occur to me that I have to manage the state of the select via properties in the component and only when the form is submitted update the underlying data-store .... I would prefer not to do that but I would be interested to know if that was thought to be the best idea.
Thanks
EDIT 1: I forgot to say that I have attempted to alter the onchange property of the ember-power-select so that instead of looking like this
onchange=(action "nationalityChangeAction")
... it looks like this ...
onchange=(action (mut item.nationality))
That has the effect that :
the value selected is visible in the form (as you would normally expect but unlike my current effort) but
the value placed into the underlying data store record is not a two character country code but instead an instance of the array returned the API call, an object with two properties {"name":"New Zealand","alpha2Code":"NZ"}.
Model
//app/models/guest.js
import DS from 'ember-data';
import { validator, buildValidations } from 'ember-cp-validations';
const Validations = buildValidations({
name: [
validator('presence', true),
],
nationality: [
validator('presence', true),
],
});
export default DS.Model.extend( Validations, {
name: DS.attr('string'),
nationality: DS.attr('string')
});
Route
//app/routes/guest/new-using-ember-power-select.js
import Route from '#ember/routing/route';
export default Route.extend({
model() {
return this.store.createRecord('guest', {
name: "",
nationality: ""
});
},
actions: {
updateNationality(slctnValue) {
this.controller.model.set('nationality' , slctnValue);
},
}
});
Template
//app/templates/guests/new-using-ember-power-select.js
<h2>Guest: Add New</h2>
<div class="well well-sm">
Demonstration of 'ember-power-select'
</div>
{{guest-form-ember-power-select
item=model
changeNationalityHandler="updateNationality"
updateRecordHandler="updateRecord"
cancelHandler="cancelAndExit"
}}
{{outlet}}
Component Template
//app/templates/components/guest-form-ember-power-select.hbs
<div class="form-vertical">
{{!-- Guest Name --}}
<div class="form-group">
<label class="control-label">Name</label>
<div class="">
{{ input type="text"
value=item.name
class="form-control"
placeholder="The name of the Guest"
focus-out=(action (mut this.errMsgDspCntrl.nameError) true)
}}
</div>
{{#if this.errMsgDspCntrl.nameError}}
<div class="text-danger">
{{v-get item 'name' 'message'}}
</div>
{{/if}}
</div>
<div class="form-group">
<label class="control-label">Countries (using power-select)</label>
<div class="">
{{#power-select
searchPlaceholder="Text to provide user info about what they can search on"
search=(action "searchCountries")
selected=item.nationality
onchange=(action (mut item.nationality))
as |countries|
}}
{{countries.name}}
{{/power-select}}
</div>
{{#if this.errMsgDspCntrl.nationalityError}}
<div class="text-danger">
{{v-get item 'nationality' 'message'}}
</div>
{{/if}}
</div>
{{!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--}}
{{!-- Buttons --}}
<div class="form-group">
<div class="">
<button type="submit" class="btn btn-default" {{action "buttonSaveClicked" item}}>{{buttonLabel}}</button>
<button type="button" class="btn btn-default" {{action "buttonCancelClicked" item}} >Cancel</button>
</div>
</div>
</div>
{{yield}}
Component
//app/components/guest-form-ember-power-select.js
import Component from '#ember/component';
export default Component.extend({
actions:{
searchCountries(term) {
//Response to :
//
//https://restcountries.eu/rest/v2/name/z?fields=name;alpha2Code
//
//
//looks like this
// [
// ...
// {"name":"New Zealand","alpha2Code":"NZ"}
// ...
// ]
//
let url = `https://restcountries.eu/rest/v2/name/${term}?fields=name;alpha2Code`
let dbg = fetch(url)
.then(function(response) {
return response.json();
});
return dbg;
},
nationalityChangeAction(slctn){
this.sendAction('changeNationalityHandler', slctn.alpha2Code);
},
}
});
I'm going to answer showing some diffs with the changes required to make the select work in your repo: https://github.com/shearichard/emberjs-select-addon-comparison
The key thing to understand is that ember-power-select receives a block, in your case
as |country|}}
{{country.name}}
{{/power-select}}
That block will be called to render each of the options, but also the selected option. In this case, the options are country objects with this shape: {"name":"American Samoa","alpha2Code":"AS"}. That is why you call {{country.name}} to render it. However, with your approach, the selected value that you are passing in is not an object with a name property. In fact is not even an object, but the string "AS" in the case of American Samoa, so you can output the name property of a string.
In your situation, the information you store (the country code) is not enough to display a nice "American Samoa" in the trigger of the select, and since you don't know the countries before hand until you make a search you can't look the country with that country code.
If you don't have an edit form, my suggestion is to store the entire selected country in a property which is the one you pass to selected.
diff --git a/app/components/guest-form-ember-power-select.js b/app/components/guest-form-ember-power-select.js
index edf9390..2467d85 100644
--- a/app/components/guest-form-ember-power-select.js
+++ b/app/components/guest-form-ember-power-select.js
## -25,6 +25,8 ## export default Component.extend({
//messages
nameOfErrMsgDspCntrl : 'errMsgDspCntrl',
+ nationality: undefined,
+
actions:{
searchCountries(term) {
## -73,7 +75,7 ## export default Component.extend({
},
nationalityChangeAction(slctn){
- //this.set(this.myValue, slctn);
+ this.set('nationality', slctn);
this.sendAction('changeNationalityHandler', slctn.alpha2Code);
},
diff --git a/app/templates/components/guest-form-ember-power-select.hbs b/app/templates/components/guest-form-ember-power-select.hbs
index 56f007d..5c69834 100644
--- a/app/templates/components/guest-form-ember-power-select.hbs
+++ b/app/templates/components/guest-form-ember-power-select.hbs
## -24,7 +24,7 ##
{{#power-select
searchPlaceholder="Text to provide user info about what they can search on"
search=(action "searchCountries")
- selected=item.nationality
+ selected=nationality
onchange=(action "nationalityChangeAction")
as |countries|
}}
## -36,14 +36,14 ##
This works as long as you don't want to edit the nationality of a user you created before, perhaps even weeks ago. You won't have a reference to the country in that case, only the country code. In that situation I'd recommend making selected a computed property that returns a promise the resolves to the country object with the user's country code, if your API allows that. And seems that it does, so the BEST solution would be
diff --git a/app/components/guest-form-ember-power-select.js b/app/components/guest-form-ember-power-select.js
index edf9390..f889734 100644
--- a/app/components/guest-form-ember-power-select.js
+++ b/app/components/guest-form-ember-power-select.js
## -1,4 +1,5 ##
import Component from '#ember/component';
+import { computed } from '#ember/object';
export default Component.extend({
buttonLabel: 'Save',
## -25,6 +26,16 ## export default Component.extend({
//messages
nameOfErrMsgDspCntrl : 'errMsgDspCntrl',
+ nationality: computed('item.nationality', function() {
+ let countryCode = this.get('item.nationality');
+ if (countryCode) {
+ return fetch(`https://restcountries.eu/rest/v2/alpha/${countryCode}?fields=name;alpha2Code`)
+ .then(function (response) {
+ return response.json();
+ });
+ }
+ }),
+
This last one will fetch the information for the country you know the code of.
selected property must be an element included in options provided to Ember Power Select. In your scenario you are not using options property but setting the options through search action but that doesn't make a big difference.
Your search action return an array of objects (e.g. [{"name":"New Zealand","alpha2Code":"NZ"}]). nationalityChangeAction sets the selected value to the value of alpha2Code. Therefore selected is not included in options:
[{"name":"New Zealand","alpha2Code":"NZ"}].includes('NZ') // false
So the state your Power Selects ends in is similar to this one:
<PowerSelect
#options={{array
(hash foo="bar")
}}
#selected="bar"
/>
A simplified version of what you are doing look like this:
<PowerSelect
#options={{array
(hash foo="bar")
}}
#selected={{selected}}
#onchange={{action (mut selected) value="foo"}}
/>
Please have a look in Ember Power Select documentation regarding the difference between using options and search:
When that's the case you can provide a search action instead of options (it's the only situation where the options are not mandatory) that will be invoked with the search term whenever the user types on the search box.
[...]
There is only three things to know about this action:
- You should return a collection or a promise that resolves to a collection from this action.
- You can provide both options and a search action. Those options will be the initial set of options, but as soon as the user performs a search, the results of that search will be displayed instead.
Therefore it doesn't make a difference for your issue if you are using options or returning a collection from search action. It all comes down to having a selected value that is not part of the collection bound to options or returned by search action.
This is actually the reason why your UI is working as expected if using onchange=(action (mut item.nationality)). In that case item.nationality is that to the selected object in collection returned by search (e.g. {"name":"New Zealand","alpha2Code":"NZ"}) and not to the value of it's alpha2Code property.
I'm using angle bracket component invocation syntax in my answer. Hope that fine. It makes it easier to read it in my opinion.

Make single column editable in a UI5 smart table

I want to create a smart table with an editable column. I am using the metadata annotations to generate the table. The table property (editable = "true") makes all the columns editable. In my case, I want to make only a single column editable. I tried with the property sap:updatable="true" but it didn't work. Any ideas.
Thanks in advance.
This is my view by the way:
<mvc:View
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
controllerName="QuickStartApplication3.controller.View1"
xmlns:smartFilterBar="sap.ui.comp.smartfilterbar"
xmlns:smartTable="sap.ui.comp.smarttable">
<smartFilterBar:SmartFilterBar
id="smartFilterBar"
entityType="Product">
<smartFilterBar:controlConfiguration>
<smartFilterBar:ControlConfiguration
key="ProductId"
visibleInAdvancedArea="true"
preventInitialDataFetchInValueHelpDialog="true" >
</smartFilterBar:ControlConfiguration>
</smartFilterBar:controlConfiguration>
<smartFilterBar:controlConfiguration>
<smartFilterBar:ControlConfiguration
key="Name"
visibleInAdvancedArea="true"
preventInitialDataFetchInValueHelpDialog="true" >
</smartFilterBar:ControlConfiguration>
</smartFilterBar:controlConfiguration>
<smartFilterBar:controlConfiguration>
<smartFilterBar:ControlConfiguration
key="Category"
visibleInAdvancedArea="true"
preventInitialDataFetchInValueHelpDialog="true" >
</smartFilterBar:ControlConfiguration>
</smartFilterBar:controlConfiguration>
</smartFilterBar:SmartFilterBar>
<smartTable:SmartTable
id="smartTable_ResponsiveTable"
smartFilterId="smartFilterBar"
tableType="ResponsiveTable"
editable="false"
entitySet="Products"
useVariantManagement="false"
useTablePersonalisation="true"
header="Products"
showRowCount="true"
useExportToExcel="true"
enableAutoBinding="true">
</smartTable:SmartTable>
</mvc:View>
I don't have enough rep to put a simple comment to your question but could you provide a sample of your view so that I can understand more precisely what you do?
Have you tried the use the sap.m.Table object as detailed here in the documentation :
https://sapui5.hana.ondemand.com/explored.html#/sample/sap.m.sample.TableEditable/preview
In order to have only certain cell editable you can manage it throught the template used see below how I changed the template in the onInit method of the controller:
onInit : function(evt) {
this.oModel = new JSONModel(jQuery.sap.getModulePath(
"sap.ui.demo.mock", "/products.json"));
this.oTable = this.getView().byId("idProductsTable");
this.getView().setModel(this.oModel);
this.oReadOnlyTemplate = this.getView().byId(
"idProductsTable").removeItem(0);
this.rebindTable(this.oReadOnlyTemplate, "Navigation");
this.oEditableTemplate = new sap.m.ColumnListItem({
cells : [ new sap.m.ObjectIdentifier({
title : "{Name}",
text : "{ProductId}"
}), new sap.m.Input({
value : "{Quantity}",
description : "{UoM}"
}), new sap.m.Input({
value : "{WeightMeasure}",
description : "{WeightUnit}"
}), new sap.m.Input({
value : "{Price}",
description : "{CurrencyCode}"
}) ]
});
If you don't use this object, then please tell me which one, I might be able to help.
Please have a look at the “Preview”-screenshot and the metadata.xml.
What happens if you set editable = "true" and annotate all properties of ‘Products’ with sap:updatable=”false”,
except for the single property that shall be editable, which you set to sap:updatable=”true”.
Don’t annotate entity set ‘Products’ with sap:updatable (or make sure that sap:updatable=”true”).

Vue list items not re-rendered on state change

I have some array of object, when user click button I fetch new array and display display some results.
It works fine until I fetch second array. When I fetch first array with one element and then fetch array with two elements it change (add or remove) only second element.
How I change array value:
fetchAsync(result){
this.issue = result.body;
}
How issues looks like?
const issues = [
{
"id":100,
"key":"DEMO-123",
"summary":"Demo issue description",
"devices":[
{
"id":100,
"name":"iPhone6S",
"browsers":[
{
"id":100,
"name":"Safari",
"displayVariants":[
{
"id":100,
"issueKey":"DEMO-123",
"state":1,
"browserName":"safari",
"user":"user-1",
"landScope":false
}
]
}
]
}
]
}
]
and the value which was changed is issues[].devices[].browsers[].displayVariants[].state
How to force Vue to rerender this component when nested change appear?
[ EDIT ]
I render issues like this:
<tr v-for="issue in issues">
<td>
<div>{{ issue.key }}</div>
<div class="issue-description">[ {{ issue.summary }} ]</div>
</td>
<template v-for="d in issue.devices">
<td v-for="browser in d.browsers">
<!--{{ d }}-->
<device v-for="variant in browser.displayVariants"
:variant="variant"
:browserId="browser.id"
:issueKey="issue.key"
:issueId="issue.id"
:deviceId="d.id"></device>
</td>
</template>
</tr>
and device template
<template>
<svg viewBox="0 0 30 30" class="mobileSVG" #click="changeState" :class="[state, {landscape: variant.landScope}]">
<use xlink:href="#mobile"/>
</svg>
</template>
I think adding keys to your list will solve the problem:
https://v2.vuejs.org/v2/guide/list.html#key
Vue tries to make minimum changes to the DOM, and think that the first item has not changed, so it is not re-rendered. In your case you already have the id, using that as key should solve the issue.
Vue cannot detect the following changes made to the array.
Here is the documentation.
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
vm refers to component instance.
To overcome the limitation 1 do:
Vue.set(items, indexOfItem, newValue)
For limitation 2:
items.splice(newLength)
So in your case you could do
this.$set(this.issues[0].devices[whateverIndex].browsers[anyIndex].displayVariants, indexOfVariant, valueOfVariant)

Kendo autocomplete showing [object object] instead of propery value

I'm trying to configure kendo autocomplete using their tutorial.
The problem is that autocomplete control display objects instead of property value which I set in kendo initialization (see capture):
#(
Html.Kendo().AutoComplete()
.Name("products")
.Placeholder("Find Product...")
.DataTextField("Name")
.Template("<span><img src='/Content/Images/default-photo.jpg' " +
"width='20' height='20' /> ${data}</span>")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetProducts", "Search")
.Data("onAdditionalData");
})
.ServerFiltering(true);
})
)
<script>
function onAdditionalData() {
return {
text: $("#products").val()
};
}
</script>
After I click this item the name is showing properly:
My action return type is return Json(products, JsonRequestBehavior.AllowGet);
where products is ICollection<VmProduct>
Whats going on?
You've set the DataTextField but you're overwriting it when you set the Template, because kendo will execute the template instead of getting the field you set. But that's not the problem, the problem is that in your template you're printing the data object, which is.. an object actually. You need to print it's property related to the suggestion text, e.g.:
.Template("<span><img src='/Content/Images/default-photo.jpg' " +
"width='20' height='20' /> ${data.Name}</span>")
Give it a try and tell us what happens.

Getting multiple tag values when use find method in jQuery

I am using .find method to get value of xml tag. In my xml if xmltag is repeat, it gives me value of all tag.
Example
<Response>
<Result>0</Result>
<Message>Tenant Information fetched for tenantID=1</Message>
<Data>
<Tenants>
<row>
<TenantID>1</TenantID>
<TenantTypeID>1</TenantTypeID>
<CardCode>CLINSH0184</CardCode>
<CustomerName>S. H. Pitkar Orthotools Pvt Ltd</CustomerName>
<CustomerGroup>VAT Customer</CustomerGroup>
<ServiceContract>69</ServiceContract>
<SUserID>S0004493123</SUserID>
<ContactPerson>Nupur Pitkar</ContactPerson>
<Phone1>123</Phone1>
<Phone2>456</Phone2>
<Mobile>789</Mobile>
<Email>abcd#inecom.com.sg</Email>
<StartDate>2014-01-01T00:00:00</StartDate>
<EndDate>2015-01-01T00:00:00</EndDate>
<Active>1</Active>
<B1Version>8.82</B1Version>
<PatchLevel>12</PatchLevel>
<SqlVersion>2008</SqlVersion>
<ServiceURL>http://localhost:8932/CRMService.asmx</ServiceURL>
<DataBaseName>WTS</DataBaseName>
<ExpiredMessage>Subscription to this account is expired. Please contact System Administrator</ExpiredMessage>
<ExpirationMessage>Subscription to this account will expire soon. Please contact System Administrator</ExpirationMessage>
<WarningDays>3</WarningDays>
<logo>CLINSH0184.jpg</logo>
<LicenseDetails>
<row>
<ItemCode>SaaS-CRM-Sales</ItemCode>
<ItemName>SaaS - CRM Module-Sales</ItemName>
<StartDate>2014-07-15T00:00:00</StartDate>
<EndDate>2014-08-15T00:00:00</EndDate>
<License>1</License>
</row>
<row>
<ItemCode>SaaS-CRM-Purchase</ItemCode>
<ItemName>SaaS - CRM Module-Purchase</ItemName>
<StartDate>2014-07-15T00:00:00</StartDate>
<EndDate>2014-08-15T00:00:00</EndDate>
<License>2</License>
</row>
<row>
<ItemCode>SaaS-CRM-Service</ItemCode>
<ItemName>SaaS - CRM Module-Service</ItemName>
<StartDate>2014-07-15T00:00:00</StartDate>
<EndDate>2014-08-15T00:00:00</EndDate>
<License>3</License>
</row>
</LicenseDetails>
</row>
</Tenants>
</Data>
In the mention xml, I use below code to get the value of xml tag.
var bindName='Response Data Tenants row StartDate';
$xmlNode = $xml.find(bindName);
if ($xmlNode != null) {
var value = $xmlNode.text();
//do some thing with code.
//here I am geting all value of xml tag start with 'StartDate'
//I am expecting value of only single node specified in bindName variable.
}
Can anybody help me in this context?
If you're looking for only the StartDate in the row nodes, you should use the direct child selector instead of the descendant selector. Your final query selector should be:
var bindName='Response Data Tenants > row > StartDate';
That will exclude the StartDate from the LicenseDetails sections.
Keep in mind that if you have multiple Tenants or multiple row sections inside a single Tenant you may still get more than one result and should iterate over it using .each():
$xmlNode = $xml.find(bindName);
if ($xmlNode.length > 0) {
$xmlNode.each(function(index, item) {
console.log($(this).text());
});
}
You need to specify the row you want, now it matches startdate in every row.
Change
var bindName='Response Data Tenants row StartDate';
to
var bindName='Response Data Tenants row row:first StartDate';
to match the first row.
Update
Sorry, updated because I found the upper level row element too.

Categories