How to GetLocalizedItems using Tridion Anguilla Framework? - tridion

I would like to use the GetLocalizedItems method in the Anguilla Framework.
I don't know how to create a new filter and set the conditions or what to use for success and failure.
In the GUI:
tridion.Web.UI.ContentManager.WhereUsed.GetListUsedItems(id, filter.conditions,
filter.columns, success, failure);
Are the methods in this namespace intended to be used by our extensions?

Building a filter
Here is an example of how to build a filter
var filter = new Tridion.ContentManager.ListFilter();
filter.conditions.ItemTypes = 16 | 2; // folders and components
filter.conditions.Recursive = true;
filter.conditions.BasedOnSchema = "tcm:1-23-8,tcm:1-32-8".split(",");
filter.columns = Tridion.Constants.ColumnFilter.DEFAULT;
Or this extremely simple case from General.js:
var templateFilter = new Tridion.ContentManager.ListFilter({
conditions: { ItemTypes: [ itemType ] }
});
Calling a WCF method
The second part of your question was really already covered in https://stackoverflow.com/a/9385975/209103, although I'll make it a bit more concrete here.
WCF/AJAX calls such as this are executed asynchronously, since they may take some time to complete. While you would normally simple handle the result of the call on the line after the closing parenthesis, you can't do that in AJAX calls since that line will be execute before the function has completed. Instead you have to pass in one or more callback functions that get called once the function completes.
I typically just pass in two functions that break into the JavaScript debugger of my browser when I first start figuring out such a method:
Tridion.Web.UI.ContentManager.WhereUsed.GetListUsedItems(
"tcm:1-23",
filter.conditions,
filter.columns,
new function() { console.log(arguments); debugger; },
new function() { console.log(arguments); debugger; }
);
So the first (anonymous) function is called when the (asynchronous) HTTP call to the TCM server succeeded, while the second is called when the call failed. In the answer I linked above, we called them onSuccess and onFailure to make their nature more explicit.
Both functions in this case simply write the implicit arguments parameter that is always passed in JavaScript. They then break into the JavaScript debugger of your browser, so you can inspect the arguments further.

Related

Returned value from Meteor Helper not showing up in template

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

How to use wrapAsync in Meteor

I am having trouble figuring out how to use wrapAsync properly using Meteor. I am currently working with node-apac and the Amazon Product Advertising API.
If am trying to run the following code, how do I run it asynchronously:
opHelper.execute('ItemSearch', {
'SearchIndex': 'Books',
'Keywords': 'harry potter',
'ResponseGroup': 'ItemAttributes, Offers'
}, function(err, results) {
console.log(results);
});
I have tried to watch several videos, but am having trouble
Meteor.wrapAsync takes an asynchronous method like opHelper.execute and makes it synchronous. It can do this so long as the last param taken by the method returns a callback where the first param is the error and second the result. This is just like your method!
You make a new method that is synchronous:
var opExecuteSynchronous = Meteor.wrapAsync(opHelper.Execute, opHelper);
The first param opHelper.Execute is the method you want to make asynchronous, and the second param is the context of the method (opHelper).
You can use this synchronously now:
var results = opExecuteSynchronous('ItemSearch', {
'SearchIndex': 'Books',
'Keywords': 'harry potter',
'ResponseGroup': 'ItemAttributes, Offers'
})
This will throw an error if err is called instead of results in the callback.

Can sinon stub withArgs match some but not all arguments

