I have a list of users coming from back-end and I append each one of them to my HTML page like below. My goal is to have javascript rating system for every user.
<head>
<link href="/css/rateyo.css"/>
<script src="/js/rateyo.js"/>
<script type="text/template" id="mustache-template">
{{#user}}
<li>
{{name}}
<div id="rating"></div>
</li>
{{/user}}
</script>
<script>
$("#rating").rateYo().on("rateyo.set", function (e, data) {
});
</script>
</head>
<body>
<ul>
<!-- All the individual users will be in their own li element here -->
</ul>
</body>
Everything is working except my rating script. It should make five stars next to each user. But I heard that you can't put scripts inside templates, is it correct? If I move that <div id="rating"/> to somewhere else it works like it's meant and shows the stars.
What should I do? I can't really put that script outside of my templates.
You can add your function to the object you are trying to parse with the template, and then call the function in your template.
var userlist = {
user: [
{name: 'name nameson', doRating: function() {someOtherFunction();}},
{name: 'some otherguy', doRating: function() {someOtherFunction();}}
]
};
var someOtherFunction = function() {
$("#rating").rateYo().on("rateyo.set", function (e, data) {
//Do stuff?
});
}
<script type="text/template" id="mustache-template">
{{#user}}<li>{{name}} <div id="rating"></div></li>{{/user}}{{doRating}};
</script>
Or you could of course calculate the rating on beforehand, and implement some sort of {{user.rating}} and use that to generate in your template.
Or as I look closer, you could just add the listener after mustache is done rendering. Since the div#rating would then be available (which should really be a class btw).
This would be my approach at least.
Related
<!--language: lang-html-->
#foreach (var book in Model.Category.Books)
{
<div class="rate-star-#book.Id"></div>
<script>
$(".rate-star-#book.Id").stars({ stars:5, readonly:false});
</script>
}
A brief explanation about what I'm trying to do:
This script code inside the loop is one of the star rating plugins that I need to implement for books on the page. The problem here is that I need to include jquery code further up in the body but since JQuery library loads later than the script code, I get "$ is not defined" error.
What Have I tried:
I tried to implement the solution on this page
[Handling code which relies on jQuery before jQuery is loaded written by Ashkan Mobayen Khiabani. I put the code inside a function. The function is called at the bottom of the body. It is supposed to be called as many times as it's created in per iteration. But since it's called only once at the bottom the other created functions don't run and only one book gets affected by the function as a result.
<!--language: lang-html-->
#foreach (var book in Model.Category.Books)
{
<div class="rate-star-#book.Id"></div>
<script>
function rateStar{
$(".rate-star-#book.Id").stars({ stars:5, readonly:false});
</script>
}
}
#section scripts
{
<script>
rateStar();
</script>
}
So how to do that function gets called as many times as it's created in the document?
in Views/Shared open _Layout.cshtml and in the head tag create a script tag and create an array (lets name it postJquery:
<head>
...
<script>var postJquery = [];</script>
...
</head>
Now in the same file (_Layout.cshtml) go to the bottom and add this code before ending of body tag:
<script>for(var i=0;i<postJquery.length;i++) postJquery[i]();</script> //just ad this line
</body>
Now anywhere that you need to run a code that depends on jquery just add it to a function and add it to postJquery array and it will run after jQuery is loaded like this:
postJquery.push(function(){
//your code goes here
});
for example, your case would be:
#foreach (var book in Model.Category.Books)
{
<div class="rate-star-#book.Id"></div>
<script>
postJquery.push(function(){
$(".rate-star-#book.Id").stars({ stars:5, readonly:false});
});
</script>
}
}
The code above will run just fine, but there is what I don't like about it that the script tag and its content will be repeated for each book item, for example, if you have 20 books in your loop, the following code will be repeated 20 times (of course book id will change for each one):
<script>
postJquery.push(function(){
$(".rate-star-#book.Id").stars({ stars:5, readonly:false});
});
</script>
So instead I would do something like this:
#foreach (var book in Model.Category.Books)
{
<div class="rate-star" data-id="#book.Id"></div>
}
<script>
postJquery.push(function(){
$(".rate-star").stars({ stars:5, readonly:false});
});
</script>
As I don't know about .stars if it should be used on a single element (and clicking on above code effects all items) you could do it like this:
#foreach (var book in Model.Category.Books)
{
<div class="rate-star" data-id="#book.Id"></div>
}
<script>
postJquery.push(function(){
$(".rate-star").each(function(){
$(this).stars({ stars:5, readonly:false});
});
});
</script>
In below code I want to add the div dynamically under div tag with id="includedContent". And also load data using number of ids.So instead of having a hardcoded Id(123) to load method i want to make it accept Array of ids and displqay the data in the dynamically created tag???
<script src="js/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
function vikas(){
var i=0;
$(function(){
$("#includedContent").load("b.xhtml #123");
});
}
</script>
</head>
<body>
<button type="button" style="width:80px; height:40px;" onclick="vikas()">Click</button>
<div id="includedContent" >vikas<img click="vikas()" src="www.google.com" alt=""></img><br/></div>
</body>
This looks like a perfect case for using a templating engine like MustacheJS http://mustache.github.io/
It allows you to create a template into a variable and then "throw" a JSON-Object onto a new instance of an mustacheJS and it will replace some placeholders you are defining in the template (they'll look like this:{{variable}}). You can find easy follow examples on their website.
I hope i could help.
I've managed to get myself into a bit of trouble with a project I'm working on.
Originally the site has one page on it that uses Knockout, with the other pages using jQuery. Due to some problems with the Foundation modal placing itself in the root of the body element, I ended up applying the bindings for the viewmodel for this page to the body element.
Fast forward 4 months, and without foreseeing the trouble I'm in now, I went and rebuilt our shopping basket in Knockout. The shopping basket is visible on every page and is included using a ZF2 partial.
Going back to the page I worked on 4 months ago, it is completely broken with the error message in console saying:
Uncaught Error: You cannot apply bindings multiple times to the same element.
Here's some code to show my layout:
<html>
<head>
<title>My Website</title>
</head>
<body> // 4 month old SPA bound here
<nav>
<div id='shopping-basket'> // Shopping basket bound here
...
</div>
</nav>
<div id='my-app'>
...
</div>
</body>
</html>
JavaScript:
var MyAppViewModel = function() {
// logic
};
var ShoppingBasketViewModel = function() {
//logic
};
ko.applyBindings(new MyAppViewModel(), document.body);
ko.applyBindings(new ShoppingBasketViewModel(), document.getElementById('shopping-basket');
If I had the time I could go back and rework the original application to sit within it's own div container that would site side by side with the basket, but unfortunately this isn't an option.
The other option is to discard the last bit of work I did on the shopping basket and replace it with jQuery, but this would mean losing a weeks worth of work.
Is there anyway when I'm applying the bindings that I could have both viewmodels working side by side, while being nested in the Dom, and remaining independent of each other?
I had some similar problem. I needed to applybinding to specific nested elements and start with a bind on the document first. Same problem. My solution was to add some ignore element part and than bind the specific element manually.
1) Add a custom binding so you can skip binding on the specific shopping basket:
ko.bindingHandlers.stopBinding = {
init: function() {
return { controlsDescendantBindings: true };
}
};
ko.virtualElements.allowedBindings.stopBinding = true;
2) Add the custom binding in your html (surround your shopping basket):
<html>
<head>
<title>My Website</title>
</head>
<body> // 4 month old SPA bound here
<nav>
<!-- ko stopBinding: true -->
<div id='shopping-basket'> // Shopping basket bound here
...
</div>
<!-- /ko -->
</nav>
<div id='my-app'>
...
</div>
</body>
3) Apply your bindings as you already do:
ko.applyBindings(new MyAppViewModel(), document.body);
ko.applyBindings(new ShoppingBasketViewModel(), document.getElementById('shopping-basket');
4) The first bind will skip the shopping-basket because of your custom binding handler and your second bind will explicitly bind the shopping-basket.
I haven't tested the code above on your specific example, but it should point you into the correct direction.
Please see the code here http://plnkr.co/edit/FqfkcyZSqPkA7JjMMLrb?p=preview
I am embedding a javascript object/value in html, which needs to be read by angular. It reads value in index.html, but not in partial. (_global_link is read properly, but not _global_link_partial). Is it because _global_link_partial not available at $routeChangeSuccess, if so which event I need to listen to. I could provide the value as const in module definition, or directly in controller etc, but this value is very view specific and better to maintain there.
Thanks.
error:
_global_link Object {link: "abc"} controllers.js:3
ReferenceError: _global_link_partial is not defined
code:
function Test1Ctrl($scope) {
$scope.$on('$routeChangeSuccess', function ($event, current) {
console.log('_global_link', _global_link);
console.log('_global_link_partial', _global_link_partial);
});
}
function Test2Ctrl($scope) {
$scope.$on('$routeChangeSuccess', function ($event, current) {
console.log('_global_link', _global_link);
console.log('_global_link_partial', _global_link_partial); });
}
index.html
<div class="container">
<ul>
<li>test1
<li>test2
</ul>
<div ng-view></div>
</div>
<h2>in index.html</h2>
<script>
_global_link = {link: 'abc'}
</script>
partials (test1, test2)
<h2>In test1</h2>
<script>
_global_link_partial = {link: 'link1'}
</script>
<h2>In test2</h2>
<script>
_global_link_partial = {link: 'link2'}
</script>
I short, you need to include jQuery before angular.js
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js"></script>
Take a look at #igor's answer at https://groups.google.com/forum/?fromgroups=#!topic/angular/H4haaMePJU0 :
long story short: it's because the script tag is special and angular doesn't treat it as such. if you include jquery on this page, the code should work.
When angular detects jquery, it will use it for dom manipulation and jquery is smart enough to treat the script tag as special.
Here is a plunker: http://plnkr.co/edit/UhKx8QWGTExLgdQMJuyi?p=preview
I changed your example plunker to latest version of angular.js (1.2.13) and also changed the routes a little.
this is my html:
<script type="text/html" id="ul-template">
<ul id="list">
{{> li-templ}}
</ul>
</script>
<script type="text/html" id="ul-template2">
<div id="list2">
{{> li-templ}}
</div>
</script>
<script type="text/html" id="li-templ">
<p>{{ name }}</p>
</script>
as you can see, I want to reuse the #li-templ part, but it seems that I have to write it into a file called li-templ.mustache then I can include it as partial?
can I just define them in the single html file?
I'm assuming you're using the JS flavor of Mustache.
In mustache.js an object of partials may be passed as the third argument to Mustache.render. The object should be keyed by the name of the partial, and its value should be the partial text.
You need to:
Define some dummy data for name
Get your partial template by getting the HTML of #li-templ
Create an object with the name of the partial (li-templ) as the key
Tell Mustache to render each template with the view data including your partial
Here's some jQuery to do just that:
var view = {"name" : "You"},
li = $('#li-templ').html(),
partials = {"li-templ": li},
ul1 = Mustache.to_html($('#ul-template').html(), view, partials),
ul2 = Mustache.to_html($('#ul-template2').html(), view, partials);;
document.write(ul1, ul2);
Here's a jsFiddle of it all working- http://jsfiddle.net/maxbeatty/EYDfP/
ICanHaz.js (ICH) can help you with this.
ICanHaz.js: A simple/powerful approach for doing client-side templating with Mustache.js.
I've found that mixing templates (in scripts tags) with the ordinary HTML in the page messes with my code editor (syntax highlighting, indenting etcetera). Loading them as a separate server keeps your HTML clean.
Check out this ICH pull request for automatic loading of <script type="text/html" src="my-templates.html"></script> from your server to one template per file.
You could also load more than one template per remote HTML file this using simple code like:
function getTemplates(url) {
$.get(url, function (response) {
$('template', response).each(function () {
ich.addTemplate(this.id, $(this).text());
});
});
}
Or, if you'd like ICH to load them automatically from urls in your page:
<head>
<link rel="templates" type="text/html" href="my-templates.html">
</head>
$("link[type=templates]").each(function (index, link) {
getTemplates(link.attr("href"));
});
In your my-templates.html
<templates>
<template id="ul-template">
<ul id="list">
{{> li-templ}}
</ul>
</template>
<template id="ul-template2">
<div id="list2">
{{> li-templ}}
</div>
</template>
<template id="li-templ">
<p>{{ name }}</p>
</template>
</templates>