How to use Dust.js on the client dynamically? - dust.js

All the template engines I know use a <script> tag to include custom templates in the page. For example, JsRender:
<script id="theTmpl" type="text/x-jsrender">
<div>
<em>Name:</em> {{:name}}
{{if showNickname && nickname}}
(Goes by <em>{{:nickname}}</em>)
{{/if}}
</div>
</script>
Then I can dynamically use this template to refresh elements of a page:
var template = $.templates("#theTmpl");
var htmlOutput = template.render(data);
$("#result").html(htmlOutput);
However, I don't understand how client Dust.js works. How to have custom Dust.js templates on my page? How to dynamically render a content into them and put the result on the page?

Dust templates can be included in script tags on the page, and that works well for experimentation and testing. The template would look something like:
<script id="theTmpl" type="text/x-dust">
<div>
<em>Name:</em> {name}
{?showNickname}
{?nickname}
(Goes by <em>{nickname}</em>)
{/nickname}
{/showNickname}
</div>
</script>
And the JavaScript would look something like this:
// Get the template content
var template = $('#theTmpl').html();
// Compile the template into JavaScript string
var compiledTemplate = dust.compile(template, 'nicknameTemplate');
// Execute the JavaScript string and register the template within Dust
dust.loadSource(compiledTemplate);
var data = {
name: 'Green',
nickname: 'Dust guy',
showNickname: true
};
// Render the template with template name, data, and a callback function
// (because Dust is asynchronous
dust.render('nicknameTemplate', data, function(err, out) {
// The rendered output is in the out variable. Do with it what you will.
});
While this method works, it is not ideal for a production site. For the above example to work, you must include dust-full.js on your page, which is a much larger file than dust-core.js (because dust-full.js includes the parser and compiler, while dust-core.js only needs the runtime). Ideally, Dust templates are precompiled on the server and served to the client as JavaScript files.
HTML file:
...
<script type="text/javascript" src="templates/nickname.js"></script>
...
And the JavaScript:
var data = {
name: 'Green',
nickname: 'Dust guy',
showNickname: true
};
// The template is already loaded and registered, so all we need to do is render
dust.render('nicknameTemplate', data, function(err, out) {
// The rendered output is in the out variable. Do with it what you will.
});

Related

Using VueJS in a ASP project

Currently developing a ASP MVC site.
Some sections of the site will use VueJS for displaying some list, forms etc.
The project setup is Bower, Grunt, standard C# ASP project using TypeScript.
This is my first time using Vue, and the simple stuff is pretty stragt forward. Seting up a page with a form, getting data from a WebService etc.
My problem/question is, what, and how, do i get the best setup, for using Single File Components (Vue) in my cshtml view files.
So, lets say I have a section on my site, where i want to display orders from the user.
Layout, navigation etc is setup by my excisting ASP code. I have a CSHTML viewpage for the current page, pretty vanilla:
#inherits MyViewPage<MyViewModel>
#{
Layout = "~/Views/layout.cshtml";
}
<div id"app">
</div>
Thats it for the excisting view page. In this page, i want to include a Vue Single File Component.
Previously i had the markup directly in the CSHTML page, which works fine. But when i want to user Vue-router, it becomes a problem to maintain the different views. So i should move the markup into a Component.
This is the basic setup;
const page1 = { template: '<div>Page1</div>' }
const page2 = { template: '<div>Page2</div>' }
const routes = [
{ path: '/', component: page1 },
{ path: '/page2', component: page2 }
]
const router = new VueRouter({
routes
})
var vm = new Vue({
router,
el: "#app"
})
Lets say i create a .vue file called page1.vue instead. This contains
<template>
my new page
</template>
<script>
export default {
name: '?',
date: function() {
}
}
</script>
How do i get this file included in my CSHTML file for instance?
You need to develop with webpack to build Single File Components.
See the Vue documentation on this.
Use the Vue Cli and drop a web pack build into your cshtml page.

Directive for loading forms into a single page app lost controller binding

