Conflict between EJS and Marionette - marionette

For my single page application, the page is delivered by node-express-ejs on the server side to Maironette-Backbone based client on the browser. Unfortunately, both EJS and Marionette use the syntax "<%= var %>" for plugging in computed values. My page has some values that need to be plugged in by the server and some that will be computed by the client. So I need something like the following snippet to work.
<html> .....
Server Computed = $ <%=serverComputed%>
Client Computed = $ <%=clientComputed%>
....</html>
And my routes.js on the server would have something like,
res.render('test', { serverComputed: '5000' });
But, EJS throws
ReferenceError: .../test.ejs:17
.....
clientComputed is not defined
The following does not help either
res.render('test', { serverComputed: '5000', clientComputed: '<%= clientComputed %>' });
since it as expected, ends up as
$lt:%= clientComputed %>
Is there a way (a market, a processor directive etc) to tell EJS processor to not process a part of the file?
Is there any other way to work around this conflict?
Thanks,
Hemant

Found the solution.
EJS does allow defining your own tags by setting them in the options. So far as I can tell, this is not documented. I only found it by going through the source code. Anyway, the following option setting would avoid conflict with Marionette open-close tags.
On server side, routes.js can specify options like this.
res.render('test', { open: '{{', close: '}}', serverComputed: '5000' });
In the ejs file, now you can have different markups for server-computed and client-computed variables, like so.
Server Computed = $ {{=serverComputed}}
Client Computed = $ <%=clientComputed%>
EJS processor will not process the "<%=clientComputed%>" part.
Hemant

Related

Is it possible to write these Protractor expectations using no continuations?

Using protractor and jasmine(wd) we want to check that a table on the web page contains expected values. We get fetch the table from the page using a CSS selector:
var table = element(by.css('table#forderungenTable')).all(by.tagName('tr'));
We then set our expectations:
table.then(function(forderungen){
...
forderungen[2].all(by.tagName('td')).then(function(columns){
expect(columns[1].getText()).toEqual('1');
expect(columns[5].getText()).toEqual('CHF 277.00');
});
});
Is it possible to change this code so that we don't have to pass functions to then, in the same way that using jasminewd means that we don't have to do this? See this page, which states:
Protractor uses jasminewd, which wraps around jasmine's expect so that you can write:
expect(el.getText()).toBe('Hello, World!')
Instead of:
el.getText().then(function(text) {
expect(text).toBe('Hello, World!');
});
I know that I could write my own functions in a way similar to which jasminewd does it, but I want know if there is a better way to construct such expectations using constructs already available in protractor or jasminewd.
You can actually call getText() on an ElementArrayFinder:
var texts = element(by.css('table#forderungenTable')).all(by.tagName('tr')).get(2).all(by.tagName('td'));
expect(texts).toEqual(["text1", "text2", "text3"]);

Client-side translation does not work in CKAN

