Want To Copy Certain Fields From Previous Entry To New Fragment - google-app-maker

Short Version: I want to have my Copy button in a table to be able to grab the values from an existing entry and populate those into a "Create Entry" Page Fragment. This way users don't have to reenter all the data when making a new entry.
Long Version:
I have two buttons added the rows in my table: Edit and Copy.
The Edit Button uses the following code to grab the information from that specific row and uses the Fragment to edit the entry.
widget.datasource.saveChanges();
app.datasources.SystemOrders.selectKey(widget.datasource.item._key);
app.showDialog(app.pageFragments.SystemOrders_Edit);
The Copy button is currently using the following code to duplicate the entry and automatically create it.
//Allows for copying table/row
var rowDataSource = widget.datasource;
var listDatasource = app.datasources.SystemOrders_HideComplete;
var createDataSource = listDatasource.modes.create;
widget.datasource.saveChanges();
// Enter fields you want to duplicate below
createDataSource.item.ProjectName = rowDataSource.item.ShowName;
createDataSource.item.DeliveryInfo = rowDataSource.item.DeliveryInfo;
createDataSource.item.SOB = rowDataSource.item.SOB;
createDataSource.item.DeliveryDate = rowDataSource.item.DeliveryDate;
createDataSource.item.Company = rowDataSource.item.Company;
createDataSource.item.Location = rowDataSource.item.Location;
createDataSource.item.AdditionalPeripherals = rowDataSource.item.AdditionalPeripherals;
createDataSource.item.Notes = rowDataSource.item.Notes;
createDataSource.createItem();
I would like to change this behavior so that the Copy button grab the values from those specific fields, however instead of doing a createDataSource/createItem(); I want it to place those values into a Page Fragment (ex: SystemOrders_Add) that has the corresponding fields.
This way the user can click "Copy" and the SystemOrders_Add Fragment appears with pre-populated values.
I want to make sure these values are only in the Page Fragment and do not get commited until the user presses the Submit button.
newSOEmailMessage(widget);
widget.datasource.createItem();
app.closeDialog();
Thank you for your help!

one way you can accomplish this is by passing the data to Custom Properties defined in your Page Fragment and then you can place those properties to the corresponding fields. I recommend you also check this article https://developers.google.com/appmaker/ui/viewfragments#use_custom_properties_to_customize_page_fragments
First you need to create the Custom Properties inside your Page Fragment. Then in your Copy button onClick event you can use something like this to save the row data from your table to the Custom Properties:
var rowDataSource = widget.datasource.item._key;
app.datasources.SystemOrders.selectKey(rowDataSource);
var projectName = app.datasources.SystemOrders.item.project_name;
var deliveryInfo = app.datasources.SystemOrders.item.delivery_info;
//...
app.pageFragments.SystemOrders_Edit.properties.ProjectName = projectName;
app.pageFragments.SystemOrders_Edit.properties.DeliveryInfo = deliveryInfo;
//...
app.showDialog(app.pageFragments.SystemOrders_Edit);
Assuming you have a form inside your Page Fragment, you can bind the value of each field with custom properties. Binding will ensure that the data is pre-populated. This can be done for each field via the Property Editor and the binding should look like this: #properties.ProjectName
Inside your Submit button onClick event you can use something like this to create a new item in the datasource using the values available in each field.
var projectName = widget.root.descendants.Field1.value;
var deliveryInfo = widget.root.descendants.Field2.value;
//...
var myDatasource = app.datasources.SystemOrders_HideComplete;
var myCreateDatasource = myDatasource.modes.create;
var draft = myDatasource.modes.create.item;
draft.project_name = projectName;
draft.delivery_info = deliveryInfo;
//...
// Create the new item
myCreateDatasource.createItem();
app.closeDialog();
You can set properties back to null once item is created (maybe onDetach) like this:
app.pageFragments.SystemOrders_Edit.properties.ProjectName = null;
Hope this helps!

I have a feeling that removing this line from the Copy Button click handler will make a trick(of course, if your page fragment is bound to ds.modes.create.item):
createDataSource.createItem();
In case, you are using Manual save mode and you are trying to reuse Page Fragment without overriding datasource... you need create new items using different approach:
// Copy Button click handler
var source = widget.datasource.item;
var listDatasource = app.datasources.SystemOrders_HideComplete;
// This line will add new item to the list datasource
// without saving it to database.
listDatasource.createItem();
var target = listDatasource.item;
// Enter fields you want to duplicate below
target.Field1 = source.Field1;
target.Field2 = source.Field1;
...
// Show fragment (assuming it is bound to listDatasource.item)
app.showDialog(app.pageFragments.EditItemFragment);
// -----------
// Page Fragment's Submit Button click handler
...
listDatasource.saveChanges(function() {
// TODO: handle successful save
});