I'm creating a single page app using AngularJS. I have created a main page with a custom directive for loading a form URL. I want to change the the forms based on a variable in my controller. However, I lost my bindings once I compiled and linked the form from URL
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<div ng-app="tradesman" ng-controller="application" class="form-horizontal" role="form">
Hello {{applicant.name}}
<my-form name="formName">
</my-form>
</div>
var tradesman = angular.module("tradesman")
tradesman.controller("application",function application($scope){
$scope.formName="personals";
$scope.applicant={};
});
tradesman.directive("myForm",function($http,$compile){
return {
restrict:"E",
scope:false,
replace:true,
link:function(scope,element,attrs){
var url="http://tradesman.local/views/"+scope.formName+".html";
$http.get(url).then(function(response){
scope.$watch(function(){
element.html(response.data);
$compile(element.contents())(scope);
});
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
This has successfully loaded my personals page but lost the binding in that page. More over I want a new form to be loaded whenever I change my formName in controller.
What needs to be done to make form loading and binding work simultaneously

Ajax request not fired form partial page when the main page also has an ajax request

In my _layout.cshtml I have a partial view that includes js trough requirejs.
<script data-main="/Scripts/Search" src="/Scripts/require.js"></script>
In this js file I use the following to populate a knockout vm.
$.getJSON("/Search/Index", function (data) {
self.availableCities(data.AvailableCities);
});
This works well on all pages except when my main view also has an ajax request.
<script data-main="/Scripts/Index" src="/Scripts/require.js"></script>
$.getJSON("/Property/All", function (data) {
self.properties(data);
});
Here is my require config, it is the same for the partial and the main view.
require.config({
baseUrl: "/Scripts",
paths: {
"text": "text",
"knockout": "knockout-3.3.0",
"jquery": "jquery-2.1.3.min"
},
shim: {
"jquery": { exports: "$" }
}
});
When the main page has an ajax request only this request is fired, I am not sure how to fix this. It looks like a configuration issue, tested it in both Firefox an Chrome so it does not appear to be browser specific.
It turns out having multiple <script data-main="/Scripts/Search" src="/Scripts/require.js"></script> tags in one page isn't such a bright idea.
I figured it out after some more research,
this question has a good solution if you run into a similar problem.
Basically you need one 'main.js' file and add the other page components via the require logic.
Edit:
Doing this may result in the following knockout error:
Error: You cannot apply bindings multiple times to the same element.
To fix this I have used the following binding handler:
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
To enable this binding handler on containerless elements use the following:
ko.virtualElements.allowedBindings.stopBinding = true;
Apply the following binding around the partial view. To prevent the main-page from binding to the elements in the partial.
<!-- ko stopBinding: true-->
<!-- /ko -->
Finally use ko.applyBinings on the partialview like this:
ko.applyBindings(new partialViewModel(), document.getElementById("your-partial"));

unwanted multiple ajax request to an action in asp.net mvc

I am developing an asp.net mvc application and have ajax calls on my page. Here is a form which I load by ajax call to page :
The form is located in a partial view
<div id="CreateCultureArea">
<%
using (Ajax.BeginForm("CreateCulture", "Admin", new AjaxOptions() { OnSuccess = "handleCreateCulture" }))
{ %>
.....
<% } %>
</div>
Code Updated
The following script is located in a view :
Create Culture
<script type="text/javascript">
$('.CreateCulture').live('click', function (e) {
e.preventDefault();
var idval = this.id;
$.ajax({
url: "/Admin/CreateCulture",
dataType: 'html',
data: { id: idval },
success: function (mydata) {
$("#CultureContentArea").empty();
$("#CultureContentArea").empty().hide().append(mydata).fadeIn(2000);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
},
type: "GET"
});
return false;
})
</script>
When users click on a link with CreateCulture class, the form is loaded to page. But as I saw the requests in firebug, it calls the action multiple times. I read similar posts like mine on stackoverflow.com and most of them suggested removing repetitive "jquery.unobtrusive-ajax.min.js" calss in page, but as I saw the output page I only see on link to the "jquery.unobtrusive-ajax.min.js" script.
The problem is that #Ajax.BeginForm emits a form that has the attribute data-ajax="true". In the unobtrusive AJAX script (see the non-minified version and look for calls to asyncRequest). So by calling $.ajax yourself, you are repeating the work of #Ajax.BeginForm. Just take out your call to $.ajax and you'll see your action get called only once.
If you need to take action after the AJAX call completes, set the OnComplete property of AjaxOptions in your call to Ajax.BeginForm.
Make sure that you have not multiple .js refrence in page
<script src='/Scripts/jquery.unobtrusive-ajax.min.js'></script>
<script src="/Scripts/jquery.validate.min.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js"></script>
The problem was that I put some scripts in partial view and each time it duplicated by loading this. So I put the scripts all in on page and problem solved.

MVC4 Razor confused about braces

I've got a fairly simple question for all the Razor experts out there. I'm trying to make a jQuery $.ajax() call to a URL, using Url.Content() to translate the home-relative path into an root-relative path. In so doing, Razor is getting a bit confused about where the end of my #section is located. I'd prefer to be able to specify the URL inline, but when I do that, Razor thinks that the end of the $.ajax() parameter list is the end of my #section. I'm using #section because I want to use layouts to place my javascript at the bottom of each file. Why is Razor getting so confused? I've even tried using #(Url.Content(...)), but that doesn't work either.
Also, is this the best way to approach the problem? I'm using the ASP.NET MVC 4 Preview.
This works:
#section Scripts {
<script type="text/javascript">
var getMessagesUrl = '#Url.Content("~/Logging/GetMessages")';
$(document).ready(function () {
$.ajax({
url: getMessagesUrl,
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
});
});
</script>
}
This doesn't:
#section Scripts {
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '#Url.Content("~/Logging/GetMessages")',
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
}); //Razor thinks this is the curly-brace that ends the section!
});
</script>
}
This is likely down to the behaviour of the parser. When it encounters the # symbol, the parser switches to code mode and will read the implicit expression Url.Content("~/Logging.GetMessages") (well, actually it will read until the ' symbol, determine it is not a valid character in an expression and trackback to return until the end of the ). It's after this stage that the parser is getting a little confused with your view because it's likely in code mode when it encounters the final } character, and thinks it is the end of of a code span.
The reality is, you have to be quite careful when using javascript within a razor view with C#. The transitions to code are explicit, e.g. # and after a {, but the transitions to markup are a little harder to determine.
Razor aside, my recommendation would be to stick your javascript application code in an external file, and take advantage of data-* attributes to convey meta information to your application code, e.g:
<script id="messageScript" type="text/javascript"
src="/Scripts/messages.js"
data-messages="#Url.Content("~/Logging/GetMessages")"></script>
Which you can access as:
(function($) {
$(function() {
var $this = $("#messageScript");
$.ajax({
url: $this.attr("data-messages"),
type: "html",
success: function(result) {
$("tbody").html(result);
}
});
});
})(window.jQuery);
Update: Dave's less than symbol was not causing the problem, he only added it in his question for illustrative purposes.
On MVC4 I was able to isolate the issue. This would not compile:
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '#Url.Content("~/Logging/GetMessages")',
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
}); //<-- test
});
</script>
But this would:
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '#Url.Content("~/Logging/GetMessages")',
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
}); //-- test
});
</script>
Seems like it was just the < in the comment that was throwing it.
Matthew's answer pretty much explains the behaviour (although, to be honest, I can't reproduce your problem - nor see why it wouldn't work - both your examples run just fine here). For a different approach, you could dedicate an action/view to generated javascript variables (urls, settings, localized texts, whatever), i.e.:
// Could/should be OutputCached depending on the scenario
public ActionResult Globals()
{
var model = new ClientGlobalsModel();
// ClientGlobalsModel has a single (could be more) Dictionary<string, string>
// (Urls) and a ToJSON() method which uses JavaScriptSerializer to serialize
// the object:
model.Urls.Add("GetMessages", Url.Content("~/Logging/GetMessages"));
// I mostly use this method for e.g. actions:
model.Urls.Add("UploadImage", Url.Action("Upload", "Image"));
Response.ContentType = "text/javascript";
return View(model);
}
Globals.cshtml:
#model ClientGlobalsModel
#{
Layout = null; // If you have a layout supplied in e.g. _ViewStart
}
var GLOBALS = #Model.ToJSON()
Yeah, this could have been a simple Content() result rather than a view - but when you have more globals (e.g. settings + urls + texts), you may want easier control over the script output and maybe serialize each dictionary individually. May also want to namespace that "GLOBALS" variable in some shared application namespace to avoid polluting the global scope.
(e.g.) Index.cshtml:
<script src="#Url.Action("Globals", "Client")"></script>
<script src="#Url.Content("~/Scripts/main.js")"></script>
... which simply includes the output from /Client/Globals. And "main.js", into which we have now moved the rest of your script:
main.js (static):
$(document).ready(function () {
$.ajax({
url: GLOBALS.Urls.GetMessages,
dataType: 'html',
success: function (result) {
$('tbody').html(result);
}
});
});
You can, of course, use the same kind of approach to output a few user/context/view-specific settings directly into the view. For a few URL's or data, the data-* attribute approach may be better depending on your tastes. I'm not a fan of stuffing tons of what's basically settings into attributes on every HTML page, though.
Seems to still be a problem with final MVC4 / Visual Studio 2010, but here is my fix:
#section jQueryDocumentReady
{
#{
<text>
// All the javascript and bracers you want here
</text>
}
}
Thanks for all of the help, folks.
It looks like it must have been a bug in the ASP.NET MVC 4 Preview. I just upgraded to the ASP.NET MVC 4 Beta which came out on February 15, and the problem is now completely gone.

Resources