I am having issues with translating client-side text in CKAN. My site is in french, so here is the problem:
- if I click on the green button "S'abonner" from the left, it should change the button to a red one and show the text "Se désabonner", but instead, it shows me "Unfollow", like in the photos:
And if I click it again it shows me "Follow" (instead of "S'abonner"). So this is a problem on the client side. Normally, when you generate text on the server side you write in the templates _('msgid present in pofile'), but, on the client side, it uses the result from the AJAX call to "/api/i18n/{language}".
I did some digging and it seems that the client-side translation uses the result from the AJAX call to "/api/i18n/fr", but all I get is a json object:
{
"": {
"domain": "ckan",
"lang": "fr",
"plural-forms": "nplurals=2; plural=(n > 1)"
}
}
But, if I look at the ckan demo website, (http://demo.ckan.org/api/i18n/fr), I get a long JSON file, that contains all the translations.
I managed to discover from where this Ajax call is done, apparently it comes from : "src/ckan/ckan/public/base/javascript/client.js", at the function:
getLocaleData: function (locale, success, error) {
var url = this.url('/api/i18n/' + (locale || ''));
return jQuery.getJSON(url).then(success, error);
}
If I replace
var url = this.url('/api/i18n/' + (locale || ''));
with
var url = this.url('http://demo.ckan.org/api/i18n/fr')
the problem is solved, because I get the translations in the json object.
My question is how I can get the right data to be generated, is there a parameter to put in production.ini? Unfortunately, the documentation in CKAN is really poor so I have no leads on this. Does anybody have a clue?
Thanks!
I suspect that you might be running an unreleased branch of CKAN (eg master), so the Javascript translations have not been built.
This is done with the following command:
paster front-end-build -c config_file.ini
Just run this command and the translations should come up fine.
On the main repository this is only done just before releases to avoid complicating the git history.
Check for instance this site running the latest master branch, but on which the front end is built every night:
http://master.ckan.org/fr/dataset/world-countries

breeze 1.4.8 & angular ajax adapter: how to customize ajax settings?

My code:
breeze.config.initializeAdapterInstance("ajax", "angular", true);
...
var ajaxAdapter = breeze.config.getAdapterInstance('ajax');
ajaxAdapter.defaultSettings = {
method: 'POST',
data: {
CompanyName: 'Hilo Hattie',
ContactName: 'Donald',
City: 'Duck',
Country: 'USA',
Phone: '808-234-5678'
}
};
in line 14813 of breeze.debug.js:
ngConfig = core.extend(compositeConfig, ngConfig);
compositeConfig.method has a value of 'POST' until it is overwritten, because ngConfig.method has a value of 'GET'.
I imagine this question is relevant for any ajax setting, but of course I'm mainly struggling with how to post with the angular ajax adapter, so maybe there's an entirely better way to do that? this approach feels dirty anyway, but breeze.ajaxPost.js only works with the jQuery ajax adapter, right?
6 Oct 2014 update
We recently had reason to revisit ajaxpost and have updated both the code and the documentation.
The original recommendation works. We're merely clarifying and updating the happy path.
A few points:
The ajaxpost plug-in works for both jQuery and Angular ajax adapters.
Those adapters long ago became aware of adapter.defaultSettings.headers and have blended them into your Breeze ajaxpost http calls (take heed, PW Kad).
You must call breeze.ajaxPost() explicitly after replacing the default ajax adapter as you do when you use the 'breeze.angular' service.
You can wrap a particular ajax adapter explicitly: breeze.ajaxPost(myAjaxAdapter); We just don't do that ourselves because, in our experience, it is sufficient to omit the params (breeze.ajaxPost()) and let the method find-and-wrap whatever is the active ajax adapter of the moment.
The documentation explains the Angular use case which I repeat here to spare you some time:
// app module definition
var app = angular.module('app', ['breeze.angular']); // add other dependencies
// this data service abstraction definition function injects the 'breeze.angular' service
// which configures breeze for angular use including choosing $http as the ajax adapter
app.factory('datacontext', ['breeze', function (breeze) { // probably inject other stuff too
breeze.ajaxPost(); // wraps the now-current $http adapter
//... your service logic
}]);
Original reply
AHA! Thanks for the plunker (and the clever use of the Todo-Angular sample's reset method!).
In brief, the actual problem is that breeze.ajaxpost.js extends the jQuery ajax adapter, not the angular ajax adapter. There is a timing problem.
The root cause is that you can't tell breeze.ajaxpost.js which adapter to wrap. It always wraps the default adapter at the time that it runs. As you've got your script loading now, the jQuery adapter is the current one when breeze.ajaxpost.js runs.
The workaround is to set the default adapter to the angular adapter before breeze.ajaxpost.js runs.
One way to do that is to load the scripts as follows ... adding an inline script to set the default ajax adapter to the "angular" version.
<script src="breeze.debug.js"></script>
<script>
<!-- Establish that we will use the angular ajax adapter BEFORE breeze.ajaxpost.js wraps it! -->
breeze.config.initializeAdapterInstance("ajax", "angular", true);
</script>
<script src="breeze.ajaxpost.js"></script>
Clearly this is a hack. We'll look into how we can change breeze.ajaxpost.js so you can wrap any ajax adapter at the time of your convenience.
Thanks for finding this issue, and thanks Ward for bringing it to my attention. I've updated the breeze.ajaxpost.js code to use .data as you described, and added a function you can call after adapter initialization. So now you can do:
var ajaxAdapter = breeze.config.initializeAdapterInstance("ajax", "angular");
ajaxAdapter.setHttp($http);
breeze.ajaxpost.configAjaxAdapter(ajaxAdapter); // now we can use POST
So, it's slightly less hacky.
This answer is applicable to the following plugins and version:
Breeze.Angular v.0.8.7
Breeze.AjaxPost v.1.0.6
I just saw a new plugin called Breeze.Angular.js that can work in conjunction with Breeze.AjaxPost.js
You have to modify the Breeze.AjaxPost.js in order for it to work. Breeze.Angular.js initializes your adapter when your page loads, and Breeze.AjaxPost.js will take in the adapter initialized from Breeze.Angular.js
You can learn more about the Breeze.Angular server here: breeze-angular-service
You can learn more about Breeze.Ajaxpost here: breeze-ajaxpost
Now to set this up wasn't exactly apparent, because I took both files exactly as they were in git.
1.) Reference the files in this order:
<script src="Scripts/q.min.js"></script>
<script src="Scripts/breeze.angular.js"></script>
<script src="Scripts/breeze.ajaxpost.js"></script>
2.) Go into your breeze.ajaxpost.js
Remove this line (or comment this line):
breeze.ajaxpost(); // run it immediately on whatever is the current ajax adapter
The reason why is because, this will cause your ajaxpost method to run before your breeze.angular service. When you go into the ajaxpost method, the ajaxAdapter parameter that is supposed to be passed in will be null. Removing this line appends the functionality for later in your code, so you can call it from the breeze.angular service.
3.) In breeze.angular.js go to your useNgHttp method. It should look like this:
// configure breeze to use Angular's $http ajax adapter
var ajaxAdapter = breeze.config.initializeAdapterInstance("ajax", "angular");
ajaxAdapter.setHttp($http);
breeze.ajaxpost(ajaxAdapter); // now we can use POST
When you run your program, both plugins should be able to set up the environment for you without having to include it in every javascript that makes your calls to the webapi.
Thanks guys for the plugins.