Thank you to Pavel and Wilmar. The solution that worked for me is listed below:
//Allows for copying table/row
var rowDataSource = widget.datasource;
var listDatasource = app.datasources.SystemOrders_HideComplete;
var createDataSource = listDatasource.modes.create;
widget.datasource.saveChanges();
// Enter fields you want to duplicate below
createDataSource.item.ShowName = rowDataSource.item.ShowName;
createDataSource.item.DeliveryInfo = rowDataSource.item.DeliveryInfo;
createDataSource.item.SOB = rowDataSource.item.SOB;
createDataSource.item.Notes = rowDataSource.item.Notes;
app.datasources.SystemOrders.selectKey(widget.datasource.item._key);
app.showDialog(app.pageFragments.SystemOrders_Add);

Related

Is it possible to create multiple draft items on createdatasource?

I am building an application that will have the ability to create agenda items to discuss in a meeting. The agenda item might include one or more attachments to discuss so there is a one to many relation between the AgendaItems and the AgendaDocs models. So far, I have an insert form that looks like this:
The "Select File" button is a drive picker and the code I have inside the onDocumentSelect event is the following:
var docs = result.docs;
var createDataSource = app.datasources.AgendaDocs.modes.create;
for(var i=0; i<docs.length-1; i++){
var uniqueDraft = createDataSource.item;
createDataSource.items.push(uniqueDraft);
}
for(var i=0; i<createDataSource.items.length-1; i++){
var draft = createDataSource.item;
createDataSource.items[i].DocTitle = docs[i].name;
createDataSource.items[i].DocURL = docs[i].url;
createDataSource.items[i].DriveID = docs[i].id;
}
console.log(createDataSource.items);
The code is supposed to fill out the the List widget below the "Select File" button, but as how you see, the three items are the same. The datasource of the List widget is "AgendaDocs.modes.create" and the datasource of the insert form is "AgendaItems.modes.create".
Reading the official documentation from appmaker, makes me think it is possible since the properties of "CreateDataSource" includes "items". I need help from an expert here. Is this possible? Am I using the wrong approach?
First things first, it seems that you are trying to create records from different models and relationship between them in a one call... at this time App Maker is not that smart to digest such a complex meal. Most likely you'll need to break your flow into multiple steps:
Create (persist) Agenda Item
Create AgendaDocs records and relation with AgendaItem
Similar flow is implemented in Travel Approval template app, but it is not exactly the same as yours, since it doesn't create associations in batches.
Going back to the original question. Yep, it is possible to have multiple drafts, but not with the Create Datasource. You are looking for Manual Save Mode. Somewhere in perfect world your code would look similar to this:
// AgendaItems in Manual Save mode
var agendaDs = app.datasources.AgendaItems;
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it.
agendaDs.createItem();
var agendaDraft = agendaDs.item;
// Field values can be populated from UI via bindings...
agendaDraft.Type = 'X';
agendaDraft.Description = 'Y';
// onDocumentSelect Drive Picker's event handler
var docsDs = agendaDs.relations.AgendaDocs;
result.docs.forEach(function(doc) {
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it...however it will throw an exception
// with this message:
// Cannot save a foreign key association for the 'AgendaItem'
// relation because the target record has not been persisted
// to the server. To fix this, call saveChanges()
// on the data source for that record's model: AgendaItem
docsDs.createItem();
var docDraft = docsDs.item;
docDraft.DocTitle = doc.name;
docDraft.DocURL = doc.url;
docDraft.DriveID = doc.id;
});
// submit button click
agendaDraft.saveChanges();

Trying To Filter Only Rows That Meet Two Criteria

I promise I have read through the Query information page, but obviously I am missing/misunderstanding something.
I have a Table that has the statuses for multiple departments (the fields are Strings). When a user loads that table I want App Maker to hide jobs that have been finished.
The way we categorize a job as finishes is when:
The Inventory Status = Complete and when the The Delivery Status = Delivered.
Both these conditions need to be met.
Example:
Inventory (Complete) + Delivery (Delivered) = hide
Inventory (In Progress) + Delivery (Delivered) = don't hide
Inventory (Complete) + Delivery (Scheduled) = don't hide
I tried the following, however it hides all the example listed above, not just the first one.
var datasource = app.datasources.SystemOrders;
var inventory = ['Complete'];
var delivery = ['Delivered'];
datasource.query.filters.InventoryStatus._notIn = inventory;
datasource.query.filters.DeliveryStatus._notIn = delivery;
datasource.load();
I have also tried this:
var datasource = app.datasources.SystemOrders;
datasource.query.filters.InventoryStatus._notIn = 'Complete';
datasource.query.filters.DeliveryStatus._notIn = 'Delivered';
datasource.load();
But I get this error:
Type mismatch: Cannot set type String for property _notIn. Type List is expected. at SystemOrders.ToolBar.Button2.onClick:2:46
Any help would be greatly appreciated.
Filters are using AND operator. Please consider switching the Datasource Query Builder and applying the following query:
"InventoryStatus != :CompleteStatus OR DeliveryStatus != :DeliveredStatus"
Set CompleteStatus variable to Complete
Set DeliveredStatus variable to Delivered
Explanation:
Filter you want to apply is "NOT(InventoryStatus = Complete AND DeliveryStatus = Delivered)" which is equivalent to "InventoryStatus != Complete OR DeliveryStatus != Delivered".
Vasyl answer my question perfectly, but I wanted to add a few details in case anyone needs to do the same thing and aren't familiar with using the Datasource Query Builder.
All I did was click the Database I was using and then clicked the Datasources section at the top.
I clicked Add Datasource, named it a new name and pasted Vasyl's code into the Query Builder Expression box.
Two new boxes appear below it allowing me to enter the desired statuses that I wanted to filter out.
Lastly I went back to my Table and changed its datasource to my newly created datasource.
Since you are changing your datasource, if you have any extra code on there it may need to be updated to point to the new datasource.
Example:
I had some buttons that would filter entries for the various departments.
So this:
widget.datasource.query.clearFilters();
var datasource = app.datasources.SystemOrders;
var statuses = ['Complete'];
datasource.query.filters.WarehouseStatus._notIn = statuses;
datasource.load();
had to change to this:
widget.datasource.query.clearFilters();
var datasource = app.datasources.SystemOrders_HideComplete;
var statuses = ['Complete'];
datasource.query.filters.WarehouseStatus._notIn = statuses;
datasource.load();
You can use multiple run and then concatenate their results something like following
/**
* Retrieves records for ActionItems datasource.
* #param {RecordQuery} query - query object of the datasource;
* #return {Array<ActionItems>} user's rating as an array.
*/
function getActionItemsForUser_(query) {
var userRoles = app.getActiveUserRoles();
query.filters.Owner._contains = Session.getActiveUser().getEmail();
var ownerRecords = query.run();
query.clearFilters();
query.filters.AddedBy._contains = Session.getActiveUser().getEmail();
var addedByRecords = query.run();
return addedByRecords.concat(ownerRecords);
}

