I have this little snippet of code:
getSearchQuery(evt) {
let query = evt.target.value;
Meteor.subscribe('searchResults', query, function() {
console.log('subscription ready');
});
}
render() {
return (
<TextField
hintText='Hint text'
onChange={this.getSearchQuery.bind(this)}
fullWidth={true}
/>
)
}
After typing first letter in input I expect in console 'subscription ready' output, but this output appears only after typing the second letter. In other words the 'subscription ready' output delays till the next letter typing.
For example if the search string is '123', typing '123' will result in two 'subscription ready' outputs, while I am expecting for three.
Is it possible to fix this problem somehow?
I noticed you are passing onChange prop to I recommend that you use onKeyPress see: https://facebook.github.io/react/docs/events.html#keyboard-events as It's pure react event, as for the subscription ready log. Make sure you made proper conditions in your publish function, instead of .length >= 2 to .length > 2.
Related
I have a few questions (I am new to Dart) w.r.t the code below which I found. What I am trying to achieve:
1) Write more than one line in the brackets for errors. But I get error saying 'set literals were supported upto 2.2....'. It would also be good to know the list of possible errors so I can alert the user accordingly.
2) Increment a counter pertaining to number of documents and keep storing document id's in one variable, by overwriting the last value if more than one document is found. I am trying to check that there should only be one document and take action if counter goes to 2.
void _checkIfAdmin() {
Firestore.instance
.collection('Admin')
.where('Email', isEqualTo: widget._user.email)
.snapshots()
.handleError((error)=>{
//
})
.listen((data) => data.documents.forEach((doc) => gpCounter+=1)); // Store document id?
print('Counter: $gpCounter');
}
Here I am getting printed gpCounter=0 when I know there are two documents in there. I tried adding async/await in the function but it seems the firebase statement does not return any future so I get a warning to use await only for futures.
I will try to address as many issues as I could understand in your question. If you have any other issues, you should probably ask another question.
Set literals: the syntax for anonymous functions is () {} and not () => {}.
() => is a shorthand syntax to return the value of a single expressions and in your case, you are returning a Set in your anonymous functions because {} create Set's (or Map's if you have colons in there).
The solution for this is (error) { ... }.
You print your gpCounter value synchronously, however, you are listening to a Stream.
Because of that, you should move your print statement to your listen function:
.listen(data) {
print('Counter: ${data.documents.length}');
})
I'm trying to print variable in the console, For this I have the configuration below. the first method works fine, but not the second method containing an argument.
In index.wxml file, I have:
<button onclick='test1'>Button 1</button>
<button onclick='test2("bar")'>Button 1</button>
In index.js, I have:
Page({
test1 {
console.log('foo')
},
test2(v) {
console.log('foo: ' + v)
},
})
What's wrong with the second method ?
I know that it's been a while since you asked this question and maybe you don't need an answer anymore, but I ran into this problem too, so probably others will too.
Page is a function that takes a json object as input. So you should rewrite index.js like this:
Page({
test1 : function() {
console.log('foo')
},
test2 : function(v) {
console.log('foo: ' + v)
},
})
The documentation for the Page class is hard to find, and as far as I can tell, they haven't translated it. It exists here in Chinese.
Even though those are technically anonymous functions, you can use the keys of the json object in your wxml file as if they were functions. Your wxml file is basically right, but I'm not sure that onclick actually works. Try bindtap or bindchange.
This page has Tencent's english documentation of how to handle events in the WXML. I hope this helps someone!
Dear Guys.In wechat mini program,if you want to pass arguments to a function,you should do it like in the following way,for examples
Firstyly,you write your tap events in your index.wxml
<view bindtap="handleTap" data-item="100"> passing arguments through events</view>
Then you can define a function call handleTap in your index.js file,like
handleTap:function(e){
console.log(e);
console.log(e.currentTarget.dataset.item);
}
Now when you click,you can see the value log in the console,wechat mini programming passes arguments through the attribute "data-*".
I have an HTML form (really simple one, no tricky parts like Ajax, ...) on a simple page. I try to read default value of any input (type="text", again no tricks) and store it in constant for later use (assert).
HTML looks like:
<form method="post" ...>
<input type="text" id="xyz" name="xyz" value="123">
</form>
Cypress test, that DOESN'T work, looks like:
describe('General tests', function(){
context('General', function() {
it('can read the input and store in const', function () {
cy.visit('http://localhost/settings')
const xyz = Cypress.$('#xyz').val()
cy.log(xyz)
})
})
})
This doesn't work. BUT after several hours of playing (by accident this worked for me in more complex test suite = file). What I realized is, that this construct works as expected in case previous test ( = it() ) visits the same URL as the last visited URL. Than it works like a miracle.
Cypress test, that WORKS, looks like:
describe('General tests', function(){
context('General', function() {
it('makes magic and allows the next test to work', function () {
cy.visit('http://localhost/settings')
})
it('can read the input and store in const', function () {
cy.visit('http://localhost/settings')
const xyz = Cypress.$('#xyz').val()
cy.log(xyz)
})
})
})
I thought that tests should be independent, but it looks like they are not.
I tried other ways how to get the value of input info variable and the closest what I needed was using closure ".then()", but it can be used for just a single input, not for more complex forms.
Simple asserting like "cy.get('#id_of_input').should('eq', ...)" works fine, but it doesn't allow me to work with default (and by test overridden) value of inputs.
So my questions:
1) Is it OK to use included jQuery in this way to get and store value of input into constant? If now what is the other way for cases when I need to do this for like 5 various input fields in the form (for a signel input closure will be just fine)
2) Is it OK that tests influence each other?
Thanks everyone for any help.
To answer your questions:
1) According to the docs, Cypress.$ is "a great way to synchronously query for elements". (emphasis theirs)
The way you're using it circumvents the intended Cypress workflow with asynchronous command queuing. If you don't know what I'm talking about here, I suggest reading the fantastic introduction to Cypress in their documentation.
I would suggest the following as an alternative to the code you posted:
cy.visit('http://localhost/settings');
cy.get('#xyz').then(elem => {
// elem is the underlying Javascript object targeted by the .get() command.
const xyz = Cypress.$(elem).val();
cy.log(xyz);
});
.then(). lets you queue up code to be run in sequence with your test by the test runner. See this answer for more info about command queuing and .then().
2) Yes, it is okay for describe functions to influence each other. Cypress runs each file separately, but separate describes are simply run in the sequence they are queued in.
For example, the following code will work just fine:
let message = "";
describe("Set a value", () => {
it("Sets a value", () => {
message = "hi";
});
});
describe("Retrieve a value", () => {
it("Retrieves and prints the set value", () => {
cy.log(message); // Logs "hi" to the Cypress log window
});
});
I have a Meteor Helper that does a GET request and am supposed to get response back and pass it back to the Template, but its now showing up the front end. When I log it to console, it shows the value corerctly, for the life of mine I can't get this to output to the actual template.
Here is my helper:
UI.registerHelper('getDistance', function(formatted_address) {
HTTP.call( 'GET', 'https://maps.googleapis.com/maps/api/distancematrix/json? units=imperial&origins=Washington,DC&destinations='+formatted_address+'&key=MYKEY', {}, function( error, response ) {
if ( error ) {
console.log( error );
} else {
var distanceMiles = response.data.rows[0].elements[0].distance.text;
console.log(response.data.rows[0].elements[0].distance.text);
return distanceMiles;
}
});
});
In my template I pass have the following:
{{getDistance formatted_address}}
Again, this works fine and shows exactly what I need in the console, but not in the template.
Any ideas what I'm doing wrong?
I posted an article on TMC recently that you may find useful for such a pattern. In that article the problem involves executing an expensive function for each item in a list. As others have pointed out, doing asynchronous calls in a helper is not good practice.
In your case, make a local collection called Distances. If you wish, you can use your document _id to align it with your collection.
const Distances = new Mongo.collection(); // only declare this on the client
Then setup a function that either lazily computes the distance or returns it immediately if it's already been computed:
function lazyDistance(formatted_address){
let doc = Distances.findOne({ formatted_address: formatted_address });
if ( doc ){
return doc.distanceMiles;
} else {
let url = 'https://maps.googleapis.com/maps/api/distancematrix/json';
url += '?units=imperial&origins=Washington,DC&key=MYKEY&destinations=';
url += formatted_address;
HTTP.call('GET',url,{},(error,response )=>{
if ( error ) {
console.log( error );
} else {
Distances.insert({
formatted_address: formatted_address,
distanceMiles: response.data.rows[0].elements[0].distance.text
});
}
});
}
});
Now you can have a helper that just returns a cached value from that local collection:
UI.registerHelper('getDistance',formatted_address=>{
return lazyDistance(formatted_address);
});
You could also do this based on an _id instead of an address string of course. There's a tacit assumption above that formatted_address is unique.
It's Meteor's reactivity that really makes this work. The first time the helper is called the distance will be null but as it gets computed asynchronously the helper will automagically update the value.
best practice is not to do an async call in a helper. think of the #each and the helper as a way for the view to simply show the results of a prior calculation, not to get started on doing the calculation. remember that a helper might be called multiple times for a single item.
instead, in the onCreated() of your template, start the work of getting the data you need and doing your calculations. store those results in a reactive var, or reactive array. then your helper should do nothing more than look up the previously calculated results. further, should that helper be called more times than you expect, you don't have to worry about all those additional async calls being made.
The result does not show up because HTTP.call is an async function.
Use a reactiveVar in your case.
Depending on how is the formated_address param updated you can trigger the getDistance with a tracker autorun.
Regs
Yann
I have an object of message streams that looks like this:
ractive.data.messages:
{
stream_id1: {
some_stream_metadata: "foo",
stream: [
{id: "someid1", message: "message1"},
{id: "someid2", message: "message2"}
]
},
stream_id2: {
some_stream_metadata: "bar",
stream: [
{id: "someid3", message: "message3"},
{id: "someid4", message: "message4"}
]
}
}
main_template:
{{#messages[ current_stream_id ]}}
{{>render_message_stream}}
{{/messages[ current_stream_id ]}}
render_message_stream:
{{#stream}}
<div class="stream">
...someotherstuff...
{{>render_message}}
</div>
{{/stream}}
render_message:
<div class="message">
...someotherstuff...
{{message}}
</div>
I change "current_stream_id" to change the rendered stream of messages.
On updates, i change the contents of the message streams like this:
ractive.merge(
"messages.stream_id1.stream",
new_message_stream,
{
compare: function ( item ) { return item.id; }
});
I also tried the compare: true option instead of the function, with the same results:
Ractive always thinks that these two messages belong effectively to the same DOM element, even though they live in a completely different message stream:
ractive.data.messages[ "stream_id1" ].stream[1].message
ractive.data.messages[ "stream_id2" ].stream[1].message
Problems:
When there are intro/outro animations ractive animates always just the end of the messages stream, even when a message in the middle of the stream was deleted, i need help to make ractive understand which messages are identical.
When i change the current_stream_id, ractive does not rerender the complete {{>render_message_stream}} partial, but goes inside the existing dom and changes the {{message}} field in all existing messages, though this might be good for dom element reuse, this triggers a lot of animations that are wrong. (Eg. it triggers intro/outro animations for the last message in the stream if stream1 has one message more than stream2).
One of these issues has a straightforward answer; unfortunately the other one doesn't.
I'll start with the easy one - the fact that
ractive.data.messages[ "stream_id1" ].stream[1].message
ractive.data.messages[ "stream_id2" ].stream[1].message
belong to the same DOM element. You're correct in that Ractive updates the existing elements rather than removing them and creating new ones - this is a core part of its design. In this case that's undesirable behaviour, but you can work around it like so:
// instead of immediately switching to a new stream ID like this...
ractive.set( 'current_stream_id', 'stream_id2' );
// you can set it to a non-existent ID. That will cause the existing DOM
// to be removed. When you set it to an ID that *does* exist, new DOM
// will be created:
ractive.set( 'current_stream_id', null );
ractive.set( 'current_stream_id', 'stream_id2' );
// or, if you'd like the initial transitions to complete first...
ractive.set( 'current_stream_id', null ).then(function () {
ractive.set( 'current_stream_id', 'stream_id2' );
});
The other issue - that merge() isn't merging, but is instead behaving as though you were doing ractive.set('messages.stream_id1.stream', new_message_stream) - is tougher. The problem is that while you and I know that {{#messages[ current_stream_id ]}} equates to messages.stream_id1 when current_stream_id === 'stream_id1, Ractive doesn't.
What it does know is that we have an expression whose value is determined by messages and current_stream_id. When the value of either of those references changes, the expression is re-evaluated, and if that value changes, the DOM gets updated - but using a standard set(). When you do ractive.merge('messages.stream_id1.stream', ...), Ractive updates all the things that depend on keypaths that are 'upstream' or 'downstream' of messages.stream_id1.stream - which includes messages. So that's how the expression knows that it needs to re-evaluate.
It's possible that a future version of Ractive will be able to handle this case in a smarter fashion. Perhaps it could make a note of arrays that are subject to merge operations, and check evaluator results to see if they're identical to one of those arrays, and if so use merge() rather than set(). Perhaps it could analyse the function in some way to see if the {{#messages[ current_stream_id ]}} section should register itself as a dependant of messages.stream_id1 for as long as current_stream_id === 'stream_id1', rather than the internally-generated ${messages-current_stream_id-} keypath.
None of that helps you in the meantime though. The only way to use merge() in your current situation is to have a separate reference that doesn't use an expression, and a bit of magic with pattern observers:
main_template:
{{#current_messages}} <!-- rather than `messages[ current_stream_id ]` -->
{{>render_message_stream}}
{{/current_messages}}
render_message_stream:
{{#current_message_stream}} <!-- rather than `stream` -->
<div class="stream">
{{>render_message}}
</div>
{{/current_message_stream}}
code:
ractive.observe( 'current_stream_id', function ( id ) {
var current_messages = this.get( 'messages.' + id );
this.set( 'current_messages', current_messages );
// hide existing stream, then show new stream
this.set( 'current_message_stream', null ).then(function () {
this.set( 'current_message_stream', current_messages.stream );
});
});
// when ANY message stream changes, we see if it's the current one - if so, we
// perform a merge on the top-level `current_message_stream` array
ractive.observe( 'messages.*.stream', function ( new_stream, old_stream, keypath, id ) {
// the value of any * characters are passed in as extra arguments, hence `id`
if ( id === this.get( 'current_stream_id' ) ) {
this.merge( 'current_message_stream', new_stream, {
compare: function ( item ) {
return item.id;
}
});
}
});
I've set up a JSFiddle demonstrating this. I hope it makes sense, let me know if not - and sorry I didn't get round to answering this question much sooner.