Asynchronous jQuery templates - javascript

I would like to be able to create jQuery templates that gets filled in by data fetched by some asynch queries, for this I need to refer back to elements I render within a template. I have come up with a solution using a generated class-name, which I use after rendering the template to insert the data, but this is a kludge. Is there a way to get a reference to the rendered element from within the template code? Here is the code I would like to get working:
<script>
function renderData(prduct_id, element){
getProductData(product_id, function(data){
$(e).empty();
$("#product-info-tmpl").tmpl({"data":data}).appendTo($(element));
});
}
</script>
<script id="product-info-loading-tmpl" type="text/x-jquery-tmpl">
loading
//Here is where I'd need a reference to the rendered element
${renderData(product_id, this_element_when_rendered)}
</script>

Does this help, so in your case something with $item, maybe $item.parent or something...
Copied somewhere from http://api.jquery.com/template-tag-equal/
The $item and $data Template Variables
The following variables are exposed to
expression evaluation within
templates:
$: The jQuery object. $item: The
current template item - which allows
access to $item.data, $item.parent,
etc. as well as any user-defined
values or methods passed in with the
options map. $data: The current data
item (equivalent to $item.data). Note:
A template tag with content such as
{{each}}...{{/each}} may expose
additional variables to template
evaluation within the content. In the
case of {{each}}, for example, the
additional template variables $value
and $index are provided within the
content of the {{each}} tag.

Related

RiotJS - How to pass events between subtags using Observable pattern?

