Why are my asynchronously retrieved data only appearing after I click (Vue) - asynchronous

I have an input with an #change event:
<f7-searchbar
:clear-button="true"
#focus="showFilters=true"
#blur="showFilters=false"
v-model="searchText"
#change="handleSearch"
>
This is meant to get search results from a database and display them in a table.
The method to get the results:
handleSearch: function(){
var preResults = []
var postResults
var self=this
var vals
db.query('categories/search', {reduce: false}).then(function (res) {
console.log(res['rows'][0]['key'] + ' is the result of searched')
vals = res['rows']
}).then(function(){
self.searchResults = vals.map(row => {
return row['key']['metadata']
})
})
},
Which should be displayed in this table:
<table>
<tr v-for="entry in searchResults" class="popup-trigger" style="text-align: left;">
<span>{{ entry }}</span>
</tr>
</table>
I am expecting these results to update when searchText changes. But that doesn't happen. The results display only after I enter something into the search, and then click where the table should be displayed. I have no idea why clicking should update it. Can anyone help me understand this?

You should use #input event instead of #change event
Also my suggestion is to not send request for every character, for example if you want to search apple, and type fast:
a - send request
p - send request
p - send request
l - send request
e - send request
You can use timer to check if user stoped with typing
<input #input="userStopTyping"
userStopTyping () {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(this.yourFunction, 200)
}
So if you type fast apple, you will send only one request.

Related

Trigger a button click from a URL

