in my SAPUI5 app I am using a Multi Input Field with tokens which are bound to a JSON Model. Newly added entries are saved in the JSON Model. However, when deleting a token by pressing the "x" next to the token text, the token disappears from the multi input field. But when adding a new token the deleted one reappears.
How can I ensure that the deleted entry is also deleted from the JSON Model?
This is my current code for adding the token to the model:
multiInputField.addValidator(function(args){
MessageBox.confirm("Do you really want to add Token\"" + args.text + "\"?", {
onClose: function(oAction) {
if (oAction === MessageBox.Action.OK){
var oToken = new Token({key: args.text, text: args.text});
args.asyncCallback(oToken);
var aFields = sap.ui.getCore().getView().getModel("myModel").getProperty("/Tokens");
var oNewFields= {
Tokens: args.text
};
aFields .push(oNewFields);
sap.ui.getCore().getView().getModel("myModel").setProperty("/Tokens", aFields );
sap.ui.getCore().getView().getModel("myModel").refresh();
} else {
args.asyncCallback(null);
}
},
title: "Add Token"
});
return sap.m.MultiInput.WaitForAsyncValidation;
});
I guess we can use "tokenUpdate" event for this.
For example, given that I have this MultiInput in my view:
<MultiInput width="500px" id="multiInput" suggestionItems="{ path: 'dataModel>/data'}" showValueHelp="true" tokenUpdate="onTokenUpdate">
<core:Item key="{dataModel>key}" text="{dataModel>value}"/>
</MultiInput>
then in my controller I can handle this like :
onTokenUpdate: function(oEvent) {
var sType = oEvent.getParameter("type");
if (sType === "removed") {
var sKey = oEvent.getParameter("removedTokens")[0].getProperty("key");
var oModel = this.getView().getModel("dataModel");
var aData = this.getView().getModel("dataModel").getProperty("/data");
for (var i = 0, len = aData.length; i < len; i++) {
var idx;
console.log(sKey + "-" + aData[i].key);
if (aData[i].key === sKey) {
idx = i;
}
}
aData.splice(idx, 1);
oModel.setProperty("/data", aData);
console.log(oModel);
}
}
And this is my json:
{
"data": [
{
"key": "token1",
"value": "token1"
},
{
"key": "token2",
"value": "token2"
}
]
}
Related
The main issue is the following:
Get JSON from the server
Put data to form
Serialize form
Create JSON with correct structure
Send it to the server
I have difficulties on the fourth step, what I've done:
methods: {
onSubmit: function() {
var dataForm = new FormData(this.$refs['form']);
var data = [];
for (var _iterator = dataForm.entries(), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _Object$assign;
var _Helper = {};
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var _ref2 = _ref,
key = _ref2[0],
val = _ref2[1];
Object.assign(_Helper, (_Object$assign = {}, _Object$assign[key] = val, _Object$assign));
}
}
},
Here you go - a link to the codepen.
As you can see I could create JSON like that:
{"0":"a","1":"b","2":"c","3":"d","4":"e","5":"d"}
However, I need smth like that:
{
"0": {
"text": "a"
},
"1": {
"text": "b"
},
"2": {
"text": "c"
},
"3": {
"text": "d"
},
"4": {
"text": "e"
},
"5": {
"text": "d"
}
}
What Can I do to implement it and also keep the correct structure of my JSON?
To change the format, change in the location it assigns the property value:
Object.assign(data, (_Object$assign = {}, _Object$assign[key] = {text: val}, _Object$assign));
// -------------------------------------------------------------^^^^^^^^^^^
Instead of a string (val), assign the object in the format you want ({text: val}).
Updated CodePen.
If you can use modern (ES6) JavaScript, there's a much shorter notation for that:
var [key, val] = _ref;
Object.assign(data, {[key]: {text: val}});
CodePen here.
Or (because you are using FormData#entries() you do are using modern JS):
var formData = Array.from(new FormData(this.$refs['form']).entries());
var data = Object.assign(...formData.map(([key, val]) => ({[key]: {text: val}})));
console.log(JSON.stringify(data));
Targetting IE10 and above
To target IE10 and above you'll need polyfills. The problem is not the syntax, but the absence of functions like .entries(), that will be added by the polyfills. To use the least amount possible of polyfills, you'll have to iterate the iterator "manually" (kind of like you are already). For more info, check this answer.
You can do the whole construction much more simply:
onSubmit: function () {
const dataForm = new FormData(this.$refs['form']);
const data = {};
for (const i of dataForm.entries()) {
data[i[0]] = { text: i[1] }
}
console.log(JSON.stringify(data));
}
I've built a Google Form that I'm using to create calendar events, and the only thing I haven't worked out yet is how to properly get any attached Drive files from the Form onto the calendar.
I found a very useful snippet of code from this post
that has shown me how to attach a file to my calendar event using its Drive fileId, but only from a standalone script where the file info is put in manually.
Where I'm getting stuck, is that I'm receiving an array of file ID's from my form, and I want to attach them to my calendar event with their actual, respective file names. I know I'll have to iterate through the array somehow, but I especially can't seem to pull the file name info from each of the files.
I am not a programmer by trade, so I'm learning as I go. Any help would be greatly appreciated.
This is my app script code for my Google Form (The calendar is used for AV event setups and teardowns at a college).
Thanks in advance.
Joel
//Load the Moment.js library once.
//MFSDXCPBYb-JUlxKtMlncfZPjmlt5YPXB
var moment = Moment.load();
var formInput = (function() {
return {
formId: "xxxxx",
calendarId: "xxxxx",
formMap: {
ticket: "Ticket Number",
eventTitle: "Event Title",
startTime : "Event Date and Start Time",
duration: "Event Duration in Hours",
description: "Event Description",
location: "Event Location",
setup: "Set Up Required?",
eventDocs: "Event Docs"
}
}
})();
function onFormSubmit() {
getFormResponse.formResponse();
createEvent.newEvent();
}
var getFormResponse = (function() {
// Get a form object by opening the form using the
// form id stored in the formInput variable object
return {
formResponse: function() {
var form = FormApp.openById(formInput.formId);
//Get all responses from the form.
//This method returns an array of form responses
responses = form.getResponses();
//find the length of the responses array
length = responses.length;
//find the index of the most recent form response
//since arrays are zero indexed, the last response
//is the total number of responses minus one
lastResponse = responses[length-1];
//get an array of responses to every question item
//within the form for which the respondent provided an answer
itemResponses = lastResponse.getItemResponses();
//create an empty object to store data from the last
//form response
//that will be used to create a calendar event
eventObject = {};
//Loop through each item response in the item response array
for (var i = 0, x = itemResponses.length; i<x; i++) {
//Get the title of the form item being iterated on
var thisItem = itemResponses[i].getItem().getTitle(),
//get the submitted response to the form item being
//iterated on
thisResponse = itemResponses[i].getResponse();
//based on the form question title, map the response of the
//item being iterated on into our eventObject variable
//use the formInput variable formMap sub object to match
//form question titles to property keys in the event object
switch (thisItem) {
case formInput.formMap.ticket:
eventObject.ticket = thisResponse;
//Logger.log(eventObject.ticket);
break;
case formInput.formMap.eventTitle:
eventObject.title = thisResponse;
//Logger.log(thisResponse);
break;
case formInput.formMap.startTime:
eventObject.startTime = thisResponse;
//Logger.log(thisResponse);
break;
case formInput.formMap.duration:
eventObject.duration = thisResponse;
//Logger.log(thisResponse);
break;
case formInput.formMap.description:
eventObject.description = thisResponse;
//Logger.log(thisResponse);
break;
case formInput.formMap.location:
eventObject.location = thisResponse;
//Logger.log(thisResponse);
break;
case formInput.formMap.setup:
eventObject.setup = thisResponse;
//Logger.log(thisResponse);
break;
case formInput.formMap.eventDocs:
eventObject.eventDocs = thisResponse;
//Logger.log(thisResponse);
break;
}
}
return eventObject;
}
};
})();
var createEvent = (function() {
return {
newEvent: function() {
var eventStartTime = moment(eventObject.startTime);
var eventEndTime = eventStartTime.clone().add(eventObject.duration, 'hours');
var eventSetupStart = eventStartTime.clone().subtract(2, 'hours');
var eventSetupEnd = eventSetupStart.clone().add(1, 'hour');
var eventTeardownStart = eventEndTime.clone().add(30, 'minutes');
var eventTeardownEnd = eventTeardownStart.clone().add(30, 'minutes');
// create if else loop to test if setup is needed
if (eventObject.setup === 'Yes') {
// create base event with default calendar color label
var event = {
summary: eventObject.ticket + " | " + eventObject.title,
start: {
dateTime: eventStartTime.format()
},
end: {
dateTime: eventEndTime.format()
},
location: eventObject.location,
description: eventObject.description,
attachments: [{
'fileUrl': 'https://drive.google.com/file/d/' + eventObject.eventDocs
}]
};
// create setup event with green label
var setup = {
summary: eventObject.ticket + " | " + "Set Up -",
start: {
dateTime: eventSetupStart.format()
},
end: {
dateTime: eventSetupEnd.format()
},
location: eventObject.location,
description: eventObject.description,
colorId: 10
};
// create teardown event with green label
var teardown = {
summary: eventObject.ticket + " | " + "Tear Down -",
start: {
dateTime: eventTeardownStart.format()
},
end: {
dateTime: eventTeardownEnd.format()
},
location: eventObject.location,
description: "",
colorId: 10
};
event = Calendar.Events.insert(event, formInput.calendarId, {
'supportsAttachments': true
});
setup = Calendar.Events.insert(setup, formInput.calendarId);
teardown = Calendar.Events.insert(teardown, formInput.calendarId);
} else {
// create monitoring-only event with grey label
var eventMon = {
summary: eventObject.ticket + " | " + eventObject.title,
start: {
dateTime: eventStartTime.format()
},
end: {
dateTime: eventEndTime.format()
},
location: eventObject.location,
description: eventObject.description,
colorId: 8
};
eventMon = Calendar.Events.insert(eventMon, formInput.calendarId, {
'supportsAttachments': true
});
}
}
}
})();
I have a Json document as:
{
"_id": "3de00db35e6549604c711e7295a1982a",
"_rev": "1-ecba71644d341dfe5cb9abf6dd13b23a",
"dateCreated": "2014-01-29 00:00:00",
"attributeCollection": {
"attributeArray": [
{
"updateable": false,
"lookup": "issuetype",
"issueAttributeDefinitionId": 13,
"attributeType": 1,
"name": "Web Type",
"value": [
"Improper Limitation of Authentication"
]
},
{
"updateable": true,
"lookup": "status",
"issueAttributeDefinitionId": 1,
"attributeType": 4,
"name": "Status",
"value": [
"Access with Right Permission"
]
}
]
},
"hash": "287030d6efa085b5b92b7106c0edb6d7"
}
I want to create search index for document using "name" and "value" ("name" or "value" only). I accessed these attributes by this codes:
for (var i=0; i<doc.attributeCollection.attributeArray.length; i++) {
if (doc.attributeCollection.attributeArray[i].name) {
name = doc.attributeCollection.attributeArray[i].name;
}
if (doc.attributeCollection.attributeArray[i].value) {
value = doc.attributeCollection.attributeArray[i].value;
}
}
It works when i use contentindex = name + " "+ value; the content shows "Web Type Improper Limitation of Authentication". However, if i use value only contentindex = value, it doesn't work, it shows null.
I know that the structure of "value" likes array (array with 1 element) and it doesn't have any attribute name.
How can i access the value properly?
Update:
When i index some cases as:
1. It works
var content=name + " " + value;
index("default", content);
2. It works
index("default", name);
3. It doesn't work
index("default", value);
4.I fixed with by revised the code to get "value" as:
if (doc.attributeCollection.attributeArray[i].value) {
for (var j=0; j<doc.attributeCollection.attributeArray[i].value.length; j++){
value = doc.attributeCollection.attributeArray[i].value[j];
}
}
Or
if (doc.attributeCollection.attributeArray[i].value) {
value = doc.attributeCollection.attributeArray[i].value[0];
}
It works with index("default", value);
However, when i used permutation function as discussed in this post
5. It works
var content= permuteword(name);
for(var k=0; k<content.length; k++){
index("default", content[k], { store : true });
}
6. It doesn't work
var content= permuteword(value);
for(var k=0; k<content.length; k++){
index("default", content[k], { store : true });
}
7. It doesn't work
var content=name + " " +value;
var content1= permuteword(content);
for(var k=0; k<content1.length; k++){
index("default", content1[k], { store : true });
}
I'm not exactly sure I completely understand your question. It sounds like you are creating an index like this:
for (var i=0; i<doc.attributeCollection.attributeArray.length; i++) {
if (doc.attributeCollection.attributeArray[i].name) {
name = doc.attributeCollection.attributeArray[i].name;
}
if (doc.attributeCollection.attributeArray[i].value) {
value = doc.attributeCollection.attributeArray[i].value;
}
index("contentindex", name + " " + value);
}
If this is the case you could try indexing the name and value separately (Update: changed to access each element in the value array):
for (var i=0; i<doc.attributeCollection.attributeArray.length; i++) {
if (doc.attributeCollection.attributeArray[i].name) {
name = doc.attributeCollection.attributeArray[i].name;
index("contentindex", name);
}
if (doc.attributeCollection.attributeArray[i].value) {
for (var j=0; j<doc.attributeCollection.attributeArray[i].value.length; j++) {
value = doc.attributeCollection.attributeArray[i].value[j];
index("contentindex", value);
}
}
}
Then querying by either the name or the value should return the correct result.
Please let me know if I am misunderstanding you question.
Hopefully an easy question for you.
I have an API service from Rackspace which I call, recently they changed the order of some of the responses, meaning part of my app would no longer upload images to the correct POST url.
I've put a temp fix in place by hard coding the URL's it should be posting to, but I want to be sure I future proof against any changes in their API ordering or indeed any changes to the URL itself.
So, originally I was using the JSON response and choosing the first node and its children.
var CFtenantID = getRSToken.access.serviceCatalog[0].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[0].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[0].endpoints[0].publicURL;
Now, that node has moved to position 18 in a long list. So, I can manually set the script to retrieve it.
var CFtenantID = getRSToken.access.serviceCatalog[17].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[17].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[17].endpoints[0].publicURL;
What I'd like to be able to do, is scan for the "name" instead and just return the CloudFiles info I need, rather than having to declare an actual number of the array.
Here is a snippet of the JSON response from Rackspace.
{
"name": "cloudFeeds",
"endpoints": [
{
"region": "LON",
"tenantId": "1234",
"publicURL": "https://lon.feeds.api.rackspacecloud.com/1234",
"internalURL": "https://atom.prod.lon3.us.ci.rackspace.net/1234"
}
],
"type": "rax:feeds"
},
{
"name": "cloudFiles",
"endpoints": [
{
"region": "LON",
"tenantId": "MossoCloudFS_xxxxxx",
"publicURL": "https://storage101.lon3.clouddrive.com/v1/MossoCloudFS_xxxxx",
"internalURL": "https://snet-storage101.lon3.clouddrive.com/v1/MossoCloudFS_xxxxx"
}
],
"type": "object-store"
},
{
"name": "cloudFilesCDN",
"endpoints": [
{
"region": "LON",
"tenantId": "MossoCloudFS_xxxxxx",
"publicURL": "https://cdn3.clouddrive.com/v1/MossoCloudFS_xxxxxx"
}
],
"type": "rax:object-cdn"
}
And here is my overall script in Appcelerator.
var authCloudFiles = Ti.Network.createHTTPClient({
onload: function() {
// output
Ti.API.info(this.responseText);
var getRSToken = JSON.parse(this.responseText);
var rsToken = getRSToken.access.token.id;
var rsTenantID = getRSToken.access.token.tenant.id;
var rsTenantName = getRSToken.access.token.tenant.name;
var CFtenantID = getRSToken.access.serviceCatalog[17].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[17].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[17].endpoints[0].publicURL;
var rsUserID = getRSToken.access.user.id;
Ti.App.Properties.setString('rsToken', rsToken);
Ti.App.Properties.setString('rsTenantID', rsTenantID);
Ti.App.Properties.setString('rsTenantName', rsTenantName);
Ti.App.Properties.setString('CFtenantID', CFtenantID);
Ti.App.Properties.setString('CFregion', CFregion);
Ti.App.Properties.setString('CFpublicURL', CFpublicURL);
Ti.App.Properties.setString('rsUserID', rsUserID);
//alert(rsToken);
Ti.API.info('rsToken: ' + rsToken);
Ti.API.info('rsTenantID: ' + rsTenantID);
Ti.API.info('rsTenantName: ' + rsTenantName);
Ti.API.info('CFtenantID: ' + CFtenantID);
Ti.API.info('CFregion: ' + CFregion);
Ti.API.info('CFpublicURL: ' + CFpublicURL);
Ti.API.info('rsUserID: ' + rsUserID);
// then we need to load the next step
rackspaceUpload();
}
});
authCloudFiles.open('POST', 'https://identity.api.rackspacecloud.com/v2.0/tokens');
Can anyone help?
Simon
I have never used Appcelerator Titanium before, but it looks like it is just JavaScript.
You could try replacing the following:
var CFtenantID = getRSToken.access.serviceCatalog[17].endpoints[0].tenantId;
var CFregion = getRSToken.access.serviceCatalog[17].endpoints[0].region;
var CFpublicURL = getRSToken.access.serviceCatalog[17].endpoints[0].publicURL;
with:
var CFtenantID = "";
var CFregion = "";
var CFpublicURL = "";
var catalog = getRSToken.access.serviceCatalog;
for (var i=0; i<catalog.length; i++) {
if (catalog[i].name == "cloudFiles") {
for (var j=0; i<catalog[i].endpoints.length; j++) {
if (catalog[i].endpoints[j].region == "LON") {
CFtenantID = catalog[i].endpoints[j].tenantId;
CFregion = catalog[i].endpoints[j].region;
CFpublicURL = catalog[i].endpoints[j].publicURL;
break;
}
}
}
}
My JavaScript is a little rusty though, you may want to add some additional error handling.
In EXTJS i will use a model and store for my grid. Now is the problem that sometimes the json will not match the model. There will be less information then in my model. When this happens EXTJS will not show any data in the grid. So i looked for a fix and found this:
Ext.define('App.Reader', {
extend: 'Ext.data.reader.Json',
extractData: function(root) {
var me = this,
values = [],
records = [],
Model = me.model,
i = 0,
length = root.length,
idProp = me.getIdProperty(),
node, id, record;
if (!root.length && Ext.isObject(root)) {
root = [root];
length = 1;
}
for (; i < length; i++) {
node = root[i];
values = me.extractValues(node);
id = me.getId(node);
record = new Model(values, id, node);
records.push(record);
if (me.implicitIncludes) {
me.readAssociated(record, node);
}
}
return records;
},
extractValues: function(data) {
var fields = this.getFields(),
i = 0,
length = fields.length,
output = {},
field, value;
for (; i < length; i++) {
field = fields[i];
value = this.extractorFunctions[i](data);
if(value === undefined)
{
Ext.iterate(fields, function(key, val) {
if (data[key] === undefined & i==val) {
console.log( "Model field <" + key.name + "> does not exist in data/node.");
value = "INVALID OR MISSING FIELD NAME";
var p = 0;
for(var prop in data) {
if(p==i){
if(data.hasOwnProperty(prop))console.log("Instead of <" + key.name + "> we have <" + prop + "> with value <" + data[prop]+ ">");
}
p++;
}
}
}, this);
}
output[field.name] = value;
}
return output;
}
});
var myReader = new App.Reader({
type:'json'
});
i found this online. But when i use this with EXTJS 4.1.1 there is an error in ext-all: TypeError: j is undefined.
Where should i look for the fix for this?
There's no need to do something complicated to solve this trivial problem. Read up on Ext.data.Model and Ext.data.Field, configure your Model properly and you're all set.