How to make a simple database query in Meteor - javascript

Every time I have a new idea for an application, I start with Meteor. And every time I end up using something else. And I think it comes down to me not knowing how to do a simple database query.
Here's a hypothetical example. I want to make an app where the user types something into a box, presses a button, and then an image of whatever they typed in shows up. It uses some image search api.
<template name="image">
<input type="text" class="description"></input>
<button class="showImage"></button>
<img src="{{img}}"></img>
</template>
Seems simple enough so far. Now there isn't a way to send information to the client without putting it in a database first, as is my understanding. Let's assume we have some function addToDB that takes queries and enters the image information into the database.
Template.image.events({
'click .showImage': function() {
addToDB($('.description').val());
}
});
Great! That's still not too bad. But now to send the data back to the client...
//server.js
Meteor.publish("image", function(query) {
Images.find({q: query});
}
But wait. We can't just subscribe when the client loads, because we don't know the query yet. So maybe the event handler needs to be
Template.image.events({
'click .showImage': function() {
addToDB($('.description').val());
Deps.autorun(function() {
Meteor.subscribe("images", $('.description').val());
});
}
});
Okay, let's feed that into the template...
Template.image.img = function() {
return Images.findOne().imgsrc;
}
Nope, that results in an error because when the template is first loaded, we haven't subscribed to Images yet. So we can update the template like so:
<template name="image">
<input type="text" class="description"></input>
<button class="showImage"></button>
{{#each info}}
<img src="{{info.img}}"></img>
{{/each}}
</template>
And then change the template filling function to:
Template.image.info = function() {
return Images.find({}, {limit: 1});
}
And voila!
I spent longer than I'm willing to admit stumbling through all of that this evening. If it was just plain old node, I could've used some simple jQuery.
But there are a million amazing features that Meteor has that I really want to take advantage of. So what I'm hoping is that someone can show me the way. What mistakes did I make in this process, and in the final result? What's the nicest, cleanest, simplest way to get this done within a meteor app.
This is so complicated, within a framework that makes so many other things so simple. So how can I just make a simple database query?

Consider looking at the meteor examples they provide. All of the examples have the functionality of taking user input, managing collections, and displaying collection queries to templates.
Most of your code looks fine but you are asking several questions at once and starting from a perspective that makes it difficult to answer. Some general notes:
consider using Session to hold variables a user sets with an event.
subscribe to the data you want by passing Session variable to a Deps.autorun function
display the data you want by passing a Session variable to the template.
basic javascript rules still apply - null.someThingIWant is an error. A handy pattern is return something && something.someThingIWant;
One problem in the code above is that your publish is not returning the query results. I assume that is a typo.

Related

Netsuite Customize NetSuite Transaction Summary

I'm trying to figure out how to change the Transaction Summary on the sales order UI.
I've been doing some research and I'm getting the impression I need to create a user event script(?)
For example: I want to add more details like Markup / Freight cost / etc.
I'm very new to this, but please let me know if I'm going in the right direction.
/**
* #NApiVersion 2.0
* #NScriptType UserEventScript
*/
define([], function() {
return {
afterSubmit : function (context) {
var salesorder = context.newRecord;
var total = salesorder.getValue('total');
var subtotal = salesorder.getValue('subtotal');
var tbl = document.getElementById("totallingtable");
log.debug('total', total);
log.debug('subtotal', subtotal);
}
};
});
NetSuite does not support access to the native UI through the DOM. You could do this on client side, but it will absolutely break the record. Therefore, I would recommend either:
Making an HTML field on the Sales Order record and writing HTML/CSS to mimic the Transaction Summary box. It would probably be best to place it in its own subtab; something like "Transaction Details", and write to it with a client script on pageInit().
Add a button to the Sales Order record on User Event beforeLoad(). Have that button open a Suitelet that essentially mimics a popup with HTML/CSS to mimic the Transaction Summary as stated in example 1.
Thinking about it, I think the second option is better.
If you need additional help, please comment back and I'll be happy to assist. I have no problem stepping through code, even if baby steps.
EDIT:
Based on the conversation below, the second option seems better for your business needs.
Before we dive into that, I did some research and believe you were trying to do something similar to this post. That approach may very well work, but is not something I would do, citing this excerpt from the NetSuite Help Documentation:
"SuiteScript does not support direct access to the NetSuite UI through the Document Object Model (DOM). You should only access the NetSuite UI by using SuiteScript APIs."
NetSuite Help Documentation: SuiteScript 2.0 Custom Pages
In addition, I'm adding a few notes to this post regarding the whole thing overall. I'll say this once here, and once at the end as a reminder. But remember, it carries for everywhere.
First, if you don't understand something, don't hesitate to ask questions. The only stupid question is the one you don't ask at any time, even if it's the i-th time you're asking as asking questions is a fundamental part of learning anything. Second, make sound coding decisions. There's no way for me to know if this code is still exactly what you need, especially since I don't know your business or the setup of your account. But, I'm sure going to try my best to help in the spirit of help.
With that said, for this example, do the following in order. Once you get the hang of it, you can work with the nuances as you see fit. I won't discuss any of the nuances here. I wish I could, but that's somewhat like explaining how to ride a bike. Here we go!
Make a sub-folder in your SuiteScripts folder called "Transaction_Details" (make sure to use the underscore). We're going to upload everything to here so it's all in one folder under the folder where we need it to be for scripts to run (that is the SuiteScripts folder). From the Administrator role that's:
Documents > Files > SuiteScripts > Add Folder
Create an HTML file that displays what you're looking for when opened on localhost. Name that file "Transaction_Details.html". NetSuite will default to this file name later, and we're going to use it a few times in our example. So, don't name the file something else. You may want to test the file in a few browsers, especially if you're supporting Internet Explorer. Since we're trying to mimic the Transaction Summary, let's make that our target. To me, it looks like a table with some CSS to make it stand out. Transaction summary is also a native NetSuite term, so we'll call this our "Transaction Details". This way, when someone says, "I don't see it in the Transaction Summary", you can say "Click the Transaction Details button which shows more details than the summary". We want to pass some numbers into this HTML which are the numbers that pertain to our "details". We don't really need syntax to do this, but it can help our readability if we, sort of, alert ourselves that whatever is supposed go in this place is marked in some way. So, we'll arbitrarily mark this as [VARIABLE_TO_REPLACE]. A sample file is below to get you started. When you have the file the way you like it, upload that to your "Transaction_Details" subfolder. From the Administrator role that's:
Documents > Files > SuiteScripts > Transaction_Details > Add File
Select Transaction_Details.html from your computer and set the folder to SuiteScripts : Transaction_Details
<!DOCTYPE html>
<html>
<head>
<title>Transaction Details</title>
<style>
<!--Any CSS you might need; background color, bold, etc.-->
</style>
</head>
<body>
<table>
<tr>
<td>Gross</td>
<td>[GROSS]</td>
</tr>
<tr>
<td>Discount</td>
<td>[DISCOUNT]</td>
</tr>
<tr>
<td>Variable To Replace</td>
<td>[VARIABLE_TO_REPLACE]</td>
</tr>
<!--Rinse and repeat-->
</table>
</body>
</html>
Create a Saved Search with whatever information you need. Let's title the Saved Search "Transaction Details" and give it an ID of "_transaction_details". You may need more than one Saved Search to get everything out. But, in many, many, situations, you can get the data out in one Saved Search. Most times, this is done using SQL. Unfortunately, I don't know everything you'll need here, so I have to leave much of this step up to you; there's a lot of nuances here. However, one thing you should consider is using a filter to filter results which only pertain to a record's Internal ID. This will ensure you're only searching for information on a record. In the Saved Search, you can pick any Internal ID of a relevant record(a Sales Order, most likely) and test on that. Once your results are how you like them, remove the Internal ID filter, as we will dynamically push this as an object in our Suitelet (coming up in step 4). Eventually, you'll need to provide access to the Saved Search. Access is controlled on the Saved Search itself using things like the Audience subtab, and the "Public" checkbox. You will know which access is best for your business. But, note that for anyone to see the Transaction Details we are building they will at least need access to the search. Access permissions is a whole other thing with NetSuite; nuances. To create a saved search from the Administrator role that's:
List > Search > Saved Searches > New > Transaction
Warning: this is a big step, but it really is all one step. Create a Suitelet that loads our HTML file and replaces our placeholder text with the proper text for that placeholder. Call this file "Transaction_Details_Suitelet.js". We're going to replace the text by running the Saved Search we created with an Internal ID filter that points to our transaction and filters the data, making it easier to extract. Now, I'm making an assumption here that the data we need came out in only one row of results. If there is more than one row, that is fine, but you'll have to do your own formatting, or give me a screenshot of your results so I can edit my answer again. An example is below to get you started. This is going to take some configuration, so it's best to get something into NetSuite that at least passes it's code inspection (which is done as you try to upload the file). Once it's uploaded, you can open the URL to the Script from its Script Deployment, which if error-free will render the HTML we've written in step 2. If it doesn't and you're getting frustrated, just upload the script as seen in example 1 below. All example 1 is saying is, "render my HTML", on the condition that your HTML is properly formatted. If that works, you know "Transaction_Details.html" is good, so you can move onto example 2. If example 2 breaks, it is likely "Transaction_Details_Suitelet.js" that is the problem. To upload the Suitelet from the Administrator role that's:
Customization > Scripting > Scripts > New > Select Transaction_Details_Suitelet.js from your computer
Set the folder to SuiteScripts : Transaction_Details
Some error checking will now occur.
If it passes, title it "Transaction Details Suitelet", and give it an ID of "_transaction_details_sl" (sl is short for Suitelet)
Click the Deployments subtab, and title the Deployment "Transaction Details Suitelet". Give it an id of "_transaction_details_sl".
So, the Script and its Deployment should mimic each other in their names. This will be important in our next step
Example 1:
/**
* #NScriptType Suitelet
* #NApiVersion 2.x
*/
define(["N/file"], function(file) {
function onRequest(context) {
if (context.request.method === "GET") {
var fileTransactionDetails = file.load({
id: "/SuiteScripts/Transaction_Details/Transaction_Details.html"
}).getContents();
//Tell NetSuite to take the HTML in our "Transaction_Details.html" file and render that. We'll navigate to the Suitelet which renders our HTML file in step 5.
context.response.write(fileTransactionDetails);
}
}
return {onRequest: onRequest}
})
Example 2
/**
* #NScriptType Suitelet
* #NApiVersion 2.x
*/
define(["N/file"], function(file) {
function onRequest(context) {
if (context.request.method === "GET") {
var fileTransactionDetails = file.load({
id: "/SuiteScripts/Transaction_Details/Transaction_Details.html"
}).getContents();
var searchTransactionDetails = search.load({
id: "customsearch_transaction_details"
});
searchTransactionDetails.filters.push(search.createFilter({
name: "internalid",
operator: search.Operator.EQUALTO,
value: context.request.params.id
}));
var resultsTransactionDetails = searchTransactionDetails.run();
resultsTransactionDetails.each(function(result) {
// getText, if you need the text representation
fileTransactionDetails = fileTransactionDetails.replace("[GROSS]", result.getText(resultsTransactionDetails.columns[0]);
// getValue, if you need something that is a date, a SQL formula result, or something "behind the scenes" like the Internal ID of an element in a list/record.
fileTransactionDetails = fileTransactionDetails.replace("[GROSS]", result.getValue(resultsTransactionDetails.columns[0]);
// I'm intentionally commenting this line out, but if you had more than one row to consider, you would return true, which tells the search, "go to the next row". For now, don't do that.
//return true;
});
//Tell NetSuite to take the HTML in our "Transaction_Details.html" file and render that. We'll navigate to the Suitelet which renders our HTML file in step 5.
context.response.write(fileTransactionDetails);
}
}
return {onRequest: onRequest}
})
Create a User Event Script that places a button, on a Transaction, on beforeLoad, in view mode only, that opens our Suitelet in step 4, where step 4 runs our search in step 3, replaces text in our HTML file from step 2, and renders the whole thing (sorry for the long sentence. but that's really the one sentence summary of the whole thing anyways). A very important note here: it is best to turn buttons on in view mode only so you can ensure all the data which you need is written to the database.
We'll need to upload the User Event script and tell the script to make the button appear on only the records selected in the User Event's Deployment tab. Let's assume its a Sales Order for now. From the Administrator role that's:
Customization > Scripting > Scripts > New > Select Transaction_Details_User_Event.js from your computer and set the folder to SuiteScripts : Transaction_Details
Some error checking will now occur.
If it passes, title it "Transaction Details User Event", and give it an ID of "_transaction_details_ue" (ue is short for User Event).
Select the Deployments tab > Applies To > Sales Order and set the ID to "_so_transaction_details_ue" which is like saying "This is the Sales Order Deployment for Transaction Details on the User Event side".
/**
* #NScriptType UserEventScript
* #NApiVersion 2.x
*/
define(["N/url"], function(url) {
function beforeLoad(context) {
if (context.type === context.UserEventType.VIEW) {
// This gives us the link to our Suitelet.
// We can pass any URL parameters to our Suitelet with params.
// One param you will definitely need is id,
// which is the id of the current record you are opening
// the transaction details from, and is used in our
// Saved Search in step 3.
// Recall that we need the id's of our Suitelet and it's Script
// Deployment to be exactly what we said above. NetSuite will prepend
// "customscript" and "customdeploy" to the id of the Suitelet and
// Deployment, respectively, so we need to do the same.
// Missing this step is a common reason why links won't open as
// expected.
// Lastly, although returnExternalUrl defaults to false, let's
// set it to false for peace of mind. We don't want to expose
// anything to the outside.
var urlTransactionDetails = url.resolveScript({
scriptId: "customscript_transaction_details_sl",
deploymentID: "customdeploy_transaction_details_sl",
params: {
id: context.newRecord.id
},
returnExternalUrl: false
});
// Because we're using at least 2.1, we can use `backticks` to
// interpolate urlTransactionDetails into window.open, which makes
// things much easier.
context.form.addButton({
id: "custpage_view_transaction_details,
label: "Transaction Details,
functionName: `window.open("${urlTransactionDetails}");`
});
}
}
return {beforeLoad: beforeLoad}
}
And that's it! Hopefully that gets you farther along. As I stated before, don't hesitate to ask questions. Also, practice good coding practices. Practice good anything for that matter. My intentions are to help you and others here. Unfortunately, this post will not solve everything, but I hope it will help you and others who stop by.
Let me know how it goes!

Javascript - How to update a document in couchdb?

So we need to be able to update JSON objects in cloudant using javascript. Our teacher told us something about using the _rev number but I've got no clue on how to begin.
Let's say that this is the document I need to update:
{
_id:"bla",
_rev:"blabla",
name:"something"
}
And I want to update it to this:
{
_id:"bla",
_rev:"blabla",
name:"something else"
}
Please see the following docs: http://guide.couchdb.org/editions/1/en/api.html#revisions
Basically you need to fetch the existing document, change the fields and post it back, making sure the revision is correct. Revision is used to prevent updates of documents that have been updated since you got your copy (conflict).

MEAN / AngularJS app check if object already posted

I have thig angularJS frontend and I use express, node and mongo on the backend.
My situation looks like:
//my data to push on server
$scope.things = [{title:"title", other proprieties}, {title:"title", other proprieties}, {title:"title", other proprieties}]
$scope.update = function() {
$scope.things.forEach(function(t) {
Thing.create({
title: t.title,
//other values here
}, function() {
console.log('Thing added');
})
})
};
//where Thing.create its just an $http.post factory
The HTML part looks like:
//html part
<button ng-click="update()">Update Thing</button>
Then on the same page the user has the ability to change the $scope.things and my problem is that when I call update() again all the things are posted twice because literally thats what I'm doing.
Can someone explain me how to check if the 'thing' its already posted to the server just to update the values ($http.put) and if its not posted on server to $http.post.
Or maybe its other way to do this?
I see a few decisions to be made:
1) Should you send the request after the user clicks the "Update" button (like you're currently doing)? Or should you send the request when the user changes the Thing (using ngChange)?
2) If going with the button approach for (1), should you send a request for each Thing (like you're currently doing), or should you first check to see if the Thing has been updated/newly created on the front end.
3) How can you deal with the fact that some Thing's are newly created and others are simply updated? Multiple routes? If so, then how do you know which route to send the request to? Same route? How?
1
To me, the upside of using the "Update" button seems to be that it's clear to the user how it works. By clicking "Update" (and maybe seeing a flash message afterwards), the user knows (and gets visual feedback) that the Thing's have been updated.
The cost to using the "Update" button is that there might be unnecessary requests being made. Network communication is slow, so if you have a lot of Thing's, having a request being made for each Thing could be notably slow.
Ultimately, this seems to be a UX vs. speed decision to me. It depends on the situation and goals, but personally I'd lean towards the "Update" button.
2
The trade-off here seems to be between code simplicity and performance. The simpler solution would just be to make a request for each Thing regardless of whether it has been updated/newly created (for the Thing's that previously existed and haven't changed, no harm will be done - they simply won't get changed).
The more complex but more performant approach would be to keep track of whether or not a Thing has been updated/newly created. You could add a flag called dirty to Thing's to keep track of this.
When a user clicks to create a new Thing, the new Thing would be given a flag of dirty: true.
When you query to get all things from the database, they all should have dirty: false (whether or not you want to store the dirty property on the database or simply append it on the server/front end is up to you).
When a user changes an existing Thing, the dirty property would be set to true.
Then, using the dirty property you could only make requests for the Thing's that are dirty:
$scope.things.forEach(function(thing) {
if (thing.dirty) {
// make request
}
});
The right solution depends on the specifics of your situation, but I tend to err on the side of code simplicity over performance.
3
If you're using Mongoose, the default behavior is to add an _id field to created documents (it's also the default behavior as MongoDB itself as well). So if you haven't overridden this default behavior, and if you aren't explicitly preventing this _id field from being sent back to the client, it should exist for Thing's that have been previously created, thus allow you to distinguish them from newly created Thing's (because newly created Thing's won't have the _id field).
With this, you can conditionally call create or update like so:
$scope.things.forEach(function(thing) {
if (thing._id) {
Thing.update(thing._id, thing);
}
else {
Thing.create(thing);
}
});
Alternatively, you could use a single route that performs "create or update" for you. You can do this by setting { upsert: true } in your update call.
In general, upsert will check to see if a document matches the query criteria... if there's a match, it updates it, if not, it creates it. In your situation, you could probably use upsert in the context of Mongoose's findByIdAndUpdate like so:
Thing.findByIdAndUpdate(id, newThing, { upsert: true }, function(err, doc) {
...
});
See this SO post.
#Adam Zemer neatly addressed concerns I raised in a comment, however I disagree on some points.
Firstly, to answer the question of having an update button or not, you have to ask yourself. Is there any reason why the user would like to discard his changes and not save the work he did. If the answer is no, then it is clear to me that the update should not be place and here is why.
To avoid your user from loosing his work you would need to add confirmations if he attempts to change the page, or close his browser, etc. On the other if everything is continuously saved he has the peace of mind that his work is always saved and you dont have to implement anything to prevent him from loosing his work.
You reduce his workload, one less click for a task may seem insignificant but he might click it many time be sure to have his work save. Also, if its a recurrent tasks it will definitely improve his experience.
Performance wise and code readability wise, you do small requests and do not have to implement any complicated logic to do so. Simple ng-change on inputs.
To make it clear to him that his work is continuously save you can simply say somewhere all your changes are saved and change this to saving changes... when you make a request. For exemple uses, look at office online or google docs.
Then all you would have to do is use the upsert parameter on your mongoDB query to be able to create and update your things with a single request. Here is how your controller would look.
$scope.update = function(changedThing) { // Using the ng-change you send the thing itself in parammeter
var $scope.saving = true; // To display the saving... message
Thing.update({ // This service call your method that update with upsert
title: changedThing.title,
//other values here
}).then( // If you made an http request, I suppose it returns a promise.
function success() {
$scope.saving = false;
console.log('Thing added');
},
function error() {
//handle errors
})
};

Change URL data on page load

Hello I have a small website where data is passed between pages over URL.
My question is can someone break into it and make it pass the same data always?
For example let say, when you click button one, page below is loaded.
example.com?clicked=5
Then at that page I take value 5 and get some more data from user through a form. Then pass all the data to a third page. In this page data is entered to a database. While I observe collected data I saw some unusual combinations of records. How can I verify this?
yes. as javascript is open on the website, everyone can hack it.
you will need to write some code on you backend to validade it.
always think that you user/costumer will try to hack you sytem.
so take precautions like, check if user is the user of the session, if he is logged, if he can do what he is trying to do. check if the record that he is trying get exists.
if u are using a stand alone site, that u made the entire code from the ashes, you will need to implement this things by yourself.
like using the standard php session, making the data validation etc.
or you can find some classes that other people have made, you can find a lot o this on google. as it is a common problem of web programing.
if u are using a backed framework that isnt from another world, probably already has one. sp, go check its documentation.
html:
<a id = 'button-one' name = '5'> Button One </a>
javascript:
window.onload = function() {
document.getElementById('button-one').onclick = function() {
changeURL(this.attributes.name.value);
};
};
function changeURL(data) {
location.hash = data;
}

Postponing search until template load

I want to make a request to a server to get a bunch of news articles based off of what the user clicks on (recent, trending, etc). I'd like to be able to load the page first and show a loading bar while I wait for the response from the API. What I have, and although it works and returns the JSON data I need, will wait until the response comes back from the server before loading anything. This is all it is so far:
What I want to achieve is the following: Load up an empty array of objects, and then make API calls to articles incrementally (let's say grab all the articles from this hour, then last hour, then the hour before, and so on) and whenever I retrive articles populate the view (I'm assuming I can just inject them into a controller somehow) however I'm getting lost on the Emberisms on how to add to that array of objects. I think I need to make an ArrayController and then create a model in said array, then call a function to add to it, but as I said I'm lost as to how to add items into that controller on the fly
App = Ember.Application.create();
App.Router.map(function() {
this.resource('today');
});
App.TodayRoute = Ember.Route.extend({
model: function() {
return $.getJSON('/today');
}
});
To elaborate a bit on the reasoning for my question - I'm confused on how to approach this with ember. I'm familiar on how to do something like this using jquery, however I'm trying to learn the framework and am having a little bit of trouble originally knowing what the division of labor is between the two. I know the actual AJAX requests should be jquery, but I want to do as much as possible in Ember. If this however is something that should be done by jquery though, then that is fine as well!
The getJSON method looks to have a callback function on success you could use, something like this maybe?
App.TodayRoute = Ember.Route.extend({
model: function() {
$.getJSON('/today', function(data) {
// hide loading here
/ data is the JSON returned
});
}
});
Check out this link http://api.jquery.com/jQuery.getJSON/
I found the solution - and like everything with ember, it was very simple. I used some blank fixture data, then defined my controller as below.
App.ArticleRoute = Ember.Route.extend({
model: function() {
return App.Article.find();
}
})
Then upon pageload I call a method focused around
App.Article.createRecord({
// Attributes, pulling from the returned JSON
})
Simple! :)

Categories

Resources