Asynchronous loading of partial view in Ember - ajax

I've created an Ember helper to allow for loading a dynamically generated partial view from a URL on the server. It looks like this:
Ember.Handlebars.helper('serverPartial', function(url, options) {
var template;
$.ajax(url, {
async: false,
success: function(templateText){
template = Ember.Handlebars.compile(templateText);
}
});
template(this, options);
});
And it's called from a parent Handlebars template like this:
{{serverPartial templateUrl}}
As you can see, the ajax call to retrieve the template from the server is a synchronous call, because I couldn't find any other way to return the template contents as expected by the Ember framework. Unfortunately, this synchronous call holds up the rendering of the entire parent template.
Is there a way to return a promise for the template, or any other way to allow partial views to load asynchronously or independently?

Thanks for the tip, #Rajat. I did end up using Views to accomplish this. I created a view container that initially loads a default child template, then that child loads in the actual contents from the server after it is inserted. Here's the approach I took:
App.LoadingView = Ember.View.extend({
didInsertElement: function(){
var container = this._parentView;
$.get('http://server/serverTemplate', function(data){
container.pushObject(Ember.View.create({
template: Ember.Handlebars.compile(data)
}));
container.removeObject(container.get('loadingView'));
});
},
template: Ember.Handlebars.compile('client awesome')
});
// inherit from ContainerView and initialize with default content
App.SnippetView = Ember.ContainerView.extend({
childViews: ['loadingView'],
loadingView: App.LoadingView.create()
});
I'm sure there are better ways to express this, but does the job at a minimum.

First, what you are trying to do with Handlebars helpers is not what they are intended to do. Handlebars helpers are strictly markup formatters and should be used for simple HTML adjustments.
Second, in typical ember apps, what you are trying to do is never done. There is no 'on-demand' fetching of templates.
This is because all templates are downloaded in one sweep when the app loads and live on client. They are precompiled to JavaScript functions and stored as Strings in the Ember.Handlebars hash.
They are downloaded when the app loads and then get evaluated when the view is rendered. In the meantime, they live simply as Strings.
Now if you still want to do what you are doing, I would recommend trying to do that in Views.

Related

Single page application with Rails 4 and AngularJS

Ok, this idea might seem quite a bit crazy and it kindo' is (at least for me at my level).
I have a fairly standarad rails app (some content pages, a blog, a news block, some authentication). And I want to make it into a single page app.
What I want to accomplish is:
All the pages are fetched through AJAX like when using turbolinks, except that the AJAX returns only the view part (the yield part in the layout) withought the layout itself, which stays the same (less data in the responces, quicker render and load time).
The pages are mostly just static html with AngularJS markup so not much to process.
All the actual data is loaded separately through JSON and populated in the view.
Also the url and the page title get changed accordingly.
I've been thinking about this concept for quite a while and I just can't seem to come up with a solution. At this point I've got to some ideas on how this actualy might be done along with some problems I can't pass. Any ideas or solutions are greatly appreciated. Or might be I've just gone crazy and 3 small requests to load a page are worse then I big that needs all the rendering done on server side.
So, here's my idea and known problems.
When user first visits the app, the view template with angular markup is rendered regularly and the second request comes from the Angular Resource.
Then on ngClick on any link that adress is sent to ngInclude of the content wrapper.
How do I bind that onClick on any link and how can I exclude certain links from that bind (e.g. links to external authentication services)?
How do I tell the server not to render the layout if the request is comming from Angular? I though about adding a parameter to the request, but there might be a better idea.
When ngInclude gets the requested template, it fires the ngInit functions of the controllers (usually a single one) in that template and gets the data from the server as JSON (along with the proper page title).
Angular populates the template with the received data, sets the browser url to the url of the link and sets the page title to what it just got.
How do I change the page title and the page url? The title can be changed using jQuery, but is there a way through Angular itself?
Again, I keep thinking about some kind of animation to make this change more fancy.
Profit!
So. What do you guys think?
OK, in case enyone ever finds this idea worth thinking about.
The key can be solved as follows.
Server-side decision of whether to render the view or not.
Use a param in the ngInclude and set the layout: false in the controller if that param is present.
Have not found an easier way.
Client-side binding all links except those that have a particular class no-ajax
Here's a directive that does it.
App.directive('allClicks', function($parse) {
return {
restrict: 'A',
transclude: true,
replace: true,
link: function(scope, element, attrs) {
var $a = element.find('a').not($('a.no-ajax')),
fn = $parse(attrs['allLinks']);
$a.on('click', function(event) {
event.preventDefault();
scope.$apply(function() {
var $this = angular.element(event.target);
fn(scope, {
$event: event,
$href: $this.attr('href'),
$link: $this
});
});
});
}
};
})
And then use it on some wrapper div or body tag like <body ng-controller="WrapperCtrl" all-links="ajaxLink($href)"> and then in your content div do <div id="content" ng-include="current_page_template">
In your angular controller set the current_page template to the document.URL and implement that ajaxLink function.
$scope.ajaxLink = function(path) {
$scope.current_page_template = path+"?nolayout=true";
}
And then when you get your JSON with your data from the server don't forget to use history.pushState to set the url line and document.title = to setr the title.

What is the right away to update a panel using AJAX?

