How to create sap.m.ComboBox without the strange 'is not a function' error? - combobox

I've been playing around with SapUI5 for a while and now I'm facing another issue, which is totally confusing me.
My goal: To add a ComboBox to an oTable.
What I tried: I decided to do the 'separation of concerns', in order to investigate it better, so I 'extracted' the ComboBox code from the table and am testing it on its own like this:
var cbWizardTypes = [
{
Code: "0",
Name: "Name0",
AdditionalText: "Additional0"
},
{
Code: "1",
Name: "Name1",
AdditionalText: "Additional1"
},
{
Code: "2",
Name: "Name2",
AdditionalText: "Additional2"
},
];
// now the template to use when showing the items
var cbWizardTypesTemplate = new sap.ui.core.ListItem({
key: "{Code}",
text: "{Name}",
additionalText: "{AdditionalText}"
});
// now let's create it and place it
var cbWizardType = new sap.m.ComboBox({
items: {
path: cbWizardTypes,
template: cbWizardTypesTemplate
},
showSecondaryValues: true
});
cbWizardType.placeAt(containerID, 'only');
Now, this is giving me this error in the console:
Additionally, I tried not to use a template, just to see what happens
var cbWizardType = new sap.m.ComboBox({
//items: {
// path: cbWizardTypes,
// template: cbWizardTypesTemplate
//},
items: cbWizardTypes,
showSecondaryValues: true
});
In this case, there are no errors in the Chrome Developer Tools - Console. I get a ComboBox with 3 items, but they are all blank.
Now, I will, at least, try to investigate further, although library-preload.js had been minified, so it will be really hard and time-consuming to navigate through all those 'd'-s, 'p'-s, 'j'-s, etc., I guess.
As always, I will appreciate any help. Thank you!

The problem lies with the binding path that you assign to the ComboBox. The binding should be a string & not an array. You will have to store the data in a model & bind it then to your control.
The code below should work
var cbWizardTypes = [
{
Code: "0",
Name: "Name0",
AdditionalText: "Additional0"
},
{
Code: "1",
Name: "Name1",
AdditionalText: "Additional1"
},
{
Code: "2",
Name: "Name2",
AdditionalText: "Additional2"
},
];
var oModel = new sap.ui.model.json.JSONModel({ items: cbWizardTypes});
// now the template to use when showing the items
var cbWizardTypesTemplate = new sap.ui.core.ListItem({
key: "{Code}",
text: "{Name}",
additionalText: "{AdditionalText}"
});
// now let's create it and place it
var cbWizardType = new sap.m.ComboBox({
items: {
path: "/items",
template: cbWizardTypesTemplate
},
showSecondaryValues: true
});
cbWizardType.setModel(oModel);
cbWizardType.placeAt(containerID, 'only');

Related

Update a complex object on Backand using $http PUT

I am using Backand to provide the database and REST api for my Angular app.
I am working on a capability for users to make edits to a complex object, which should then be updated on the database. Straightforward enough...
The object looks a bit like this:
obj = {
id: 1, // assigned by the db
name: "My List",
tasks: [
{ id: 1, desc: "Task 1" },
{ id: 2, desc: "Task 2" },
...
]
}
For the update ($http PUT) call, I would like to use params: { deep: true } as a shortcut to minimise code and $http calls.
The problem at the moment is that while the PUT command updates the "master" object in the database, the edited "child" objects are not updated, but appended as new child objects.
For instance, if I try to update the master and child objects in one call:
$http({
method: 'PUT',
url: baseUrl + 'lists/' + list.id,
params: {
deep: true
},
data: {
id: 1,
name: "My To Do List",
tasks: [
{ id: 1, desc: "New Description for Task 1" },
{ id: 2, desc: "New Description for Task 2" }
]
}
}).then( .... );
the database doesn't update the child objects, it appends them. Here's how the resulting object is in the database:
list = {
id: 1,
name: "My To Do List", // Updated correctly
tasks: [
{ id: 1, desc: "Task 1" },
{ id: 2, desc: "Task 2" },
{ id: 3, desc: "New Description for task 1" }, // Added not updated
{ id: 4, desc: "New Description for task 2" } // Added not updated
]
}
I have made sure that the child objects' ids are correct.
Is there any way to do this succinctly or am I resigned to doing it in multiple stages? Does deep = true even work with PUT? The Backand docs don't mention it.
Backand identifies existing objects according to their
{
__metadata: {id: "6"}
}
When you "GET" an object from Backand it contains such a metadata.
When you "PUT" an object without the metadata id, Backand threats it as a new object.
So either use the same deep object that you originally got or add the metadata id.
$http({
method: 'PUT',
url: baseUrl + 'lists/' + list.id,
params: {
deep: true
},
data: {
"__metadata": { "id": "1" },
id: 1,
name: "My To Do List",
tasks: [
{ "__metadata": { "id": "1" }, id: 1, desc: "New Description for Task 1" },
{ "__metadata": { "id": "2" }, id: 2, desc: "New Description for Task 2" }
]
}
}).then( .... );
To delete tasks children in the "PUT" request you have to add overwrite=true to the params
params: {
deep: true,
overwrite: true
}

