I am building a SPA app with the following structure:
<body>
<!-- Main Container for our application -->
<div id="main">
</div>
<!-- End Main Container -->
<!-- Vendor Libraries -->
<script src="js/vendor/jquery/jquery-1.11.0.min.js"></script>
<script src="js/vendor/knockout/knockout-3.1.0.js"></script>
<script src="js/vendor/sammy/sammy-latest.min.js"></script>
<!-- Models -->
<script src="js/models/model1.js"></script>
<!-- ViewModels -->
<script src="js/viewmodels/viewModel1.js"></script>
<script src="js/viewmodels/viewModel2.js"></script>
<!-- App scripts -->
<script src="js/routes.js"></script>
<script src="js/app.js"></script>
</body>
The html file has a div which will hold the html for each respective page handled by Sammy.js wit the following code:
Sammy('#main', function() {
this.get('#/', function(context) {
context.$element().load('views/main1.html', function() {
ko.applyBindings(new ViewModel1(), $("#home")[0]);
});
});
this.get('#/text', function(context) {
context.$element().load('views/text.html', function() {
ko.applyBindings(new ViewModel2(), $("#home")[0]);
});
});
this.get('', function(context) {
this.redirect('#/');
});
}).run();
Each time I am loading the markup found in each html file and then apply my viewmodel.
My questions are:
Can you suggest any other possible way to load the markup apart from using jquery load().
Are my old bindings being disposed each time a new route is being called?
1: This question is very 'open'. There are tons of way to do this that aren't jquery.load. But the real question is: do you NEED another way? Do you need some form of control that $.load isn't giving you?
If you do, consider switching to jquery.get or jquery.ajax, and handle the request yourself. At the bottom of this post is an example.
2: No, because you keep applying the bindings to the same element. What you instead want to do is apply bindings to the first element WITHIN the container with id 'home'. Then, when you switch views, you want to do ko.removeNode on the view that you're removing. Below is a code example that illustrates how you can gain some more control over the process and clean up your bindings behind you.
function loadView(url, viewModel) {
$.get(url, function (response) {
var $container = $('#home'),
$view = $container.find('.view'),
$newView = $('<div>').addClass('view').html(response);
if ($view.length) {
ko.removeNode($view[0]); // Clean up previous view
}
$container.append($newView);
ko.applyBindings(viewModel, $newView[0]);
});
}
this.get('#/', function(context) {
loadView('views/main1.html', new ViewModel1());
});
this.get('#/', function(context) {
loadView('views/text.html', new ViewModel2());
});
What I did in this example is using jquery.get so we gain control over the whole process of loading and displaying the HTML. I then refactored part of the logic out into a separate function that is generic enough to use on every view you have.
When a view is retrieved I store it in an element with class 'view'. The bindings are applied to this element and it is stored in your container element. When switching views I clean up and remove the old view before the new view is added to the DOM.
This is just the beginning: In the loadView function, you can now try to call generic methods that you can implement on your viewmodels, such as 'activate' and 'deactivate'. You can also show a spinner instead of a view when a new view is being retrieved, etc.
Related
I learned that it is recommended to load jQuery at the end of the page (aka footer)
I am currently experimenting with the Scaffold template from the MVC project.
Now, my scenario is that I have a _Layout where jQuery/script/bootstrap are loaded in the footer.
Says I have a view called "Index.cshtml" that will use _Layout.cshtml as layout.
And inside "Index.cshtml", I am just having something as simple as this:
<script>
$(document).ready(function () {
$('#test').on('click', function (e) {
alert("hey");
});
});
</script>
<button id="test">test</button>
My questions is:
I want to bind event to the button using jQuery. How can i call $(document).ready... inside "Index.cshtml" so I can achieve this in the most efficient way? (since the jQuery is loaded later on at the footer and I want to write my code in the corresponding page instead of in a shared page like _Layout.cshtml). I was thinking to load jQuery in the view, but that would make it a duplicate load wouldn't it?
Solution
In case anyone ran in the the same question like me, please check out Robert's answer below.
Basically the Scaffold template of ASP MVC .Net Core defined a #Section named "Scripts". Therefore, if we define that Script in our View, it will be loaded into the footer of _Layout right after the scripts for jquery/js are loaded.
There are a few ways, but why not give the script section a try?
#section Scripts {
<script type="text/javascript" src="/scripts/main.js"></script>
}
Source: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-3.0
Or in your case:
#section Scripts {
<script>
$(document).ready(function () {
$('#test').on('click', function (e) {
alert("hey");
});
});
</script>
}
I'm trying to render a Javascript ad in my Angular template but it will not show up. I've found some solutions when they append the Javascript to the head tag but I want the ad to be placed in my Html (inside body).
Here is a Plunker: https://plnkr.co/edit/WHhQ95gS5HKSphmmirio
Here is a simple plain Html example that works.
<html>
<head>
</head>
<body>
<div class="ad">
<script src="http://media.affiliatelounge.com/data/nordicbet/ad_js/display_88.js?ad=ad_793270_88.html&size=300x250&clicktag=http://record.affiliatelounge.com/_sVuSoFFh4LK58VwA2IUESrKVVrME-Gsw/1&media=108786&campaign=1"></script>
</div>
</body>
</html>
But if I add the div inside an Angular template it will not render and the console says nothing.
I have some ads up and running here (http://www.odds.nu/erbjudanden), but they are either .gif or iframes. I want to be able to show Javascript ads instead. They are added to the Html but are not rendered (placed in the bottom of the page).
Can $sce or $compile help somehow?
My index.html
<div data-ng-view="" class="mainView"></div>
My app.js
$routeProvider.when("/erbjudanden", {
controller: "offerController",
templateUrl: "/app/templates/offers.html"
});
My offers.html
<div class="ad">
<script src="http://media.affiliatelounge.com/data/nordicbet/ad_js/display_88.js?ad=ad_793270_88.html&size=300x250&clicktag=http://record.affiliatelounge.com/_sVuSoFFh4LK58VwA2IUESrKVVrME-Gsw/1&media=108786&campaign=1"></script>
</div>
Any solution?
If you had inspected result of that url request(make sure adBlock is off)
https://wlbetclic.adsrv.eacdn.com/S.ashx?btag=a_17172b_14837c_&affid=12824&siteid=17172&adid=14837&c=
You will see the actual result
document.write('<script type="text/javascript" src="//wlbetclic.eacdn.com/TrafficOpt/s.5.4.min.js?t=1"></script>');
document.write('<script type="text/javascript" src="//wlbetclic.eacdn.com/wlbetclic/img/js/Affiliate_12824.js?t=20160224"></script>');
//other lines omitted for brevity
So this file is executing document.write which obviously will not work in angular, just because they are totally different (even though you could trigger digest cycle somehow, you still don't have access to modify that script file, as it's generated by 3rd party server and has own variables)
What i would do is - make a page like ad.html, just like index.html or 404.html, then request this file from angular (not as template, but like a view file) as an iframe src with custom attributes
And i would use custom DOM element, and populate contents with jQuery, or with angular, look at jQuery sample below
Also i would need krux/postscribe plugin, because you cannot use document.write in async html files
<!-- create multiple holders -->
<ad-holder url="https://wlbetclic.adsrv.eacdn.com/S.ashx?btag=a_17172b_14837c_&affid=12824&siteid=17172&adid=14837&c="></ad-holder>
<div class="black-widow">
<!-- with size attributes -->
<ad-holder url="http://google.com" width="100" height="40"></ad-holder>
</div>
<!-- jQuery population with iframe -->
<script>
//get all custom elements
$('ad-holder').each(function(){
//create iframe placeholder
var $iframe = $(document.createElement('iframe'));
//get url of custom element
var url = $(this).attr('url');
//request ad.html file with params, currently it's url param
$iframe.attr('src', 'ad.html?url=' + url);
//some stylings acceptable here
$iframe.width('100px');
//or get styles from custom-element
var heightValue = $(this).attr('height');
$iframe.height(heightValue || '50px');
//rebuild custom element with iframe
$(this).append($iframe);
});
</script>
<!-- angular populate with iframe directive -->
<scrip>
angular.module('app', []).directive('adHolder', function(){
return {
restrict: 'E',
scope: { url: '#' },
//you could also execute in compile-phase
link: function(scope, element, attribute){
var $iframe = angular.element(document.createElement('iframe'));
$iframe.attr('src', 'ad.html?url=' + scope.url);
element.html($iframe);
}
}
});
</script>
And ad.html would look like
<body>
<div id="ad"></div>
<script>
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
//for the sake of simplicity we expect only 1 param (url)
return query.replace('url=', '');
}
var adUrl = getQueryVariable('url');
if (adUrl)
postscribe('#ad', '<script src="' + adUrl + '"><\/script>');
else {
var $h1 = $(document.createElement('h1'));
$h1.text('No ad available');
$(document.body).append($h1);
}
</script>
</body>
The best part of this solution is that you can reuse same custom-element with different url attribute for any other ads
Checkout jQuery real working demo
Although this demo heavily uses jQuery, it's easy to tweak for angular version, which i would suggest you to implement as homework =)
Short answer:
Angular does not perform compilation of Javascript in HTML templates. You either put the HTML manually in the page (instead of loading as template) or have another way to call it.
You can read more here
I am trying to load html file in backbone.js .but i am not able display view .could please tell where i did wrong ..i will share my code with u.
**code**: http://goo.gl/CcqYwX
$(document).ready(function(){
var ContactManager = new Marionette.Application();
ContactManager.addRegions({
mainRegion:"#contend"
})
ContactManager.on("start", function(){
console.log("ContactManager has started!");
});
ContactManager.start();
// router
var routers = Backbone.Router.extend({
routes: {
"": "showFirstPage"
},
showFirstPage:function(){
}
})
var ToolItemView = Backbone.Marionette.ItemView.extend({
template: 'template/test.html',
});
var toolItemview = new ToolItemView();
ContactManager.mainRegion.show(toolItemview);
})
i am trying to load test.html file but i am not able to do that..?
Marionette uses underscore templating by default. You'll need to either use some sort of external loader to load them in as variables, or you can place them in the DOM as script elements that you can then reference with your template property. See here:
So for instance if you put it in your html, the code would look like
<html>
<body>
<script type="text/template" id="example">
<div class="template-content-here">
<%=variable_here %>
<!-- probably more stuff here -->
</div>
</script>
<script src="myApp.js"></script>
</body>
</html>
then you could reference it in JavaScript as
var ToolItemView = Backbone.Marionette.ItemView.extend({
template: '#example',
});
That works nicely for small projects, for larger projects you'll want some sort of build/module system to pull in the precompiled templates and reference those directly.
Way more info here: http://marionettejs.com/docs/v2.3.1/marionette.renderer.html
I'm writing a static web site that uses JQuery to make some AJAX calls to a RESTful API and populate the page with data.
The site functions correctly (and quickly), everything is good.
As I extend the site and add additional pages, I'm noticing that I'm duplicating certain regions on every page.
For instance, each page shares a common header element.
<header>...Some non-trivial content...</header>
Rather than repeat this definition on each page is there some mechanism, by which, I can define this section once and include it in each document.
Remember that the pages must be served statically but any standard complaint browser functionality can be utilised.
Is there a good way to do this, and what is it or, will I have to abandon DRY principles for this aspect of my client side code?
There's definitely some ways to achieve this. You could either do it using some features of your server-side language that allows to include the content of a page in another page, or if you do not have any server-side technology, you could simply put that code in it's own html document and load it's content using AJAX.
In jQuery it could look like:
$('#header').load('header.html');
However, if the content isin't static for all pages, you could always define a JS module that would be responsible to render this header. You module could make use of a client-side templating engine, like Mustache, Handlebars, etc. However you do not have to use any of these.
Here's a simple example:
DEMO
//in somefile.js, please note that you should namespace your modules
var Header = {
//default config
config: {
el: '#header',
title: 'Some title'
},
init: function (config) {
var cfg = this.config = $.extend({}, this.config, config);
$(cfg.el).html('<h1>' + cfg.title + '</h1>');
}
};
$(function () {
Object.create(Header).init({
title: 'Some other title'
});
Object.create(Header).init({
el: '#header1',
title: 'Yeah'
});
});
As I mentioned in the comment, this is how I do it:
main.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Main page</title>
<sript src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function(){
$('#commonsection').load('reusablefile.htm');
// which is eqvivalent to:
//
// $.ajax({
// url: 'reusablefile.htm',
// dataType: 'html',
// success: function(data){
// $('#commonsection').html(data);
// }
// });
});
</script>
</head>
<body>
<div id="commonsection"></div>
</body>
</html>
reusablefile.html:
<script>
(function($){ //separate scope to keep everything "private" for each module
//do additional javascript if required
})(jQuery);
</script>
<p>...Some non-trivial content...</p>
You could use jQuery's ajax as to load the header file. In each file you could load the html like so:
$('#header').load('header.html');
Since you're already using AJAX calls to populate your site with data, you could do the same for the common regions.
Just store the HTML for those regions in a separate file and load it in the page with AJAX. Also, you can work with caching using the Cache-Control headers on that file so you don't reload the entire content from the server with each page load.
If you're using straight HTML, you could do it with a SSI include command or by creating a template page and including it in jQuery. Both of these links might help you
Include another HTML file in a HTML file
and
http://www.htmlgoodies.com/beyond/webmaster/article.php/3473341/SSI-The-Include-Command.htm
It looks like this in modest:
main.xhtml
<?xml version='1.0' encoding='UTF-8'?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<include>reusablePiece</include>
</head>
<body>
<reusablePiece/>
</body>
</html>
reusablePiece.xml
<header>...Some non-trivial content...</header>
Very simple would be the jQuery .clone() function.
If you have more complex content I recommend looking at Handlebars.js which is a full fledged JS templating engine.
I would like to know if it is possible to load the content of a kendo.View(...) or kendo.layout(...) from a separate html file?
This is the example from Kendo Hello World Single Page Application :
<div id="app"></div>
<script id="index" type="text/x-kendo-template">
Hello <span data-bind="text: foo"></span>
</script>
<script>
var index = new kendo.View(
"index", // the id of the script element that contains the view markup
{ model: kendo.observable({ foo: "World!" }) }
);
var router = new kendo.Router();
router.route("/", function() {
index.render("#app");
});
$(function() {
router.start();
});
</script>
Is it possible to do somethink like this :
<div id="app"></div>
<script>
var index = new kendo.View(
"hello.html", // the path of the script element that contains the view markup
{ model: kendo.observable({ foo: "World!" }) }
);
var router = new kendo.Router();
router.route("/", function() {
index.render("#app");
});
$(function() {
router.start();
});
</script>
There is now a way to do it. Here is documentation on how to do it: Kendo Remote Templates
Here is the gist of it:
You need to make a template loader that basically reads html/string from a file. Kendo does not supply one in the box but this is the one that they use in their example.(templateLoader.js). Include it on the your html page.
Add your templates in a separate html file. You can store multiple templates in the same file. However you should only be putting templates there ideally. Let's say this is in file mytemplate.html
<script id="index" type="text/x-kendo-template">
Hello <span data-bind="text: foo"></span>
</script>
On your main html page, add a <script> block that calls your template loader with that template file. This will/should inject the templates onto your main page and then you should be able to use it as if it was a local template.
<script>
templateLoader.loadExtTemplate("mytemplate.html");
</script>
It can't be done this way atm. I'm not even sure if it should be doable.
Perhaps fetch the template beforehand using $.get() or .load()?
edit: using .load() you'd have to dynamically create script template and using $.get() append the script template somewhere in the document.