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-*".
Related
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
EDIT: as the original question was too vague I have updated it to make it more concrete
I'd like to create a dashboard in Meteor that shows some statistics about my collections (e.g. how many docs, how many users...). I have been trying the past days but can't seem to find a good/intelligent way.
I initially just did the following:
Template.dashboard.helpers({
getProductsCount: function() {
return Products.find().count();
}
});
This did not work. I think because it counts the number of products from minimongo, but not sure.
Then I did the following:
In the template helper, call a method and get the value to show on the dashboard page (does not work)
Was told not to use pub/sub mechanism for this type of metric
Worked via Session variables (did work, but feels a bit strange to store this kind of metric data in Session variables
So then I read in another SO response about Reactive Variables and tried the following:
Template.dashboard.helpers({
getProductsCount: function() {
return Template.instance().myAsyncValue.get();
}
});
Template.dashboard.created = function() {
var self = this;
self.myAsyncValue = new ReactiveVar("Waiting for response from server");
Meteor.call('getProductsCount', function(error, asyncValue){
if (error)
console.log(error);
else
self.myAsyncValue.set(asyncValue);
});
};
This works, but I find this extremely difficult for something as simple as showing a product count (or any other metric). Not sure I understand the reason why I should use sth as reactive variables?
Then -out of curiosity- I tried the following (using meteor add simple:reactive-method) and it works:
Template.customerDashboard.helpers({
getProductsCount: function () {
return ReactiveMethod.call("getProductsCount");
}
});
So the question really is why having to use Reactive variables and methods for sth as simple as this. Can someone explain?
If you want to show the count only in the view, the best way is to return the count number only. you do not need publish/subscribe at all. you can use server methods. and if you want to show data also, you can go for pub-sub. and your approach is correct.
So I'm trying to figure out a way to get a value from the browser when a test is running with DalekJS. As far as I can tell, it's not currently possible. I even had a look at hacking the Dalek code, but since I know very, very little about CommonJS and NodeJS other than installing and running things, I figured I might actually ask around first.
What I'm trying to do is something like:
// Non-working example
module.exports = {
'My Test': function(test) {
var foo;
test
.open('http://localhost/mysite')
.execute(function(){
foo = document.getElementById('bar');
})
.type('#myField', foo)
.done();
}
}
Now I know why this code doesn't work: because the execute() function is executed in the context of the browser window (as though I was typing it into the console).
The question I'm asking is: is there a way I can retrieve a value to use in my test?
Context information: My use case is that I'm testing an E2E scenario where a form is submitted and a code is provided to the user. This code then needs to be entered on another page. I could potentially set up mock values for the purpose of testing, but then it's not a true scenario.
Thanks in advance!
Yes,
that is a usecase we haven't implemented yet, but until we haven't found a proper solution, you can use a workaround:
module.exports = {
'My Test': function(test) {
test
.open('http://localhost/mysite')
.execute(function(){
var foo = document.getElementById('bar').innerText;
document.getElementById('myField').setAttribute('value', foo);
})
.done();
}
};
This method has some problems, none of the eventhandlers like keyup, keydown etc. will be fired when adding the value to the field in this way.
If your code doesn't rely on them, you are good to go.
Else you would have to wait some weeks until the new version is out, which will provide a better solution for such scenarios.
Hope that helps.
Cheers
Sebastian
Based on the documented example of the .log.message function, you can do something like the following:
module.exports = {
'My Test': function(test) {
test
.open('http://localhost/mysite')
.execute(function(){
var foo = document.getElementById('bar').value;
this.data('foo', foo);
})
.type('#myField', test.data('foo'))
.done();
}
}
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.