Angularjs Sortable items and categories based on json data

I have this json structure:
{
"items":[
{
"category":"Category 1",
"name":"01",
"id":"46701a72410c"
},
{
"category":"Category 2",
"name":"02",
"id":"9a18ffe9f148"
},
{
"category":"Category 2",
"name":"03",
"id":"de3a49a458cc"
},
{
"category":"Category 3",
"name":"04",
"id":"bb06b1eec9c8"
},
{
"category":"Category 3",
"name":"05",
"id":"92973f4cfbfc"
},
{
"category":"Category 3",
"name":"06",
"id":"b06bbb86d278"
}
],
"categories":[
{
"isCollapsed":false,
"name":"Category 1"
},
{
"isCollapsed":false,
"name":"Category 2"
},
{
"isCollapsed":false,
"name":"Category 3"
}
]
}
and I'm trying to add sorting behavior using angularjs ui.sortable. I want both categories and items to be sortable.
I created two nested ordered lists based on this json, but I have no idea how to solve sortable settings. Is it possible for this json structure?
With these settings I solved only categories sorting to work. The problem is when items are moved (wrong positions or undefined are taken).
$scope.sortableOptionsCategories = {
stop: function(ev, ui) {
console.log("Category moved.");
}
};
$scope.sortableOptionsItems = {
connectWith: '.items-container',
start: function(e, ui) {
$(this).attr('data-previndex', ui.item.index());
console.log("Start: from position " + ui.item.index());
},
update: function(e, ui) {
var newIndex = ui.item.index();
var oldIndex = $(this).attr('data-previndex');
$(this).removeAttr('data-previndex');
console.log("Update: " + oldIndex + " -> " + newIndex);
},
stop: function(ev, ui) {
console.log("Item moved.");
}
};
UPDATE:
Here is my code:
http://codepen.io/anon/pen/LpGmeY
A solution that keeps the json as it is would perfect for me, but if not possible I will accept any other solution.
Have you tried using this library - https://github.com/angular-ui-tree/angular-ui-tree
I created a jsfiddle - http://jsfiddle.net/450fk7wp/5/ - that handles the nested, sortable, draggable items.
You will have to transform your JSON so that it looks like this:
$scope.rows = [{
"name":"Category 1",
"columns": [
{
"category":"Category 1",
"name":"01",
"id":"46701a72410c"
}
],
}, {
"name":"Category 2",
"columns": [
{
"category":"Category 2",
"name":"02",
"id":"9a18ffe9f148"
},
{
"category":"Category 2",
"name":"03",
"id":"de3a49a458cc"
}
],
}, {
"name":"Category 3",
"columns": [
{
"category":"Category 3",
"name":"04",
"id":"bb06b1eec9c8"
},
{
"category":"Category 3",
"name":"05",
"id":"92973f4cfbfc"
},
{
"category":"Category 3",
"name":"06",
"id":"b06bbb86d278"
}
]
}];
Just not exactly sure what type of sorting functionality you are looking for.
Hope this helps!
Provided I've understood this correctly you have nested lists, are you using nested ng-repeats?
If so it seems you can't do this with sortable (from https://github.com/angular-ui/ui-sortable)
ng-model is required, so that the directive knows which model to
update.
ui-sortable element should only contain one ng-repeat and not
any other elements (above or below).
Can you paste your HTML?

Angular select ngChange get the parent json object key not the value