Im not really sure if Im understanding correctly the way observables work and how to get references from mounted tags. I have a component. Within this component we have a component and a component. The purpose is to avoid coupling between components. Because of that, I would like that my search component triggers an event when a search is done(a button is clicked). This event should be caught by the component which will filter the collection data based on the search.
The index.html file load the tag by using:
index.html
riot.mount(".content", "page", null);
The page is defined as follow:
page.js
<page>
<!-- Search tag controls -->
<search id="searchTag"></search>
<!-- Collection data to display -->
<collection id="collectionTag"></collection>
</page>
The component script is briefly defined like:
search.js
var self = this;
riot.observable(self);
<!-- This function is called when the user click on the button. -->
self.filtering = function()
{
<!-- We get data from inputs -->
var info = Getting data from inputs;
<!-- Trigger the event hoping that someone will observe it -->
self.trigger("filterEvent", info);
}
How can I make the component observe for that event?
To me it seems that I should be able to get references from search tag and collection tag in the page.js. By doing so I could connect the events like follow:
searchComponent = riot.mount('search');
collectionComponent = riot.mount('collection');
searchComponent.on('filterEvent', function()
{
<!-- Trigger function to filter collection data -->
collectionComponent.trigger('filterData');
});
Right now I cannot make it work like that.
At the point of execution, searchComponent and collectionComponent are not defined.
I tried also getting references of these component by using this.searchTag and this.collectionTag instead of mounting them but at the time the code is executed, the components have not been mounted and so I dont get a reference to them.
Any ideas to make it work?
Inspired by the answer given by #gius, this is now my preferred method for sending events in RiotJS from one tag to another.. and it is great to work with!
The difference from #gius approach being that, if you use a lot of nested tags, passing a shared Observable to each tag falls short, because you would need to pass it again and again to each child tag (or call up from the child tags with messy this.parent calls).
Defining a simple Mixin, like this (below), that simply defines an Observable, means that you can now share that in any tag you want.
var SharedMixin = {
observable: riot.observable()
};
Add this line to your tags..
this.mixin(SharedMixin);
And now, any tag that contains the above line can fire events like..
this.observable.trigger('event_of_mine');
..or receive events like this..
this.observable.on('event_of_mine',doSomeStuff());
See my working jsfiddle here http://jsfiddle.net/3b32yqb1/5/ .
Try to pass a shared observable to both tags.
var sharedObservable = riot.observable();
riot.mount('search', {observable: sharedObservable}); // the second argument will be used as opts
riot.mount('collection', {observable: sharedObservable});
And then in the tags, just use it:
this.opts.observable.trigger('myEvent');
this.opts.observable.on('myEvent', function() { ... });
EDIT:
Or even better, since your search and collection tags are child tags of another riot tag (page) (and thus you also don't need to mount them manually), you can use the parent as the shared observable. So just trigger or handle events in your child tags like this:
this.parent.trigger('myEvent');
this.parent.on('myEvent', function() { ... });
Firstly I do not understand your file structure !
In your place I would change filenames :
page.js --> page.tag
search.js --> search.tag
And i dont see your search tag in search.js code.
So I dont see your Collection tag file ...
Are you sure that this one use this code ?
riot.observable({self|this});
Because it's him who will receive an Event.
For me when I use Riot.js(2.2.2) in my browser, if I use
searchComponent = riot.mount('search');
searchComponent will be undefined
But with this code you can save your monted tag reference :
var searchComponent ={};
riot.compile(function() {
searchComponent = riot.mount('search')[0];
});
Another option is to use global observables, which is probably not always best practice. We use Riot's built in conditionals to mount tags when certain conditions are met rather than directly mounting them via JS. This means tags are independent of each other.
For example, a single observable could be used to manage all communication. This isn't a useful example on its own, it's just to demonstrate a technique.
For example, in a plain JS file such as main.js:
var myApp = riot.observable();
One tag file may trigger an update.
var self = this;
message = self.message;
myApp.trigger('NewMessage', message);
Any number of other tag files can listen for an update:
myApp.on('NewMessage', function(message) {
// Do something with the new message "message"
console.log('Message received: ' + message);
});
Maybe overkill but simple. let riot self observable
riot.observable(riot);
So you can use
riot.on('someEvent', () => {
// doing something
});
in a tag, and
riot.trigger('someEvent');
in another.
It's not good to use global variable, but use an already exists one maybe acceptable.

AngularJS Filter Data Binding

I have a custom filter that I use to return an html string using $sce.trustAsHtml. In the template/view I use ng-bind-html directive and pass the filter to as follows:
<div ng-bind-html="userAgent | geoCode:business"></div>
Inside my filter I have an inner function that takes an input, business model in my case, which takes properties from the business model, a mongoosejs model, and generates a formatted string which is used to generate and html a tag:
'<a href="some_url_i_create" ...>'+ myFormattingInnerFunction(business) +'</a>';
What is strange is, if I use this function several of the fields are returned as undefined/blank. However, if I directly access the variables a follows:
'<a href="some_url_i_create" ...>'+ business.prop1 + business.prop2+ ... +'</a>';
Then all of the properties are found and output. Any ideas?
P.S. The model is a retrieved via an AJAX request, which in turn use mongoosejs to retrieve the data, inside of the angular controller for this section.
When you are calling the function you are accessing the business object on the first rendering event when all the scripts are loaded and the first DOM manipulation occurs.
But When you accessing the business object directly angular puts the values into the view on the first rendering event and on all of the digest events and of course on other rendering events.
You might want to consider using a $scope. attr in the template to render and call the formated string value method in the callback as well.
In the JS:
.success(function(data){
business=JSON.parse(data);//Iguess
$scope.formatedtext= myFormattingInnerFunction(business);
$scope.$apply();// If you use an angular libs callback this probably not needed.
})
In the template:
'<a href="some_url_i_create" ...>'+ formatedtext +'</a>';

How to trigger function after render template

I am using marionette in my application. I am showing ItemView through regions like in the following.
var productInfoViewObj=new productInfoView.ProductInfoView({model:tagInformationModel.tagInformationModelObj});
exports.MyApp.bodyContainer.show(productInfoViewObj);
This is the code, I written inside view.
exports.ProductInfoView=Backbone.Marionette.ItemView.extend({
domInfo:{
mainTemplateId:"tagProductListTpl",
tableTemplateId:"taginfoViewTpl",
tableContentDiv:"taginfoViewDiv",
//tad Info
tagInfoTabId:"tagInfoBtn",
productInfoTabId:"productInfoBtn"
},
template:function(){
return commonFunctions.templateCompilation("tagProductListTpl","");
},
onRender:function(){
console.log(document.getElementById("productInfoBtn"));
}
});
I am passing templateId and data as arguments to commonFunctions.templateCompilation. It will compile and return compiled string. That compiled result passing to template.
As per my assumption, after completion of template, onRender function will trigger. What I mean before onRender, dom will available whatever we are templating using template.
But I am getting null inside onRender function.
I want a callback, it should trigger after template available in dom. so I can access elements whatever I templated using template.
I can do one thing, whatever I written inside onRender, I can setup time like in the following way.
onRender:function(){
setTimeout(function(){console.log(document.getElementById("productInfoBtn"));},1000);
}
If I set time, working fine but it's not correct way to implement.
can anyone help me.
Thanks.
It's resolved, I have to use onShow instead of onRender function. Now it's working fine.

populating Moustache.js template

I am currently working with a QuickBase database trying to make a custom report using moustache.js, When I was looking into moustach.js I noticed you needed to have your data in JSON format so I used (http://pastie.org/9364908#16) to get my data into JSON which then comes out like (http://pastie.org/9364674#18)
My question is when I make the template for this, does it need to be all contained in the page that the call to get the json data is? I have looked at examples of mustache templates, but all I can find is it being used with data supplied right then made in JSON format, but my data I need is being supplied when I make the call so how would I go about setting that up.?
When working with mustache js templates (or js templates in general), you do not need to have the template be in the same location as the data being returned from the ajax call. However, you will need to have the data stored in a var that is within scope of the render function of the template. I am assuming you wish to ajax in a set of data, parse it to JSON, then at some point render that JSON data to the DOM. So to answer your questions:
1) Does the template need to be contained in the page that the JSON data is?
Not necessarily, but you will need to be able to access both parts from one locations (ie, you would ajax in both the template and the data, then put it together. Alternatively, you could store the template in the DOM as a script [recommended way unless you are using AMD], and then render the template when your ajax call is complete).
The main thing to note is you will need to have a reference to both your template and your data in the same scope.
2) Setting up the template / data ?
This can be done by using the ajax success callback: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
So on success, you can have the function call render on your template, or just set a locally scoped variable with the data and handle the template rendering after.
3) render the template normally
var template = $('#template').html(), //script tage with id="template" from your dom
data = jsonData, //saved somewhere in a local scoped var .. maybe from an ajax call
rendered = Mustache.render(template, data),
target = $("#target"); //where the template renders to
$('#target').html(rendered);
4) render the template via $.ajax
$.ajax("/api/data", function(data){
var jsonData = $.parseJSON(data),
template = $('#template').html(), //script tage with id="template" from your dom
rendered = Mustache.render(template, jsonData),
target = $("#target"); //where the template renders to
});