Getting fragment's id into detail controller

I'm working on a sap fiori Master-Detail application on sap web ide.
i want to filter a table i have on detail view based on an ID i will get from a list in master view.The table's detail view is located in a fragment. I created a js function to filter that table in the detail controller but i can't seem to have the id of the table in the fragment.
Here's my detail-controller's code:
this._oView = this.getView();
this._oView.attachAfterRendering(function() {
var sPath1 = "Qmnum";
var sOperator1 = "EQ";
var sValue1 = "10000000";
var oFilter1 = new sap.ui.model.Filter(sPath1, sOperator1, sValue1);
var oBinding = this.byId("tableid").getBinding("items");
oBinding.filter(oFilter1);
});
i tried using also this
sap.ui.getCore().byId();
&
this.getView().byId();
but still undefined.
If XML fragments are used within XML views, giving the view ID as
fragment ID will allow calling this.byId(…) in the view’s controller
to retrieve controls inside the fragment.
var oFragment = sap.ui.xmlfragment(this.getView().getId(), "my.useful.VerySimpleUiPart" );
Initialize the fragment and attach it to the current view:
// Init fragment
var oFragment = sap.ui.xmlfragment("herePutJourneyToFragment.fragmentName", this.getView().getController());
// Attach the fragment to the current view
this.oView.addDependent(oFragment);
After that you should be able to find the table in you view:
var oTable = this.byId("tableid");
EDITED 13:45 150317 bellow:
Tutorial.

How Can I Duplicate A Item/Record?

(Deleted my old question to simplify it. )
I enter data in a table, I then want to make an exact duplicate of that data in a new item/record/row*.
*not sure the proper term.
Is there any way to accomplish this?
Sorry for the slow response. Here is what you should do:
Add a "copy" button in the row. In the onClick on that button, add this code:
var createDataSource = widget.datasource.modes.create;
var rowDataSource = widget.datasource;
createDataSource.item.foo = rowDataSource.item.foo;
createDataSource.item.bar = rowDataSource.item.bar;
// And so on for each field
createDataSource.createItem();
You could probably make sure of javascript for-in to loop through all the properties of the item in so you don't have to manually specify each record, but I didn't have time to experiment with this.
Edit:
The above code won't show the copied record in the list immediately, because I used row's create data source, instead of the lists create data source. Try this instead:
var rowDataSource = widget.datasource;
// Instead of using the row datasource for create, explicitly use the data source of your list.
var listDatasource = app.datasources.NameOfYourListsDataSource;
var createDataSource = listDatasource.modes.create;
createDataSource.item.foo = rowDataSource.item.foo;
createDataSource.item.bar = rowDataSource.item.bar;
// And so on for each field
createDataSource.createItem();

Retrieve multiple value in one session

when i add to cart
i using Session["Cart"] = new List() { Id }; which is get the id pass from the query string
but when i preview on the cartview event i add 2/3 product, it only will show 1 column, which is the latest. why it will replace? my code look like. how should i do to make sure every time i add it will be display on the view cart page? if this code problem or my cart preview page have probelm?
Try this example ,
string[] a = new string[]{"a","b","c"};
Session["values"] = a;
And you can retrieve it like this.
string[] a = (string[])Session["values"]
using List
Session["test"] = yourList;
And you can retrieve it like this.
var list = (List<string>)Session["test"];

Resources