I've created a hypothetical example; since i can't share my real example with you all. So forgive my hasty json file i created.
To the problem. Say i have a select populated like so using a json file which contains an array of (US State) objects:
State.json
{ "states":
[
{
code: "AL",
name: "Alabama"
},
{
code: "AK",
name: "Alaska"
},
{
code: "AS",
name: "American Samoa"
},
{
code: "AZ",
name: "Arizona"
},
{
code: "AR",
name: "Arkansas"
},
{
code: "CA",
name: "California"
},
{
code: "CO",
name: "Colorado"
},
{
code: "CT",
name: "Connecticut"
},
... etc...
]}
I pull in the json file and set it to a scope item like so:
main-controller.js
app.controller('MainCtrl', function ('$scope') {
$scope.states = [
{ code: "AL": name: "Alabama" },
//etc
];
$scope.selectStateChange = function (stateCode) {
console.log(stateCode);
}
});
index.html
Here's my select:
<select ng-model="selectedState" ng-change="selectStateChange(selectedState)">
<option ng-repeat="state in states">{{state.name}}</option>
</select>
My Problem
How does one get the actual state code to be passed into function selectStateChange on my ng-change?
You should try using ng-options instead of a ng-repeat on options.
This way your model will be up to date and it will be quite convenient to access the selected object.
It should looks like this in your case :
<select ng-model="selectedState" ng-options="state.name for state in states" ng-change="selectStateChange()">
</select>
and your JS should display your object:
app.controller('MainCtrl', function ('$scope') {
$scope.states = { "AL": "Alabama", //etc }
$scope.selectedState = null;
$scope.selectStateChange = function () {
console.log(selectedState);
}
});
This way, selectedState is equal to {
code: "AL",
name: "Alabama"
}
What get's logged into the console by console.log(stateCode);?
Did you try
$scope.selectStateChange = function (selectedState) {
console.log(selectedState.code);
}

Angularjs and Jade ng-repeat nested issue

I am beginning to play around with Jade and I am having this weird issue. I'm sure it's something stupid, but I've been trying for one hour without success.
I have an object that contains groups, each group contains items. So, there is one ng-repeat nested inside the other.
ul.page-sidebar-menu(ng-repeat="group in menuItems")
li(ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
ul.sub-menu(ng-repeat="item in group.items")
li
a(href="{{item.target}}") {{item.name}}
The object source is like this:
[
{
name: "Inicio",
target: "/",
groupStyle: {
start: "start",
active: "active"
},
spanStyle: {
selected: "selected"
},
iconStyle: "icon-home"
},
{
name: "Catalogo",
target: "javascript:;",
groupStyle: { },
spanStyle: {
arrow: "arrow"
},
iconStyle: "icon-book",
items: [
{ name: "Clientes", target: "view1" },
{ name: "Rutas", target: "view1" },
{ name: "Transportistas", target: "view1" }
]
},
{
name: "Panel de Control",
target: "javascript:;",
groupStyle: { },
spanStyle: {
arrow: "arrow"
},
iconStyle: "icon-cogs",
items: [
{ name: "Usuarios", target: "view2" },
{ name: "Configuracion", target: "view2" }
]
}
]
So, theoretically each group has some number of items and nesting should be possible. Here comes the funny part: when Jade renders the HTML based on a list it only renders the first child of every group. This is the output:
But when I added a table before the list with another ng-repeat it works fine. The code:
ul.page-sidebar-menu(ng-repeat="group in menuItems")
li(ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
table
tr(ng-repeat="item in group.items")
td {{item.name}}
ul.sub-menu(ng-repeat="item in group.items")
li
a(href="{{item.target}}") {{item.name}}
And the output:
So, please someone with more coffee in their body or more skills in Jade give me a hand. I'm sure it must be something obvious.
Thanks in advance.
As I guessed it was a stupid issue. The ng-repeat should be at the items level, not at the list level. Here is the code fixed.
ul.page-sidebar-menu
li(ng-repeat="group in menuItems", ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
ul.sub-menu
li(ng-repeat="item in group.items")
a(href="{{item.target}}") {{item.name}}

Binding of a Collection nested inside a Model

I have this model structure in my mind:
var app = app || {};
// Caratteristica
app.Attribute = Backbone.Model.extend({
defaults: {
name: '',
selected: false
}
});
app.Attributes = Backbone.Collection.extend({
model: app.Attribute
});
// Tipo Caratteristica
app.AttributeCategory = Backbone.Model.extend({
defaults: {
name: '',
attributes: new app.Attributes()
}
});
app.AttributeCategories = Backbone.Collection.extend({
model: app.AttributeCategory,
url: '/ajax/attributes.cfm'
});
My API in '/ajax/attributes.cfm' will give me a response like that:
[
{
"id": "1",
"name": "Type1",
"attributes": [
{
"id": "1",
"name": "Attribute1"
},
{
"id": "2",
"name": "Attribute2"
},
{
"id": "3",
"name": "Attribute3"
}
]
},
{
"id": "2",
"name": "Type2",
"attributes": [
{
"id": "1234",
"name": "Attribute1234"
},
{
"id": "2567",
"name": "Attribute2567"
}
]
}
]
My question is: will this json data be parsed correctly into my nested data structure?
I mean I want to end up having two app.AttributeCategory items in my app.AttributeCategories collection. Each of these two items must then have its attributes property filled with the corresponding app.Attributes collection.
If the answer was NO, how would I override the parse() function for achieving that result?
I did it like this:
// Tipo Caratteristica
app.AttributeCategory = Backbone.Model.extend({
defaults: {
name: ''
},
initialize: function(options) {
this.set('attributes', new app.Attributes(options.attributes));
Backbone.Model.prototype.apply(this, arguments);
}
});
But better use RationalModel for set up relations betweens models
You can create the collection inside an initialize method in your AttributeCategory model, like this:
app.AttributeCategory = Backbone.Model.extend({
...
initialize: function () {
this.set('attributes', new app.Attributes(this.get('attributes')));
}
});

Resources