We need to scrape VEEC Website for the total number once a week.
As an example, for the week of 17/10/2016 - 23/10/2016 the URL returns the number Total 167,356 when the search button is clicked. We want this number to be stored in our database.
I'm using coldfusion to generate the weekly dates as params and have been passing them like the above URL. But I'm unable to find a query param so that the "Search" button click event is triggered.
I've tried like this & this but nothing seems to be working.
Any pointers?
It seems like for every form submission, a CRSF token is added, which prevents malicious activity. To make matters worse for you, the CRSF token is changed for each form submission, not just for each user, which makes it virtually impossible to circumvent.
When I make a CFHTTP POST request to this form, I get HTML FileContent back, but there is no DB data within the results table cell placeholders. It seems to me that the form owner allows form submission from an HTTP request, but if the CRSF token cannot be validated, no DB data is returned.
It maybe worth asking the website owner, if there is any kind of REST API, that you can hook into...
If you want to use a headless browser PhantomJS (https://en.wikipedia.org/wiki/PhantomJS) for this, here is a script that will save the total to a text file.
At command prompt, after you install PhantomJS, run phantomjs.exe main.js.
main.js
"use strict";
var firstLoad = true;
var url = 'https://www.veet.vic.gov.au/Public/PublicRegister/Search.aspx?CreatedFrom=17%2F10%2F2016&CreatedTo=23%2F10%2F2016';
var page = require("webpage").create();
page.viewportSize = {
width: 1280,
height: 800
};
page.onCallback = function (result) {
var fs = require('fs');
fs.write('veet.txt', result, 'w');
};
page.onLoadStarted = function () {
console.log("page.onLoadStarted, firstLoad", firstLoad);
};
page.onLoadFinished = function () {
console.log("page.onLoadFinished, firstLoad", firstLoad);
if (firstLoad) {
firstLoad = false;
page.evaluate(function () {
var event = document.createEvent("MouseEvents");
event.initEvent("click", true, true);
document.querySelectorAll(".dx-vam")[3].dispatchEvent(event);
});
} else {
page.evaluate(function () {
var element = document.querySelectorAll('.dxgv')[130];
window.callPhantom(element.textContent);
});
setTimeout(function () {
page.render('veet.png');
phantom.exit();
}, 3000);
}
};
page.open(url);
The script is not perfect, you can work on it if you're interested, but as is it will save the total to a file veet.txt and also save a screenshot veet.png.

Form link send and later submit to sender in Meteor

A Meteor web app being used by a logged in user "merchant" who needs to create a link and sms/email it to his customer. The link opens a form. The customer fills up the form and submits it so that the data gets inserted with a property merchantId, since many merchants can send to many customers.
This single page app is not using a Router but able to sms/email. How can linking a form between a merchant and a customer be accomplished elegantly so that the data from the correct customer gets "linked" to the correct merchant? Thanks
Merchant Part
You can trigger after a successful send of the email/sms a meteor method, that stores a record of the sent email/sms in a collection (in this example named Record). This could be the schema for it:
Record Collection Schema (Server/Client)
{
merchantId:String, // the id of the sender
customer:String, //id or name or address or phone num of receiver
opened:Boolean, //when the link is opened can be set to true
link:String, // the link to be send,
type:Number, //0=sms, 1=email,
expires:Date, //in case the link is not opened
}
You can for example create a Meteor method to insert the record after send:
Insert Record (Server)
Meteor.methods({
insertRecord:function(recordData) {
//... check recordData by schmema and permissions here
return Records.insert(recordData);
}
})
Sending the Email/SMS
So the merchant part of the app sends the link via sms/email and calls the insertRecord method to store the record of the saved .
Save Record (Client or Server)
const callback=function(err, res) {
if (res) { // assume your sent was successful here
Meteor.call("insertRecord", {
//... your record credentials
});
}
}
// a little bit of pseudocode
if (sms)
sms.send(form, callback);
else
email.send(form, callback);
Customer Part
When the customer opens the link it triggers a templatethat will render your form. You can initially run a meteor method to check the Record collection for a document that matches the link url.
Get Record by Url Method (Server)
Meteor.methods({
getRecordByUrl:function(url) {
//... do your input checks here
return Records.findOne({link:url});
},
});
Template for Form (Client)
Template.customerForm.onCreated(function(){
const instance = this;
instance.state = new ReactiveDict();
instance.state.set("record", null);
instance.autorun(function(){
// if no record loaded yet
if (!instance.state.get("record")) {
Meteor.call("getRecordByUrl", window.location.href, function(err, res) {
if (err || !res) {
//...handle err
}
this.state.set("record", res);
}.bind(instance));
}
});
});
Template.customerForm.helpers({
getRecord() {
return Template.instance().state.get("record");
},
getMerchantId() {
const record = Template.instance().state.get("record");
return record.merchantId;
}
});
You can then use this document to add the merchantId to the form either as a hidden input or via html data attribute.
{{#if getRecord}}
<form id="...">
<input type="hidden" name="merchantId" value="{{getMerchantId}}" />
<!-- other inputs here -->
</form>
{{/if}}
The examples can of course be optimized but I think this way it clearer to understand.

CasperJS Multiple pages check requests

I'm tracking requests from multiple pages across same domain. Instead of hardcoding steps I tried to automate loop which loops through array of url-s, opens it, clicks element and wait for request which needs to be evaluated.
casper.test.begin('track request', items.length, function suite(test) {
casper.start().eachThen(items,function(response){
this.thenOpen(response.data,function(response){
this.echo(response.url);
this.test.assertExist("button.track", "Button exists");
this.mouseEvent('click', 'button.track');
var res;
this.waitForResource(function check(resource){
res = resource;
return resource.url.indexOf("click_event") != -1;
}, function(){
this.echo("Resource found" + res.url);
// parse resource code
},function(){},15000);
});
casper.run(function() {
test.done();
});
});
I' ve tried with listening for resoursceRequested event, but nothing I tried gave results.
So how can I do this and then parse request to assert its value.
Thank you.
M
edit:
casper.on('resource.received', function(resource) {
var match = resource.url.match('p1=tracking');
if(match != null) this.echo(resource.url);
});
I get every request page make, but not the one I only need, the one after click on button
edit 2:
"TypeError: 'null' is not an object (evaluating 'b.parentNode')"
"[object Object]"

Meteor reacts to first change only after second change is made

I've reached the point in my first Meteor app where it's time to move all of my update and inserts over to Meteor.methods on the server side. The first thing that I noticed is that I lost the "instant" reactivity once I did this. Here is what I have:
html
<template name="income">
{{#each accounts}}
<tr>
<td class="row1"><input type="text" maxlength="32" value="{{income_acct_name}}" id="income_acct_name{{_id}}"></td>
<td class="row2" align="right"><input type="text" size="13" value="{{income_acct_budget}}" id="income_acct_budget{{_id}}"></td>
</tr>
{{/each}}
</template>
<template name="cashIn">
{{#each accounts}}
<tr>
<td class="row1"><input type="text" size="18" value="{{cashIn_acct_name}}" id="cashIn_acct_name{{_id}}" readonly></td>
<td class="row2_protected" align="right">{{cashIn_acct_budget}}</td>
</tr>
{{/each}}
</template>
client/income.js
Template.income.events ({
'change td.row2': function(theEvent, theTemplate) {
var changedRow = this._id;
var budget = parseFloat(theTemplate.find('#income_acct_budget'+changedRow).value.replace(/[^\/\d.-]/g,''));
var incomeCursor = income.find({"_id": changedRow});
incomeCursor.forEach( function(acct) {
total = acct.income_acct_total;
});
var achieved = 0;
if (budget > 0)
{
achieved = Math.round(total/budget*100);
}
Meteor.call("incomeBudgetChange", changedRow, budget, achieved);
// Update Total Income Budget
var incomeCursor = income.find({"userID": Meteor.userId()});
var budgetTotal = 0;
incomeCursor.forEach( function(acct) {
budgetTotal = budgetTotal + acct.income_acct_budget;
});
var achieved = 0;
if (budgetTotal > 0)
{
achieved = Math.round(incomeTotal/budgetTotal*100);
}
Meteor.call('totalIncomeBudgetChange', totalIncomeID, budgetTotal, achieved);
}
});
server/income.js
Meteor.methods({
'incomeBudgetChange': function(changedRow, budget, achieved){
income.update (
{"_id": changedRow},
{$set: {"income_acct_budget": budget, "income_budget_achieved": achieved}}
)
},
'totalIncomeBudgetChange': function(totalIncomeID, budgetTotal, achieved){
cashIn.update (
{"_id": totalIncomeID},
{$set: {"cashIn_acct_budget": budgetTotal, "cashIn_budget_achieved": achieved}}
)
}
});
The issue here is with totalIncomeBudgetChange. I start out with 10,000 in both {{income_acct_budget}} and {{cashIn_acct_budget}}. I change the value to 15,000 on the income side and the cashIn side is still 10,000. I then change the income side to 20,000 and now the cashIn side is 15,000. cashIn is always one change behind. What happened here when I moved the updating process to the server side? How do I get them to be in sync again?
The issue with your code right now is that your Meteor.calls are asynchronous but you still consider that they are synchronous : see how after calling the first Meteor.call you immediately start trying to fetch the updated income collection by iterating over a cursor and performing a computation on its supposed updated value, but it's not yet updated !
By moving your collection.update code to the server you understand that you have to perform a round-trip to the server before the client can acknowledge that the collection was indeed modified : this is why client-side Meteor.call are asynchronous most of the time (client-side Meteor.call can be synchronous by providing a method stub when it makes sense to achieve latency compensation).
Your problem is that the code executing immediately after your first Meteor.call assumes that the collection (which is a mini-mongo replica subset of the actual MongoDB server persisted collection) is already updated, but it won't be the case until the server modifications are executed and sent back to the client.
I think you should simply refactor your 2 methods calls into one server-side method call that will perform the updates synchronously as the second method call does not depend on the result of the first one : in general, it does not make sense to perform 2 method calls if the user didn't performed 2 separate actions on their own.

Meteor.autorun() not working on client when insert occures

i have been knocking my head for 2 days now in that .
am creating a search engine, am creating queries dynamically using Meteor Framwork, the queries are working fine and when i search i can rebind the UI (Table in My Case) with the dynamic data query output.
however if an insert/update/delete operation occures the data object
and the UI (html Table) is not updating.
which means that the template is not re-rendered when the data object changes.
Template.search.rendered = function () {
Meteor.autorun(function() {
alarmsData = Alarms.find(getSearchSelector($('#searchTxt').val(), $('#startTimeTxt').val(), $('#endTimeTxt').val())).fetch()
console.log("rendered")
//alarmsData = Alarms.find({},{sort: {timestamp: "desc"} }).fetch();
searchControls(alarmsData)
getConsole(alarmsData, ".console")
$('#badge').html(alarmsData.length)
})
}
the get console function is just reading the array from teh search and creating an html table (this is working fine)
as for the begining i am creating a simple query as the default for my search. and then am changing this query whenever user changes the search criteria. i can notice that only the first instance of teh data object is kept and tracked for changes, so if the second search criteria resides within the first one, it's updating the UI, if not nothing happenes
i have used Meteor.autorun(function(){}) function however i traced it's execution with console.log and i can see it's no excuting when i insert data in the database for the same collection.
One, I believe you are trying to use Deps.autorun. Also, there is nothing in your autorun that seems to be dependent on a reactive source. Since alarmsData is taking a snapshot of data it won't care when Alarms has data changing.
Second, I would probably approach this with a redirect. I would compile my data, and redirect to the same page, allowing the server to handle the querying for me. This easily allows you to jump to this page from anywhere else with a prefilled query in the parameters (because the route would then handle it) and also gives a visual change to the navigation bar when a search has happened (just like every other search engine). You would do something like this on a button click:
var query = {},
path;
query.text = encodeURIComponent($('#searchTxt').val()),
query.start = encodeURIComponent($('#startTimeTxt').val()),
query.end = encodeURIComponent($('#endTimeTxt').val()),
// redirect to current path
path = Router.routes[Router.current().route.name].path({}, {
query: query
});
Router.go( path );
In your router you would just pass the query into your server and route as a data object (assuming you are using iron-router):
this.route( "search", {
path: "/search",
waitOn: function() {
return [
Meteor.subscribe( "searchAlarms", _.omit( this.params, "hash" ) ),
]
},
data: function () {
return { "query": _.omit( this.params, "hash" ) };
}
});
This will not only give you the query data that was used for the search (in your template) but the server can now handle the search for you! Your Alarms data now holds all of the documents needed to display to the user and you no longer need to subscribe to all your Alarms. This is also great because it is automatically reactive. So if a new Alarm matches your query filter it will automatically be passed down to the client and displayed to the user without needing to setup any extra dependencies/autoruns.
Note though, that if you are subscribing to Alarms elsewhere you will still need to do filtering client-side.
What a strange meteor code…
The "rendered" code method code is called once you will be rendering the search template
getSearchSelector($('#searchTxt').val() is not reactive, my advise is to use the session variable to put your search criteria inside and use this same session to inject the find parameters inside.
Are you looking for displaying all the alarms Data ?
function getAlarms()
{
var text = Session.get("text");
var from = Session.get("start");
var to = Session.get("end");
var filter = getSearchSelector(text, from, to);
return Alarms.find(filter);
}
Template.search.alarms = function () {
return getAlarms();
}
Template.search.alarmsCount = function () {
return getAlarms().count();
}
Template.search.events({
'keypress input[name=text]' : function(e,o)
{
var val = $("input[name= text]").val()
Session.set("text", val);
},
'keypress input[name=start]' : function(e,o)
{
var val = $("input[name=start]").val()
Session.set("start", val);
},
'keypress input[name=end]' : function(e,o)
{
var val = $("input[name=end]").val()
Session.set("end", val);
}
});
// And your template will look something like:
<template name="search">
Search alarms
<input type="text" name="text" placeholder="Enter your text here…"/>
<input type="text" name="start" placeholder="start time"/>
<input type="text" name="end" placeholder="end time/>
There is {{alarmsCount}} alarms(s);
{{#each alarms}}
Alarm object: {{.}}
{{/each}}
</template>
I Guess its Solved it by using Session.set & get, and automatically subscribing to the Serevr and send the dynamic Query.
Check the below Code
Template.zConsole.rendered = function () {
Session.set("obj", getSearchSelector($('#searchTxt').val(), $('#startTimeTxt').val(), $('#endTimeTxt').val()))
Deps.autorun(function (){
Meteor.subscribe("dynamicAlarms", Session.get("obj"))
console.log("Count from AutoRun ==> " + Alarms.find(Session.get("obj")).count())
})
}
on the server
Meteor.publish('dynamicAlarms',function (searchObj) {
return Alarms.find(searchObj)
})
& it works perfect with less code.

Resources