how to inject into sintra/haml?

I coded small web app that runs ant (batch file). The processing time of the batch file could take up to minutes.
index.haml list all available ant files and run.haml runs them. The flow how it works now is that when I click a link from index.haml the ant script is run and after it finishes the whole run.haml page is sent to the browser. So after clicking link from index.haml I still can see index.haml and nothing from run.haml
After I click a link from index.haml I want to
show what script is going to be run and then
run the ant script and then
display the results of it.
I was recommended in my other question to use
an Ajax call or
a separate worker thread. Like BackgrounDRb, run_later or delayed_job
I didn't understand how a separate worker thread could help me. Would the delayed job's results that are captured by ruby's call be sent to the browser once the job finishes?
I also didn't get how I can use Ajax in sinatra.
Could somebody point me out what a solution for that could be like? Please note that I know bit of ruby, learned bit of sinatra and haml yesterday. No nothing about Ajax :-) I learn by examples ... and happy to learn anything.
index.haml gives me html like
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Available test suits</title>
</head>
<body>
<h1>Available test suits</h1>
<br/><br/>
<a href='run?run=login_build'>login_build</a>
<br />
<a href='run?run=login_cycle_build'>login_cycle_build</a>
<br />
<a href='run?run=login_cycle_academicyear_build'>login_cycle_academicyear_build</a>
</body>
</html>
run.haml looks like
!!!
%html
%head
%title Running
%body
= "Starting test suite: #{params['run']}"
- output = %x[cd C:\\Program Files\\TestPro\\TestPro Automation Framework410 && ant -lib lib -f "C:\\Program Files\\TestPro\\TestPro Automation Framework410\\Output Files\\builds\\#{params['run']}.xml"]
-#The result is
%br
= output.split("\n")[-2,2].join("<BR>")
= "<br/>"*2
%a(href="/")back to suits list
If you want to use Ajax must, choose a javascript library Dojo, Prototype, Mootools, jQuery... Each of them has specific tools for handling ajax requests.
In javascript you make an xhr(Ajax request), which may be asynchronous and still Sinatra serves you the wanted content, you can display in webpage what you want.
var xhrLoadClientMenu = function(param){
var result_node = dojo.byId('div_menu')
var xhrArgs = {
// the Sinatra get "/run" ... end handler
url: '/run',
load: function(data){
// When response is rendered come's here
result_node.innerHTML = data;
},
error: function(error){
msg = "<p>Ooops some error ...<br><br>" + error + "</p>";
}
}
// Here you can put what you want to display durring loading
result_node.innerHtml = "Loading..."
var defered = dojo.xhrGet(xhrArgs)
}
a xhrGet example using dojo. See inline comments.
xhrLoadClientMenu(param) is triggered by an event;
a HTML element with id #div_menu from page is stored in result_node object;
xhrArgs object sets the Ajax request properties like:
url: the Sinatra handler which renders the content;
load: function gets the response from sinatra and replaces result_node content when load is completed
error if something goes wrong this message will be shown in result_node;
meanwhile you may set the result_node content to anything while the real content is loading;
last line executes the xhr request.
All of this is happening without a page reload.
Delayed_job results are indeed sent "back to the browser." Please take another look at this live example of using Sinatra and DJ to process a task asynchronously and push the result to the browser once the task is complete.
The call runs asynchronously, and the rest of the page loads, and when the calls are complete the data is posted back to the page. Now, since it's on the cloud, it may seem VERY delayed; you'll want to download the source and run it on your own machine to see what kind of speed DJ offers on a local network. The results are indeed processed and sent back; scroll down the page and you'll see the results of earlier processing -- you can even enter your own text block to add to the processing queue.
So perhaps you could utilize delayed_job to create a queue for asynchronous processing of your test suites -- 'out of sync' from the rest of your page, but also with the option of passing data back 'incrementally' as well as once the task completes.
Now, Ajax offers another way to provide this same functionality. I absolutely and unequivocally recommend getting one of the Javascript frameworks and learning how to do this sort of asynchronous processing that way as well. However, based on your earlier question, I would still suggest working through the source code of the example above using Sinatra and delayed_job, as it's already extremely close to what you are trying to do, not to mention that it keeps you entirely in Ruby.