In MVC4 applications, I would like to update a panel using AJAX but using jQuery methods instead using AjaxExtensions from MVC.
But my problem is the updatePanelId.
I've seen several people use this to update it when has success:
success: function (response) {
var $target = $("#target");
var $newHtml = response;
$target.replaceWith($newHtml);
}
But when I do this, it forces me to use in every partial view that includes the id="target" at the root level of my razor view, and I guess that's not a good practice; I said this because I've realized when I use AjaxExtensions it doesn't happens, replace the update and it does not remove the panelId. But using jQuery it does.
Any idea to port the AjaxExtensions feature to jQuery?
You can use just:
$("#target").html(response); // it will just update content of the $("#target") container
Use jQuery's .load function. This will load the contents of the URL you specify into the target element. You can optionally specify a selector after the URL in load to only grab part of the target page.
$(function() {
$("#target").load("/MyURL");
});
JavaScript same origin policy applies to this.

How to dynamically load a partial view without using a controller?

There is a big chunk of html + js code. The goal is to load and show it only on a specific button click, so, there is no need in pre-loading it every time.
Normally, we can include a partial view dynamically by using jquery/ajax, but as far as I know it requires a controller. In this scenario, there is no need for a controller, as the data is static.
So, the question is: is it possible on a specific event (like button click) to include a partial view without going through a controller?
What we tried was giving the ajax request an address of partial view. However it didn't help.
<div class="reportWindow"></div>
<script type="text/javascript">
$("#feedback").click(function () {
$.ajax({
url: "#Url.Content("_Feedback")",
type: 'GET',
cache: false,
success: function (result) {
$("#reportWindow").html(result);
}
});
});
</script>
a Partial view that have cshtml extension is still a Razor view, even if the content inside of it is static, the server still needs to read it and convert its content to html (even if that means copy it exactly the way it is and send it back)
What you need is an html file, which in turn you can load dynamically with javascript or jQuery:
$("#feedback").click(function () {
$("#reportWindow").load('feedback.html');
});
Note that this will still ask the server for feedback.html, typically this won't match to any route then IIS will resolve the request by looking for a static pages called feedback.html!

Use Ajax in Prototype.js to load PART of an external page into a div

I'm looking for a way to load a part of an external page (possibly selected by an id in the external page) into a div. Something similar to Ajax.Updater, but with the option of specifying an id to look for in the external page.
Does anything like this exist in prototype. I've been googling for examples without luck. If I can't find it soon I'll have to do some "gymnastics" with Ajax.Request and some function tied to onSuccess.
You could do something like this, though it is by no means an elegant solution.
new Ajax.Updater("container", 'www.babes.com', {
onSuccess: function() {
$("container").update( $('idOfSomeLoadedElement') );
}
});
I don't think there is an actual elegant way of doing this purely in js. Ideally, you'd make your AJAX request only for what you need. You might be able to do some server-side stuff to lop out what you don't need (basically, offload the onsuccess functionality above to the server).
Instead of AJAX you might get by with an iframe.
// dollar function calls Element.extend
var iframe = $(document.createElement('iframe'));
// control how and what it loads
iframe.addEventListener('onLoad', function() {
$('container').update(iframe.contentDocument.select('#someID').first());
});
iframe.setAttribute('src', 'http://URL');
// add it as invisible, content isn't loaded until then
iframe.setAttribute('hidden', true);
document.body.appendChild(iframe);
From a minimal test it's clear that you'll have to be as concious about cross-origin policies as any AJAX method, that is, it's a PITA.

jQuery: Can I automatically apply a plug-in to a dynamically added element?

I'm in the process of converting my web app to a fully AJAX architecture.
I have my master page that is initially loaded and a div container that is loaded with dynamic content.
I created a few jQuery plugins that I apply to certain elements in order to extend their functionality. I'd normally call the functions as follows during each page load:
$(document).ready(function () {
// Enable fancy AJAX search
$(".entity-search-table").EntitySearch();
});
This would find the appropriate div(s) and call the plugin to enable the necessary functionality.
In an AJAX environment I can't just apply the plugin during the page load since elements will be added and removed dynamically.
I'd like to do something like this:
$(document).ready(function () {
// Enable fancy AJAX search
$(".entity-search-table").live("load", function () {
$(this).EntitySearch();
});
});
Question: Is there any way that I can trigger an event when a <div> or other element that matches a selector is added to the DOM?
It seems incredibly wasteful to activate the plug-in every time an AJAX request completes. The plug-in only needs to be applied to the element once when it is first added to the DOM.
Thanks for any help!
Yes - take a look at liveQuery. Example:
$('.entity-search-table').livequery(function(){
$(this).EntitySearch();
});
It seems incredibly wasteful to activate the plug-in every time an AJAX request completes. The plug-in only needs to be applied to the element once when it is first added to the DOM.
You can get the best of both worlds here, for example:
$("#something").load("url", function() {
$(".entity-search-table", this).EntitySearch();
});
This way it's only applying the plugin to the .entity-search-table elements you just loaded, since we specified a context to $(selector, context) to limit it.
The DOM 2 MutationEvent is what you really want, but unfortunately it isn't supported by IE. You'll need to either use live()/ delegate() binding in the plug-in, or (as I did when I had to work around this) use callbacks from your AJAX loaders indicating the scope of what has changed.
Use the live binding in your plugin code directly
jQuery.fn.EntitySearch = function() {
this.live(..., function(){ your plugin code });
return this;
}

Resources