We have methods to create and update the item in App Maker, but it will save the whole record. If I want to update only a particular field on the button click, there is no option.
Please post if there are any options to update a particular field?
Automatic Save Mode
In auto save mode App Maker instantly saves every field, so you have per-field saving granularity. In other words, whenever field's value is changed App Maker sends request to server to save the modification.
// this code will trigger async call to server to save the modification
// only for this particular field.
app.datasources.MyDatasource.item.MyField = 'My new value';
Manual Save Mode
With manual save mode there is no easy way to save modifications subsets separately, since whenever you call 'saveChanges' method App Maker will try to persist all modifications made to the datasource. Here are some pretty bad workarounds that I would not recommend unless you have no other options:
// Implementation with callback chaining if field save
// order matters. It will work extremely slow.
var ds = app.datasources.MyDatasource;
ds.item.MyField1 = 'My new value 1';
ds.saveChanges(function() {
ds.item.MyField2 = 'My new value 2';
ds.saveChanges(function() {
ds.item.MyField3 = 'My new value 3';
...
});
});
// Implementation without chaining. In theory should work
// faster(if it would work at all...)
var ds = app.datasources.MyDatasource;
ds.item.MyField1 = 'My new value 1';
ds.saveChanges();
ds.item.MyField2 = 'My new value 2';
ds.saveChanges();
ds.item.MyField3 = 'My new value 3';
ds.saveChanges();
...
Server Script
Pretty much same answer as for Manual Save mode, it is doable, but I would not recommend do it since performance will significantly degrade.
record.MyField1 = 'My new value 1';
app.saveRecords([record]);
record.MyField2 = 'My new value 2';
app.saveRecords([record]);
record.MyField3 = 'My new value 3';
app.saveRecords([record]);
...
Related
i m doing this in my Client script which is being called by the function of UE script button.
var createEstimatorURL = url.resolveRecord({
recordType: 'customrecord_awt_estimator_hdr',
recordId: '',
isEditMode: true,
params: {
'project': project,
'customer': customer,
'createdBy': createdBy,
'projectStatus': projectStatus,
'subsid': subsid,
'awtEstRef': awtEstRef
}
});
After getting the URL from resolve record, I m doing newWindow = window.open(createEstimatorURL);
The record is getting opened in create mode (not saved yet), but how to source the fields that I sent as params? please help
They are in the request parameters of your context on the new page that opens.
You can use this bit
context.request.parameters['*'],
where * is the name of your parameter, to get them.
Is there a way to call an external API Endpoint on Google Forms every time the form is filled out?
First:
you'll need to set up your App script project and you'll do that by:
Visit script.google.com to open the script editor. (You'll need to be signed in to your Google account.) If this is the first time you've been to script.google.com, you'll be redirected to a page that introduces Apps Script. Click Start Scripting to proceed to the script editor.
A welcome screen will ask what kind of script you want to create. Click Blank Project or Close.
Delete any code in the script editor and paste in the code below.
This video and the doc will help
Second
you'll need to create an installable trigger, you can add it to the form directly or to the spreadsheet that has the responses
function setUpTrigger(){
ScriptApp.newTrigger('sendPostRequest') /* this has the name of the function that will have the post request */
.forForm('formkey') // you'll find it in the url
.onFormSubmit()
.create();
}
Check the doc
Third
create the sendPostRequest function and add the UrlFetchApp to it
function sendPostRequest(e){
// Make a POST request with form data.
var resumeBlob = Utilities.newBlob('Hire me!', 'text/plain', 'resume.txt');
var formData = {
'name': 'Bob Smith',
'email': 'bob#example.com',
'resume': resumeBlob
};
// Because payload is a JavaScript object, it is interpreted as
// as form data. (No need to specify contentType; it automatically
// defaults to either 'application/x-www-form-urlencoded'
// or 'multipart/form-data')
var options = {
'method' : 'post',
'payload' : formData
};
UrlFetchApp.fetch('https://httpbin.org/post', options);
}
Check the doc
Try something like this in your app script:
var POST_URL = "enter your webhook URL";
function onSubmit(e) {
var form = FormApp.getActiveForm();
var allResponses = form.getResponses();
var latestResponse = allResponses[allResponses.length - 1];
var response = latestResponse.getItemResponses();
var payload = {};
for (var i = 0; i < response.length; i++) {
var question = response[i].getItem().getTitle();
var answer = response[i].getResponse();
payload[question] = answer;
}
var options = {
"method": "post",
"contentType": "application/json",
"payload": JSON.stringify(payload)
};
UrlFetchApp.fetch(POST_URL, options);
};
Be sure to replace the POST_URL variable with your webhook, you can use requestcatcher.com to test this out.
Add a trigger to the script by clicking "Triggers" in the side menu
Open the menu (top-right dots)
Click in Script Editor
Paste the above code (changing the POST_URL)
Click in the clock icon (left-side menu), which means Triggers.
On the right-bottom corner, click in the blue Add trigger button (a form will show as the image below).
It should show onSubmit under Choose which function to run.
Make sure Select event type is set as On form submit.
Click Save button.
After that, submit your form and watch for the request to come in.
This is pretty straightforward with Google Scripts.
Just create a new project bound to your spreadsheet and create 2 elements:
A function that will contain all relevant data to make the call (see docs for making a HTTP request from Google Apps Script)
A trigger linked to the spreadsheet. You can set it to run each time an edit occurs or form is submitted
VoilĂ , your sheet will call whatever endpoint you wish on submission. You can even parse the spreadsheet to return that data to your endpoint
I'm trying to update the id (not pk) of a record onAfterCreate in auto save mode. The createContractNum_SOW() is a server script that returns a number. I have also tried using
app.datasources.MyDatasource.item.MyField = 'My new value';
but that does not work either.
Is there something that I am missing?
if (contract_type === "Statement of Work"){
var next_id = createContractNum_SOW();
record.contract_num = next_id;
}
You probably need to save the record after you modify it.
Since the datasource events are server side you can do that with app.saveRecords([record]);
In App maker we enabled the manual save mode. On button click a new form will open and we will create an empty record, when user fills the fields and clicks the save button saveChanges function will save all the values.
In documentation and sample projects I can see after a record creation _key value is updated in the data source and we can use that key value to query record from its child model.
But in our case key value is not returned. But after save changes function when we open that record key value is coming, what could be the issue.
You don't have record key on the client, until you save it, since record key is generated by the server. This applies both to Auto and Manual save modes.
Here is code snippet from App Maker documentation:
var myCreateDatasource = app.datasources.MyDatasource.modes.create;
var draft = myCreateDatasource.item;
draft.Name = "Name";
draft.Age = 21;
// Create the new item
myCreateDatasource.createItem(function(newRecord) {
// Callback is called after the record is saved, so it now has a key.
var key = newRecord._key;
// Do something with the key here.
}
I'm in the process of learning meteor. I followed the tutorial to create microscope. If some one submits a post meteor will re render the template for all users. This could be very annoying if there are hundreds of posts then the user will come back to the top of the page and loose track of where he was. I want to implement something similar to what facebook has. When a new post is submitted template isn't rendered rather, a button or link will appear. Clicking it will cause the template to re-render and show the new posts.
I was thinking of using observeChanges on the collection to detect any changes and it does stop the page from showing new posts but only way to show them is to reload the page.
Meteor.publish('posts', function(options) {
var self = this, postHandle = null;
var initializing = true;
postHandle = Posts.find({}, options).observeChanges({
added: function(id, post) {
if (initializing){
self.added('posts', id, post);
}
},
changed: function(id, fields) {
self.changed('posts', id, fields);
}
});
self.ready();
initializing = false;
self.onStop(function() { postHandle.stop(); });
});
Is this the right path to take? If yes, how do I alert the user of new posts? Else, what would be a better way to implement this?
Thank you
This is a tricky question but also valuable as it pertains to a design pattern that is applicable in many instances. One of the key aspects is wanting to know that there is new data but not wanting to show it (yet) to the user. We can also assume that when the user does want to see the data, they probably don't want to wait for it to be loaded into the client (just like Facebook). This means that the client still needs to cache the data as it arrives, just not display it immediately.
Therefore, you probably don't want to restrict the data displayed in the publication - because this won't send the data to the client. Rather, you want to send all the (relevant) data to the client and cache it there until it is ready.
The easiest way involves having a timestamp in your data to work from. You can then couple this with a Reactive Variable to only add new documents to your displayed set when that Reactive Variable changes. Something like this (code will probably be in different files):
// Within the template where you want to show your data
Template.myTemplate.onCreated(function() {
var self = this;
var options = null; // Define non-time options
// Subscribe to the data so everything is loaded into the client
// Include relevant options to limit data but exclude timestamps
self.subscribe("posts", options);
// Create and initialise a reactive variable with the current date
self.loadedTime = new ReactiveVar(new Date());
// Create a reactive variable to see when new data is available
// Create an autorun for whenever the subscription changes ready() state
// Ignore the first run as ready() should be false
// Subsequent false values indicate new data is arriving
self.newData = new ReactiveVar(false);
self.autorun(function(computation) {
if(!computation.firstRun) {
if(!self.subscriptionsReady()) {
self.newData.set(true);
}
}
});
});
// Fetch the relevant data from that subscribed (cached) within the client
// Assume this will be within the template helper
// Use the value (get()) of the Reactive Variable
Template.myTemplate.helpers({
displayedPosts = function() {
return Posts.find({timestamp: {$lt: Template.instance().loadedTime.get()}});
},
// Second helper to determine whether or not new data is available
// Can be used in the template to notify the user
newData = function() {
return Template.instance().newData.get();
});
// Update the Reactive Variable to the current time
// Assume this takes place within the template helper
// Assume you have button (or similar) with a "reload" class
Template.myTemplate.events({
'click .reLoad' = function(event, template) {
template.loadedTime.set(new Date());
}
});
I think this is the simplest pattern to cover all of the points you raise. It gets more complicated if you don't have a timestamp, you have multiple subscriptions (then need to use the subscription handles) etc. Hope this helps!
As Duncan said in his answer, ReactiveVar is the way to go. I've actually implemented a simple facebook feed page with meteor where I display the public posts from a certain page. I use infinite scroll to keep adding posts to the bottom of the page and store them in a ReactiveVar. Check the sources on github here and the live demo here. Hope it helps!