I have a function I am stubbing that gets called with multiple arguments. I want to check just the first argument. The rest are callback function, so I want to leave them alone. Thus, I might have the following 2 calls, using ajax as an example:
method.get = sinon.stub();
method.get(25,function(){/* success callback */},function(){/* error callback */});
method.get(10,function(){/* success callback */},function(){/* error callback */});
I cannot use method.get.calls... because then it doesn't differentiate between the first one get(25) and the second get(10). But if I use method.get.withArgs(25).calls... then it does not match either, because withArgs() matches all arguments, which this does not (and never could, with callbacks like that).
How do I get sinon stubs to check and set responses based on just the 1st arg?
https://sinonjs.org/releases/latest/matchers/#sinonmatchany
You can use sinon.match.any:
method.get.withArgs(25, sinon.match.any, sinon.match.any);
Solution
withArgs can be used to match some but not all the arguments.
Specifically, method.get.withArgs(25) will check just the first argument.
Correction
This is incorrect:
withArgs() matches all arguments
Details
When withArgs is called it remembers the arguments it was passed here as matchingArguments.
Then when the stub is called it gets all matching fakes here.
matchingFakes is called without a second parameter so it returns all fakes that have matchingArguments that match the arguments passed to the stub starting at index 0 up to the length of matchingArguments. This means that a fake will match when its matchingArguments match the beginning of the arguments passed even if there are additional arguments.
Any matching fakes are then sorted by matchingArguments.length and the one that matches the most arguments is the one that is invoked.
The following test confirms this behavior and passes with sinon version 1.1.0 from 7 years ago, version 1.14.0 from the time this question was asked, and the current version 6.3.5:
import * as sinon from 'sinon';
test('withArgs', () => {
const stub = sinon.stub();
stub.withArgs(25).returns('first arg is 25!');
stub.returns('default response');
expect(stub(25)).toBe('first arg is 25!'); // SUCCESS
expect(stub(25, function () { }, function () { })).toBe('first arg is 25!'); // SUCCESS
expect(stub(10, function () { }, function () { })).toBe('default response'); // SUCCESS
});
If you just want to check the first argument you can use
method.get.withArgs(25).calledOnce
or
method.get.calledWith(25)
this method works very well with spies if you want to check only one argument among many
it('should check only first argument', function ():void {
myFunction('foo', 'bar', baz');
expect(myFunctionSpy.firstCall.args[0]).to.equal('foo');
});
However I don't understand why you are using stubs here. If you just want to check how the function is called you should use a spy. If you want to check how it's called AND change it's behaviour (ex: blocking ajax calls) then you should use a mock.
Sinon mocks have their own way of checking stuff. The only way I know for your case would be to use sinon.match.many for the arguments you don't want to check:
it('should check only first argument', async function (): Promise<void> {
mock.expects('myFunction').withExactArgs('foo', sinon.match.any, sinon.match.any).returns('foo');
await myFunction('foo', 'bar', baz');
mock.verify();
});
mock.verify() will proceed to the test AND reset the mock for other tests, in case of using a spy or a stub you should do it mannually with restore() or reset() after each test
PD: sorry about TypeScript syntax here :p

MeteorJS Meteor Methods call from server to server

I understand that Meteor methods let you do a client to server call, but what's the best approach to call another function or method from a Meteor method, i.e. a server to server call.
Right now if I do a regular JS function call it only works if the JS file is in the lib folder. But I need it to be in the server folder.
Here is the code
I have a topics collection which sits in the collection folder and has the following
I have the following which is a collection
Meteor.methods({
topicPost: function(topicAttributes) {
var user = Meteor.user(),
topicWithSameTitle = Topics.findOne({title: topicAttributes.title});
// ensure the user is logged in
if (!user)
throw new Meteor.Error(401, "You need to login to add a new topic");
Meteor.call('checkUser');
}
});
I then have the following method which sits in the server folder
Meteor.methods({
checkUser: function () {
alert('aaaa');
}
});
This works, but it's not a great solution. My method for handling this is to have all of my functions outside the Meteor.methods, and simply relay to the proper functions when necessary.
// Client
Meteor.call('foo');
And:
// Server
Meteor.methods({
foo: function() {
foo();
}
});
foo = function() {
foo = bar;
};
The advantage is that the foo fn can be called from anywhere on the server without a Meteor.call. Meanwhile, Meteor.methods only exposes what is absolutely necessary to the client.
[EDIT] There is some ambiguity as to which 'foo' you're talking about; obviously the server knows you mean the one outside the methods call. But if you're feeling confused, you can always rename one or the other. The advantage to this is that there is minimal refactoring involved.
Just to clarify for readers who don't notice that the OP's code actually contains the answer, you just do
Meteor.call('checkUser');
on the server. Per the meteor docs (https://docs.meteor.com/api/methods.html#Meteor-call), on the server, if you use Meteor.call() without a callback argument, the call runs synchronously and waits for the result. For example, if 'checkUser' was written to provide a userId value, you'd just do
let userId = Meteor.call('checkUser');
On the client, though, you have to provide a callback function as an argument to Meteor.call(), and the userId would be provided asynchronously to your callback function.

Get callback info back to original calling function

Let's say you have a Javascript function that calls a web service method. So that webservice completes and calls a callback function, which has the result.
How do I get that result back into the original function that called the web service method? Essentially, I'm trying to "synchronize" an asynchronous call.
Update:
This is what I'm trying to do. I'm validating based on the return value of a web service.
$.validator.addMethod("zzz",
function(value, element) {
// Call web service function using ASP.NET Ajax.
// Get callback value to determine validity.
return this.optional(element) || ***return t/f based on validity***;
},
function(value, element) { return msg; }
);
so I guess I could do this instead:
$.validator.addMethod("zzz",
function(value, element) {
$.ajax({
async: false
url: **** web service url ****
success: set t/f to validity var
});
return this.optional(element) || ***return t/f based on validity var***;
},
function(value, element) { return msg; }
);
Since you're using jQuery, you can use async:false in your ajax command, like this:
$.ajax({
//options..
async: false
});
//this code will run after the request returns
Note though, this blocks the UI (locks up the browser), it's better to continue the work that depends on the result in the success callback, like this:
$.ajax({
//options..
success: function(data) {
doSomethingWithResult(data);
}
});
Essentially, you can't, but you can break up that function into "before" and "after" parts, like so:
function foo(a) {
var b, c;
// == The "before" part:
b = /* ... */;
// == Call the service:
callWebService(a, b, callback);
// == The "after" part is contained within the callback:
function callback(d) {
// Do something with a, b, c, and d
}
}
But it's important to note that foo will return before callback is called. There's no way to prevent that with an asynchronous call, so if you're looking to have the function return something, you'll have to refactor (or use a synchronous call, but that's a very bad idea). The refactoring would involve whatever's calling foo to provide a callback and expect the result to be provided that way rather than as a return value.
Well what you're trying to accomplish is simulating a sleep command, so your script "waits" for your ajax request? But that doesn't really makes sense. That's why you have to callback in the first place, to continue with the flow once the request has returned a reply, since you cannot predict its response time.
Not to be trite, but you can't create synchronousness from asynchronousness, only the other way around. You need to design your code to allow for this, which generally means callbacks all the way through your call chain.
There is one idiosyncratic exception, which is that you can specify 'false' on the raw XMLHttpRequest's 'open' method's async parameter, which will cause the send method to block until it's done. But this is likely not compatible with some frameworks and is pretty inflexible. Most JS stuff is async.
You should not do that. Just carry on with your processing from the point of the callback.
You risk hanging the browser completely if the call does not return.
If you control the server side then you could write some code on the js side to aggregate calls and then write something on the server side to unpack and do multiple calls from each nested call in the aggregate. When the responses come back then aggregate those and send them back. This will save on performance since large calls are cheaper than many small calls.
We did that on a project I worked on and it worked very nicely. It also consolidates logic on the js side to not be spread all over due to all the async callbacks.
The only way I can think of is like
function b(){
var a = null;
$.get("abc.json", {
success: function(data){
a = data;
}
});
while(a == null){}
alert(a);
}
b();
But generally this isn't good and may cause the browser to complain a script is taking too long to finish.

Resources