IE7 not digesting JSON: "parse error"

While trying to GET a JSON, my callback function is NOT firing.
$.ajax({
type:"GET",
dataType:'json',
url: myLocalURL,
data: myData,
success: function(returned_data) {
alert('success');
}
});
The strangest part of this is that:
my JSON(s) validates on JSONlint
this ONLY fails on IE7...it works in Safari, Chrome, and all versions of Firefox, (and even in IE8). If I use 'error', then it reports "parseError"...even though it validates!
Is there anything that I'm missing? Does IE7 not process certain characters, data structures (my data doesn't have anything non-alphanumeric, but it DOES have nested JSONs)? I have used tons of other AJAX calls that all work (even in IE7), but with the exception of THIS call.
An example data return here is: (this is a structurally-complete example, meaning it is only missing a few second-tier fields, but follows this exact hierarchy)
{"question":{
"question_id":"19",
"question_text":"testing",
"other_crap":"none"
},
"timestamp":{
"response":"answer",
"response_text":"the text here"
}
}
I am completely at a loss. Hopefully someone has some insight into what's going on...thank you!
EDIT
Here's a copy of the SIMPLEST case of dummy data that I'm using...it still doesn't work in IE7.
{
"question":{
"question_id":"20",
"question_text":"testing :",
"adverse_party":"none",
"juris":"California",
"recipients":"Carl Chan"
}
}
I am starting to doubt that it is a JSON issue...but I have NO idea what else it could be. Here are some other resources that I've found that could be the cause, but they don't seem to work either:
http://firelitdesign.blogspot.com/2009/07/jquerys-getjson.html (Django uses Unicode by default, so I don't think this is causing it)
Anybody have any other ideas?
The example data you present looks all right but my strong suspicion still is that there is an unclosed comma somewhere like this:
"timestamp":{
"response":"answer",
"response_text":"the text here"
}, <------------
}
IE is the only browser that (correctly) trips over this.
If this is not it, can you show a full data sample (or confirm that the example you show is indeed a full sample)?
Did you already exclude the possibility of a caching issue?
e.g. you tested with IE7 when myLocalURL returned invalid json. IE7 still caches that response and thus it doesn't work. Try adding something like this (e.g. if php) to myLocalURL or make myLocalURL look like myLocalURL?random=123 just for testing to make sure it isn't a caching thing
header("Cache-Control: no-cache, must-revalidate");
header("Expires: 0");
Are you returning a correct content-typ header? e.g.
header("Content-Type: application/json");
I've just encountered exactly the same issue. It turns out that IE7 fails to parse JSON responses that have leading \r\n line feeds in the response body. Your fix of removing {% load customfilter %} works because you removed the new line that was being included after this tag.
An alternative fix would be to just remove the new line to get
{% load customfilter %}{ "question":{ "question_id":"{{question.id}}",
"question_text":"{{question.question_text|customfilterhere}}"
}
}

Resources