Play 2.x: How to use two jsRoutes files in one scala template

I'm using jsRoutes in my Play 2.1.x app. Part of my routes file looks the following way:
GET /assets/template/js/routes/admin.js controllers.Admin.jsRoutes
GET /assets/template/js/routes/salonManagement.js controllers.SalonManagement.jsRoutes
And I would like to use both references in my scala template (that is by design, one controller contains necessary api functions, the other one necessary form submission urls).
So in my scala template I have the following part:
<script type="text/javascript" src="#routes.Admin.jsRoutes()"></script>
<script type="text/javascript" src="#routes.SalonManagement.jsRoutes()"></script>
Unfortunately, each generated javascript file starts with var jsRoutes = {};. Therefore, #routes.SalonManagement.jsRoutes() overrides properties of #routes.Admin.jsRoutes() and I can use only the last jsRoutes object.
Now, I know only one workaround. After each jsRoutes declaration I can insert a script that copies old jsRoutes object to a temporary object and then extends new jsRoutes with itself. But that doesn't look like the right way to go.
Isn't there any better way?
There's nothing special about the "jsRoutes" name. You can keep the same method name for consistency among the various controllers, but just pass a different name to the Routes.javascriptRouter method.
Put this in your template. I put it in a main template that wraps the other pages.
<script src="#controllers.routes.Application.jsRoutes()" type="text/javascript"></script>
and put this in your routes file
#jsroutes for ajax calls
GET /assets/js/routes controllers.Application.jsRoutes()
And then in your Application controller, refer to whatever method in whatever controller you want by implementing this method
public Result jsRoutes()
{
response().setContentType("text/javascript");
return ok(Routes.javascriptRouter("jsRoutes",
routes.javascript.Signup.forgotPassword(),
routes.javascript.Customers.findByName(),
routes.javascript.Customers.findByNumber()
));
}
These correspond with routes like
GET /customerfind/:name controllers.Customers.findByName(name: String)
Note there is no need to include parameters for the calls configured in the jsroutes method. Keeping all of this in one place, the Application controller, seems reasonable as long as it refers to methods implemented in their appropriate controller. Like in this example, a find on customer is in the Customers controller. Also, is kindof nice just having to check the one controller (Application) to see all the methods available through javascript routes for ajax calls.

Categories