Aim:
I'd like to have two models(sets of data) passed to the custom control with a predefined search field, in which later on I can execute filtering.
I'm a newbie in OpenUi5, so I might be doing something wrong and stupid here. I've started with a simplified task of passing data from the frontend to my custom control and experiencing troubles.
Background of the simplified idea:
Create a custom control with an aggregation foo , the value to it will be provided from the view.
Also create another aggregation element _searchField which will be populated with the data provided from the view.
Fire the onSuggestTerm everytime user types in a _searchField.
Custom control code:
function (Control) {
var DropDownListInput = Control.extend('xx.control.DropDownListInput', {
metadata: {
defaultAggregation: 'foo',
aggregations: {
foo: { type: 'sap.m.SuggestionItem', multiple: true, singularName: 'suggestionItem' },
_searchField: { type: 'sap.m.SearchField', multiple: false, visibility: 'hidden' }
}
}
});
DropDownListInput.prototype.init = function () {
var that = this;
this.onSuggestTerm = function (event) {
var oSource = event.getSource();
var oBinding = that.getAggregation('foo');
oBinding.filter(new sap.ui.model.Filter({
filters: new sap.ui.model.Filter('DISEASE_TERM', sap.ui.model.FilterOperator.Contains, ' Other')
}));
oBinding.attachEventOnce('dataReceived', function () {
oSource.suggest();
});
};
this.setAggregation('_searchField', new sap.m.SearchField({
id: 'UNIQUEID1',
enableSuggestions: true,
suggestionItems: that.getAggregation('foo'),
suggest: that.onSuggestTerm
}));
};
return DropDownListInput;
}, /* bExport= */true);
I'm not providing Renderer function for control here, but it exists and this is the most important excerpt from it:
oRM.write('<div');
oRM.writeControlData(oControl);
oRM.write('>');
oRM.renderControl(oControl.getAggregation('_searchField'));
oRM.write('</div>');
Passing the data to this control from the xml frontend:
<xx:DropDownListInput
id="diseaseTermUNIQUE"
foo='{path: db2>/RC_DISEASE_TERM/}'>
<foo>
<SuggestionItem text="{db2>DISEASE_TERM}"
key="{db2>DISEASE_TERM}" />
</foo>
</xx:DropDownListInput>
The code fails to run with this error Cannot route to target: [object Object] -
and I have no idea what's wrong here..
The problem is that you forgot to provide single quotes in your path:
foo="{path: 'db2>/RC_DISEASE_TERM/'}"
Related
I am trying to build a function that allows users to retrieve data from the database, but in a dynamic way. So allowing them to specify the source table, as well as which columns they would like to retrieve.
I have already built out the part that allows them to choose the table and columns, and these users only have access to data we are happy for them to retrieve.
I want this to be as dynamic as possible, so I am building a function to help me with this. I have run into one problem though so far. I have this function:
const modelMap = (model, action, criteria, options) => {
const models = {
EXTERNAL_USER: {
READ: services.externalUser.readExternalUser(criteria, options),
},
TASK: {
READ: services.task.readTask(criteria, options),
},
USER: {
READ: services.user.readUser(criteria, options),
},
}
return models[model][action]
}
So, for example, I call this function using
modelMap('EXTERNAL_USER', 'READ', { id: userID }, { populates: ['documents'] }).
This returns the data I want, however I get an ``OperationError` in my terminal:
OperationalError [UsageError]: Invalid populate(s).
Details:
Could not populate `documents`. There is no attribute named `documents` defined in this model.
The error is saying that the Task model has no attribute documents, which it doesn't. So I am guessing that even though I am not trying to access the readTask function, it is being called anyway. How can I get around this?
SOLUTION
I altered the models object to the following:
const models = {
EXTERNAL_USER: {
READ: () => services.externalUser.readExternalUsers(criteria, options),
},
TASK: {
READ: () => services.task.readTask(criteria, options),
},
USER: {
READ: () => services.user.readUser(criteria, options),
},
}
And I can then use await modelMap(model, action, criteria, options)() to get the data I need.
Yes. The functions called anyway
I have got to the point where I can click a button on a card and show my app, but I dont know how to pass the board information to my app.
Steps:
In trello, I click on card.. then the power up button. It works, as I see this
Code:
const callback = function (t) {
return t.modal({
url: '../public/index.html',
fullscreen: true,
});
};
const t = window.TrelloPowerUp.initialize({
'card-buttons': function (t, options) {
return [
{
text: 'Hello World',
callback: callback,
},
];
},
});
In docs I see there's an args key that passes an object.
" // optional arguments to be passed to the iframe as query parameters
// access later with t.arg('text')
args: { text: 'Hello' }," - https://developer.atlassian.com/cloud/trello/power-ups/ui-functions/modal/
How do I get either my board info into args.. or use window.TrelloPowerUp.initialize or window.TrelloPowerUp.iframe within the modal?
I might be going around this wrong, but I want to create a custom control that is a subclass of sap.m.Dialog. This new control will have an aggregation 'leftImage' which will then be placed in a HorizontalLayout (private variable). However, this causes an error possibly because the 'leftImage' is already a dependent of my control.
So how do I encapsulate an aggregation on another control?
sap.m.Dialog already has a "content" aggregation which holds the controls to be rendered, so you just need to add your custom bits there.
So, a pattern like this should work:
sap.ui.define([
"jquery.sap.global",
"sap/m/Dialog",
"sap/m/Image",
"sap/ui/layout/HorizontalLayout"
], function(jQuery, Dialog, Image, HorizontalLayout) {
"use strict";
var MyDialog = Dialog.extend("sap.ui.mylib.MyDialog", {
metadata: {
library: "sap.ui.mylib",
associations: {
leftImage: {type: "sap.m.Image", multiple: false}
}
},
renderer: {
// inherit rendering from sap.m.Dialog
}
});
MyDialog.prototype.init = function() {
if (Dialog.prototype.init) {
Dialog.prototype.init.apply(this, arguments);
}
var oImage = new Image({
src: '...'
}),
oHorizontalLayout = new HorizontalLayout({
content: [
oImage
]
});
this.addContent(oHorizontalLayout);
};
return MyDialog;
}, /* bExport= */ true);
Define leftImage as an association because it will be aggregated by the HorizontalLayout. It can be accessed later with:
var oLeftImage = sap.ui.getCore().byId(oMyDialog.getLeftImage());
You may also want to define the HorizontalLayout as an association so you can add more content to that as well.
Edit 11/16/14: Version Information
DEBUG: Ember : 1.7.0 ember-1.7.0.js:14463
DEBUG: Ember Data : 1.0.0-beta.10+canary.30d6bf849b ember-1.7.0.js:14463
DEBUG: Handlebars : 1.1.2 ember-1.7.0.js:14463
DEBUG: jQuery : 1.10.2
I'm beating my head against a wall trying to do something that I think should be fairly straightforward with ember and ember-data, but I haven't had any luck so far.
Essentially, I want to use server data to populate a <select> dropdown menu. When the form is submitted, a model should be created based on the data the user chooses to select. The model is then saved with ember data and forwarded to the server with the following format:
{
"File": {
"fileName":"the_name.txt",
"filePath":"/the/path",
"typeId": 13,
"versionId": 2
}
}
The problem is, the typeId and versionId are left out when the model relationship is defined as async like so:
App.File = DS.Model.extend({
type: DS.belongsTo('type', {async: true}),
version: DS.belongsTo('version', {async: true}),
fileName: DS.attr('string'),
filePath: DS.attr('string')
});
The part that is confusing me, and probably where my mistakes lie, is the controller:
App.FilesNewController = Ember.ObjectController.extend({
needs: ['files'],
uploadError: false,
// These properties will be given by the binding in the view to the
//<select> inputs.
selectedType: null,
selectedVersion: null,
files: Ember.computed.alias('controllers.files'),
actions: {
createFile: function() {
this.createFileHelper();
}
},
createFileHelper: function() {
var selectedType = this.get('selectedType');
var selectedVersion = this.get('selectedVersion');
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path'
});
var gotDependencies = function(values) {
//////////////////////////////////////
// This only works when async: false
file.set('type', values[0])
.set('version', values[1]);
//////////////////////////////////////
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
this.set('uploadError', true);
}.bind(this);
file.save().then(onSuccess, onFail);
}.bind(this);
Ember.RSVP.all([
selectedType,
selectedVersion
]).then(gotDependencies);
}
});
When async is set to false, ember handles createRecord().save() POST requests correctly.
When async is true, ember handles GET requests perfectly with multiple requests, but does NOT add the belongsTo relationships to the file JSON during createRecord().save(). Only the basic properties are serialized:
{"File":{"fileName":"the_name.txt","filePath":"/the/path"}}
I realize this question has been asked before but I have not found a satisfactory answer thus far and I have not found anything that suits my needs. So, how do I get the belongsTo relationship to serialize properly?
Just to be sure that everything is here, I will add the custom serialization I have so far:
App.ApplicationSerializer = DS.RESTSerializer.extend({
serializeIntoHash: function(data, type, record, options) {
var root = Ember.String.capitalize(type.typeKey);
data[root] = this.serialize(record, options);
},
keyForRelationship: function(key, type){
if (type === 'belongsTo') {
key += "Id";
}
if (type === 'hasMany') {
key += "Ids";
}
return key;
}
});
App.FileSerializer = App.ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
type: { serialize: 'id' },
version: { serialize: 'id' }
}
});
And a select:
{{ view Ember.Select
contentBinding="controller.files.versions"
optionValuePath="content"
optionLabelPath="content.versionStr"
valueBinding="controller.selectedVersion"
id="selectVersion"
classNames="form-control"
prompt="-- Select Version --"}}
If necessary I will append the other routes and controllers (FilesRoute, FilesController, VersionsRoute, TypesRoute)
EDIT 11/16/14
I have a working solution (hack?) that I found based on information in two relevant threads:
1) How should async belongsTo relationships be serialized?
2) Does async belongsTo support related model assignment?
Essentially, all I had to do was move the Ember.RSVP.all() to after a get() on the properties:
createFileHelper: function() {
var selectedType = this.get('selectedType');
var selectedVersion = this.get('selectedVersion');
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path',
type: null,
version: null
});
file.set('type', values[0])
.set('version', values[1]);
Ember.RSVP.all([
file.get('type'),
file.get('version')
]).then(function(values) {
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
alert("failure");
this.set('uploadError', true);
}.bind(this);
file.save().then(onSuccess, onFail);
}.bind(this));
}
So I needed to get() the properties that were belongsTo relationships before I save the model. I don't know is whether this is a bug or not. Maybe someone with more knowledge about emberjs can help shed some light on that.
See the question for more details, but the generic answer that I worked for me when saving a model with a belongsTo relationship (and you specifically need that relationship to be serialized) is to call .get() on the properties and then save() them in then().
It boils down to this:
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path',
type: null,
version: null
});
// belongsTo set() here
file.set('type', selectedType)
.set('version', selectedVersion);
Ember.RSVP.all([
file.get('type'),
file.get('version')
]).then(function(values) {
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
alert("failure");
this.set('uploadError', true);
}.bind(this);
// Save inside then() after I call get() on promises
file.save().then(onSuccess, onFail);
}.bind(this));
I have a backboneJS app that has a router that looks
var StoreRouter = Backbone.Router.extend({
routes: {
'stores/add/' : 'add',
'stores/edit/:id': 'edit'
},
add: function(){
var addStoresView = new AddStoresView({
el: ".wrapper"
});
},
edit: function(id){
var editStoresView = new EditStoresView({
el: ".wrapper",
model: new Store({ id: id })
});
}
});
var storeRouter = new StoreRouter();
Backbone.history.start({ pushState: true, hashChange: false });
and a model that looks like:
var Store = Backbone.Model.extend({
urlRoot: "/stores/"
});
and then my view looks like:
var EditStoresView = Backbone.View.extend({
...
render: function() {
this.model.fetch({
success : function(model, response, options) {
this.$el.append ( JST['tmpl/' + "edit"] (model.toJSON()) );
}
});
}
I thought that urlRoot when fetched would call /stores/ID_HERE, but right now it doesn't call that, it just calls /stores/, but I'm not sure why and how to fix this?
In devTools, here is the url it's going for:
GET http://localhost/stores/
This might not be the answer since it depends on your real production code.
Normally the code you entered is supposed to work, and I even saw a comment saying that it works in a jsfiddle. A couple of reasons might affect the outcome:
In your code you changed the Backbone.Model.url() function. By default the url function is
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
},
This is the function to be used by Backbone to generate the URL for model.fetch();.
You added a custom idAttribute when you declared your Store Model to be like the one in your DB. For example your database has a different id than id itself, but in your code you still use new Model({ id: id }); when you really should use new Model({ customId: id });. What happens behind the scenes is that you see in the url() function it checks if the model isNew(). This function actually checks if the id is set, but if it is custom it checks for that:
isNew: function() {
return !this.has(this.idAttribute);
},
You messed up with Backbone.sync ... lots of things can be done with this I will not even start unless I want to make a paper on it. Maybe you followed a tutorial without knowing that it might affect some other code.
You called model.fetch() "a la" $.ajax style:
model.fetch({
data: objectHere,
url: yourUrlHere,
success: function () {},
error: function () {}
});
This overrides the awesomeness of the Backbone automation. (I think sync takes over from here, don't quote me on that).
Reference: Backbone annotated sourcecode