How to get array length with Expression Binding? - javascript

As the title implies, I do have the following data:
{
"modelExample": [
{ "id": 0 },
{ "id": 1 },
{ "id": 2 }
]
}
The JSONModel has three entries, which essentially equates to 3 in length.
How do I get the length through an Expression Binding statement?
My Attempt:
<Text text="{ ${modelExample>/}.length}"

OK, here is a sample. Click on Run code snippet to see it in action:
sap.ui.require([
"sap/ui/core/Core",
], Core => Core.attachInit(() => sap.ui.require([
"sap/ui/core/Fragment",
"sap/ui/model/json/JSONModel",
], async (Fragment, JSONModel) => {
"use strict";
// sample XML definition:
const definition = '<ObjectStatus title="Length" text="{= ${/modelExample}.length}" class="sapMObjectStatusLarge sapUiTinyMargin" inverted="true" state="Information" xmlns="sap.m" />';
const control = await Fragment.load({ definition });
const model = new JSONModel({
modelExample: [
{ id: 0, /*...*/},
{ id: 1, /*...*/},
{ id: 2, /*...*/},
],
});
control.setModel(model).placeAt("content");
})));
<script id="sap-ui-bootstrap" src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.ui.core, sap.m"
data-sap-ui-async="true"
data-sap-ui-compatversion="edge"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-xx-waitfortheme="init"
></script>
<body id="content" class="sapUiBody"></body>
As you can see, I'm using text="{= ${/modelExample}.length}" which displays the correct length of the array:
If the model is named (e.g. "myModel"), it should be {= ${myModel>/modelExample}.length}.
Keep in mind that expression binding syntax requires {= (OneWay) or {:= (OneTime) at the beginning.
To actually make use of the expression binding, the bootstrap config bindingSyntax needs be set to "complex" or compatVersion to "edge", as mentioned in this post. E.g. in index.html:
<script id="sap-ui-bootstrap" data-sap-ui-compatversion="edge" ...>

Related

how to v-model on different array vue 3

Hi I was trying to use a v-model an input to a value in object in an array of object in Vue 3. The complexity lies in the fact the object is first processed by a function. And that it need to be processed every time when a change is made to an input. Here is my code (and a sandbox link) :
<template>
<div id="app">
<div v-for="param in process(parameters)" :key="param">
Name : {{param.name}} Value : <input v-model="param.value">
</div>
{{parameters}}
</div>
</template>
<script>
export default {
name: "App",
data(){
return{
parameters :[
{'name':'Richard Stallman','value':'cool dude'},
{'name':'Linus Torvalds','value':'very cool dude'},
{'name':'Dennis Ritchie','value':'very very cool dude'}
]
}
},
methods:{
process(parameters){
const results = parameters.map( param =>{
return {name:param.name+' extra text',
value:param.value+' extra text',
}
})
return results
}
}
};
</script>
I just want the original parameters to change when something is types in the inputs. Maybe #change could be of use. But I didn't find a fix with #change. Does anyone know a solution to my problem? Thanks in advance.
Use computed property to get reactive state of the data.
Working Demo :
new Vue({
el: '#app',
data: {
parameters :[
{'name':'Richard Stallman','value':'cool dude'},
{'name':'Linus Torvalds','value':'very cool dude'},
{'name':'Dennis Ritchie','value':'very very cool dude'}
]
},
computed: {
process() {
const results = this.parameters.map((param) => {
return {
name: param.name + ' extra text',
value: param.value + ' extra text'
}
});
return results;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="param in process" :key="param">
Name : {{param.name}}
Value : <input v-model="param.value">
</div><br>
<strong>Orinigal Data :</strong> {{parameters}}
</div>
I am not entirely sure I understood whether the person should be able to see/edit the text you added within you processing method.
Anyway, I think this sample of code should solve you problem :
<template>
<div id="app">
<div v-for="param in parameters" :key="param.name">
Name : {{ param.name }} Value : <input v-model="param.value" />
</div>
{{ process }}
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
parameters: [
{ name: "Richard Stallman", value: "cool dude" },
{ name: "Linus Torvalds", value: "very cool dude" },
{ name: "Dennis Ritchie", value: "very very cool dude" },
],
};
},
computed: {
process: function() {
const results = this.parameters.map((param) => {
return {
name: param.name + " extra text",
value: param.value + " extra text",
};
});
return results;
},
},
};
</script>
So, we're iterating through the parameters array directly, adding an input on the value just like you did.
When you type in the input, you update the parameter linked to it, in live.
I just switched the method you made into a computed method.
This way, every time parameters is updated, "process" is also updated because it's depending on it directly.
I also removed passing the "parameters" argument, it's in the component data, you can just access it directly.
This way, using "process" just like any variable, you'll always have the updated parameters + what you added to em.

Dynamically change select input in Bootstrap Vue

I am trying to set some objects in a Bootstrap-Vue form select which I get via JSON.
The JSON is made up of teacher objects from the following fields:
[
{
"id": 1,
"name": "John",
"surname": "Doe",
"email": "john.doe#gmail.com"
}
]
What I'm trying to do is put the name and surname in the select list, that is the full name.
I have already managed to do this via a computed property by processing the list.
But now I want that when I select a teacher, the list of courses is filtered according to the chosen teacher.
To do this I need the teacher's email, which I can't recover, having processed the teachers to get the full name.
Consequently, I can't even update the list of courses based on the teacher chosen.
This is the code for the template:
<b-form-group
id="input-group-3"
label="Docente:"
label-for="input-3"
>
<b-form-select
v-model="teacher"
:options="teachers"
value-field="item"
text-field="fullName"
required
#change="filterCourse"
></b-form-select>
<div class="mt-3">
Selected: <strong>{{ teacher }}</strong>
</div>
</b-form-group>
This is the script code:
import { mapGetters, mapActions } from "vuex";
export default {
data() {
return {
teacher: "",
course: "",
};
},
created: function() {
this.GetActiveTeachers();
this.GetActiveCourses();
},
computed: {
...mapGetters({
ActiveTeacherList: "StateActiveTeachers",
ActiveCourseList: "StateActiveCourses",
FilteredTeacherList: "StateTeacherByCourse",
FilteredCourseList: "StateCourseByTeacher",
}),
teachers: function() {
let list = [];
this.ActiveTeacherList.forEach((element) => {
let teacher = element.name + " " + element.surname;
list.push(teacher);
});
return list;
},
},
methods: {
...mapActions([
"GetActiveTeachers",
"GetActiveCourses",
"GetCourseByTeacher",
"GetTeacherByCourse",
"AssignTeaching",
]),
async filterCourse() {
const Teacher = {
teacherEmail: "john.doe#gmail.com", // For testing purpose
};
try {
await this.GetCourseByTeacher(Teacher);
} catch {
console.log("ERROR");
}
},
async filterTeacher() {
const Course = {
title: "Programming", // For testing purpose
};
try {
await this.GetTeacherByCourse(Course);
} catch {
console.log("ERROR");
}
},
},
};
You're currently using the simplest notation that Bootstrap Vue offers for form selects, an array of strings.
I suggest you switch to use their object notation, which will allow you to specify the text (what you show in the list) separately from the value (what's sent to the select's v-model).
This way, you'll be able to access all the data of the teacher object that you need, while still being able to display only the data you'd like.
We can do this by swapping the forEach() in your teachers computed property for map():
teachers() {
return this.ActiveTeacherList.map((teacher) => ({
text: teacher.name + " " + teacher.surname,
value: teacher
}));
},
Then, all you need to do is update your filterCourse() handler to use the new syntax, eg.:
async filterCourse() {
const Teacher = {
teacherEmail: this.teacher.email,
};
try {
await this.GetCourseByTeacher(Teacher);
} catch {
console.log("ERROR");
}
},
As a final note, if you don't want or need the full object as the value, then you can mold it to be whatever you need, that's the beauty of this syntax.
For example, you want the full name and email, instead of the parts:
value: {
fullName: teacher.name + " " + teacher.surname,
email: teacher.email
}
Here's two different options you can do.
One would be to generate the <option>'s inside the select yourself, using a v-for looping over your teachers, and binding the email property to the value, and displaying the name and surname inside the option.
This will make your <b-select>'s v-model return the chosen teachers e-mail, which you can then use in your filter.
new Vue({
el: '#app',
data() {
return {
selectedTeacher: null,
activeTeachers: [{
"id": 1,
"name": "Dickerson",
"surname": "Macdonald",
"email": "dickerson.macdonald#example.com"
},
{
"id": 2,
"name": "Larsen",
"surname": "Shaw",
"email": "larsen.shaw#example.com"
},
{
"id": 3,
"name": "Geneva",
"surname": "Wilson",
"email": "geneva.wilson#example.com"
}
]
}
}
})
<link href="https://unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.js"></script>
<div id="app">
<b-select v-model="selectedTeacher">
<option v-for="teacher in activeTeachers" :value="teacher.email">
{{ teacher.name }} {{ teacher.surname }}
</option>
</b-select>
{{ selectedTeacher }}
</div>
The other option would be to change your computed to return an array of objects instead of simple strings as you're currently doing.
By default <b-select> expects the properties value and text if you use an array of objects in the options prop.
Here you would bind the email for each teacher to the value, and the name and surname to the text prop.
This will make your <b-select>'s v-model return the chosen teachers e-mail, which you can then use in your filter.
Reference: https://bootstrap-vue.org/docs/components/form-select#options-property
new Vue({
el: '#app',
data() {
return {
selectedTeacher: null,
activeTeachers: [{
"id": 1,
"name": "Dickerson",
"surname": "Macdonald",
"email": "dickerson.macdonald#example.com"
},
{
"id": 2,
"name": "Larsen",
"surname": "Shaw",
"email": "larsen.shaw#example.com"
},
{
"id": 3,
"name": "Geneva",
"surname": "Wilson",
"email": "geneva.wilson#example.com"
}
]
}
},
computed: {
teacherOptions() {
return this.activeTeachers.map(teacher => ({
value: teacher.email,
text: `${teacher.name} ${teacher.surname}`
}));
}
}
})
<link href="https://unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.js"></script>
<div id="app">
<b-select v-model="selectedTeacher" :options="teacherOptions"></b-select>
{{ selectedTeacher }}
</div>

How to display the summarized value of multiple rows numbers in a Text [SAPUI5]

as the title states, let's say i do have the following model:
model = {
0:{"count":3},
1:{"count":4}
};
Question 1: How can i display count as summarized number i.e. 7 in a Text?
<Text text="{model>count}" />
Question 2: Is there a technicality, which could allow such summarization ?
What i tried:
Formatter function.
<Text text="{ path: 'model>count', formatter:'.formmater.sumCount'}" />
Issue: Formatter, function sumCount, does get the value of each row, i.e. 3, 4 etc..., which means it doesn't have an overarching capability to loop through the whole model and add all the counters and return the summarized value to the <Text>
Question 2: Is there a technicality, which could allow such summarization ?
You can achieve that via an formatter.
Suppose you have defined the following sap.ui.model.json.JSONModel in the controller:
var oMyModel = new sap.ui.model.json.JSONModel({
0: { "count": 3 },
1: { "count": 4 }
});
this.getView().setModel(oMyModel, "myModel");
and you have the following formatter.js file:
sap.ui.define([], function () {
"use strict";
return {
sumTwoEntries: function (iValue1, iValue2) {
return iValue1 + iValue2;
},
sumAllEntries: function (oObject) {
var iSum = 0;
var aObjectKeys = Object.keys(oObject);
for (var i = 0; i < aObjectKeys.length; i++) {
iSum += oObject[i].count;
}
return iSum;
}
};
});
this will work:
<!-- Displays the first Entrie -->
<Text text="{myModel>/0/count}"/>
<!-- Displays the second Entrie -->
<Text text="{myModel>/1/count}"/>
<!-- Displays the summarized Value of both Entries -->
<Text text="{ parts: [ { path: 'myModel>/0/count'}, { path: 'myModel>/1/count'}], formatter: '.formatter.sumTwoEntries'}"/>
<!-- Displays the summarized Value of x Entries -->
<Text text="{ path: 'myModel>/', formatter: '.formatter.sumAllEntries'}"/>

Navigating a json file to create desired array for jquery autocomplete

I am programming an autocomplete function for a search bar that features names of places in Norway.
I collect the data from a REST api URL provided by a third party.
Example with input "st" and two results:
{
"sokStatus":{
"ok":"true",
"melding":""
},
"totaltAntallTreff":"81280",
"stedsnavn":[
{
"ssrId":"23149",
"navnetype":"By",
"kommunenavn":"Larvik",
"fylkesnavn":"Vestfold",
"stedsnavn":"Stavern",
"aust":"214841.84",
"nord":"6550500.29",
"skrivemaatestatus":"Godkjent",
"spraak":"NO",
"skrivemaatenavn":"Stavern",
"epsgKode":"25833"
},
{
"ssrId":"506202",
"navnetype":"By",
"kommunenavn":"Stord",
"fylkesnavn":"Hordaland",
"stedsnavn":"Stord",
"aust":"-32194.93",
"nord":"6665261.05",
"skrivemaatestatus":"Godkjent",
"spraak":"NO",
"skrivemaatenavn":"Stord",
"epsgKode":"25833"
}
]
}
I want to have the autocomplete array contain the "stedsnavn" features from all the returned results in the json file. so for the above example it would be [Stavern, Stord].
I built my code based off a template/tutorial i found online. When I run it now the autocomplete suggestion is the "totaltAntallTreff" feature so for the json above it would suggest 81280.
Edit: What I really need to know is how to properly query the json where I now only have response(data). I have tried several methods ($.map, $.each) but whenever I modify my code it ends up giving no autocomplete suggestions.
See my code below
$(function () {
var getData = function (request, response) {
$.getJSON(
"https://ws.geonorge.no/SKWS3Index/ssr/json/sok?antPerSide=5&eksakteForst=false&navn=" + request.term + "*",
function (data) {
(response(data));
});
};
var selectItem = function (event, ui) {
$("#myText").val(ui.item.value);
return false;
}
$("#myText").autocomplete({
source: getData,
select: selectItem,
minLength: 1,
change: function() {
$("#myText").val("").css("display", 2);
}
});
});
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery-ui.css" />
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.0/themes/base/jquery.ui.autocomplete.css" />
<script src="http://code.jquery.com/jquery-1.8.3.js"></script>
<script src="http://code.jquery.com/ui/1.10.0/jquery-ui.js"></script>
<div id="menu-container">
<input type="text" id="myText" />
</div>
Given the JSON structure provided, you could get the result with the following:
let json_data = {
"sokStatus": {
"ok": "true",
"melding": ""
},
"totaltAntallTreff": "81280",
"stedsnavn": [{
"ssrId": "23149",
"navnetype": "By",
"kommunenavn": "Larvik",
"fylkesnavn": "Vestfold",
"stedsnavn": "Stavern",
"aust": "214841.84",
"nord": "6550500.29",
"skrivemaatestatus": "Godkjent",
"spraak": "NO",
"skrivemaatenavn": "Stavern",
"epsgKode": "25833"
},
{
"ssrId": "506202",
"navnetype": "By",
"kommunenavn": "Stord",
"fylkesnavn": "Hordaland",
"stedsnavn": "Stord",
"aust": "-32194.93",
"nord": "6665261.05",
"skrivemaatestatus": "Godkjent",
"spraak": "NO",
"skrivemaatenavn": "Stord",
"epsgKode": "25833"
}
]
}
let values = json_data.stedsnavn.map(item => item.skrivemaatenavn);
values.forEach(value => {
$("#list").append(`<li>${value}</li>`);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="list"><ul>
As to what json_data.stedsnavn.map(item => item.skrivemaatenavn); is doing:
json_data.stedsnavn.map(item => item.skrivemaatenavn);
// Get the "stedsnavn" key from the data, an array
// Map each object in the array to
// its "skrivemaatenavn" key

I need to filter an array in AngularJS

I need to filter an array that shown below by "all permission value" to send it on the server. It is an example. Thanks for any help.
<!DOCTYPE html>
<html ng-app>
<head>
<script src="./angular.js"></script>
<script>
function MainCtrl($scope) {
$scope.accounts = [{ name: 'KL', company: 'Alpha', permission: 'all'},
{ name: 'Jem', company: 'Altes', permission: 'no' },
{ name: 'Osama', company: 'Solar', permission: 'no' },
{ name: 'Victor', company: 'Osteriks', permission: 'all' }];
// I'd like to get here a filtered array by "all permission" from the View block filter
$scope.filteredAccounts = // [ { name: 'KL', company: 'Alpha', permission: 'all'},
// { name: 'Victor', company: 'Osteriks', permission: 'all' }];
}
</script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="account in accounts | filter: { permission: 'all' }">
<p>{{ account.name }}</p>
</div>
</body>
</html>
I didn't feel like including underscore or lodash in my project just because i wanted the findWhere function so i added it to angular myself:
simply include this outside of angular and change the "angular.element(document)" to the element you booted your app on.
angular.findWhere = function(collection,search) {
var $filter = angular.element(document).injector().get("$filter"),
refined = $filter('filter')(collection,search);
return refined[0];
}
then you just use it like this:
angular.findWhere($scope.accounts,{permission: 'all'});
Most simple way is use lodash and write
$scope.filteredAccounts = _.where($scope.accounts,{permission: 'all'});
You can write custom filters in AngularJS. Here is a link to a document that talks about how to create one: https://docs.angularjs.org/tutorial/step_09
Your custom filter might look something like this:
myApp.filter('permissionFilter', function () {
return function (accounts, permission) {
var filtered = [];
angular.forEach(accounts, function (account) {
if (account.permission === permission) {
filtered.push(account);
}
});
return filtered;
};
});
And the usage would be something like this:
<div ng-controller="MainController">
<div ng-repeat="account in accounts | permissionFilter:'all'">
<p>{{ account.name }}</p>
</div>
</div>
This would allow you to use this filter to filter any kind of permission. In other words, you can change 'all' to 'no' and it will return Jem and Osama. You could take that a step further and bind to the filter variable, so you could let the user decide what they wanted to filter based on.
Here is a Plunker with a working example.

Categories