I'm trying to make a cross platform mobile application using jQuery Mobile.
I have a JSON string that displays information about a specific object (check it out here: http://app.calvaryccm.com/mobile/web/teachings/json?callback=?) and I want it to turn it into a list view that connects to a single item view. The problem is, the listview isn't displaying like a list at all. It is almost like HTML without CSS. Check it out here: http://mbeta.calvaryccm.com/#teachings
This is the Javascript for parsing the JSON string:
<!-- Getting Teaching Data -->
<script type="text/javascript">
$(document).ready(function () {
$.getJSON("http://app.calvaryccm.com/mobile/web/teachings/json?callback=?",
function (data) {
//remove any characters from the query that might be unsafe to use as an ID for a page
//data.pageId = data.MessageNumber.replace(/[^\w]/, "");
//Feed the data to the template and add the new page to the body.
var res = $("#teachingTemplate").tmpl(data); //.appendTo(document.body);
$("#teachings").append(res);
//Grab a reference to that shiny new page
//var newpage = $("#" + data.pageId);
});
});
//Makes date readable
function GetDate(jsonDate) {
var value = new Date(parseInt(jsonDate.substr(6)));
return value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear();
}
This is supposed to be the list view:
<script id="teachingTemplate" type="text/x-jquery-tmpl">
<div id="${MessageNumber}">
<div data-role="header">
<a data-icon="arrow-l" href="#" data-rel="back">Back</a>
<h1>${Title}</h1>
</div>
<div data-role="content">
<div class="teachingsForm">
<ul data-role="listview">
<li><a href="singleTeachingView" class="tableImage">
<img src="" alt=""/>
<h3>${Title}</h3>
<p>${Speaker} - ${GetDate(MessageDate)} - ${MessageNumber} {{if Book != null}} - ${Book.BookName} ${ChapterVerse}{{/if}}</p>
</a>
</li>
</ul>
</div>
</div>
<div data-role="footer">
<h4>2011 Calvary Chapel Melbourne</h4>
</div>
I cannot figure out why my listview isn't displaying right. If you want to see it in action look here: http://mbeta.calvaryccm.com/#teachings . I need help getting my listview to display right and direct to the right page.
It looks exactly like what your template says it should look like. Note that each of your <ul> elements only has one <li> element in it.
Your template starts off with an outer <div>, and then there's a "header" <div> with the <h1> title. Then there's the "content" <div>, and ultimately the <ul>, and finally the "footer". That's what your result page looks like. If you want to do some sort of iteration, well, you'll have to explicitly do that in your template, because otherwise the template code will assume you just want it to re-apply the template to each object in the array you pass it.
There's nothing wrong with the "JSON parsing". Note that in your JSON, there's no need to quote "/" characters with "\" and in fact (though it doesn't matter for JSONP) it's not valid JSON.
edit — OK so now that I've pulled my head out from wherever it was I think I see what you're doing. The problem may be that you just need to call
$.mobile.changePage();
at the end of your JSONP callback function. However I note that your "$.mobile" doesn't have a "changePage()" function ... I don't know what that means. You're using a pretty old version of jQuery too.
Related
I'm trying to achieve something similar to what JSRender does, but I'm not sure how to go about it. Consider the HTML "template" below:
<!DOCTYPE html>
<body>
<div class="content">
<div class="notifications">{{:notifications}} notifications</div>
<div class="something else">this is {{:something_else}} to show</div>
</div>
</body>
</html>
Supposed I have JSON data like so:
{"notifications": "3", "something_else": "some arbitrary data"}
How do I populated this data into the HTML page? The way JSRender does it seems to involve creating a separate template in a <script> tag, then populating the data into the template and finally copying the template into an empty container. Is there a way to avoid this template redefinition? I believe my HTML page can already act like a template as demonstrated above.
The Question: is it possible to display JSON data into a ready HTML page (such as above) with defined "data positions"? As part of the challenge, using $('.notifications').html()-related methods should be avoided since this would be cumbersome when handling large extensive data.
You can do that using top-level JsViews top-level data-linking - with an element such as a <span> for each insertion point.
<div class="content">
<div >this is <span data-link="something_else></span> to show</div>
...
Code:
$.link(true, ".content", data);
In addition, the data is data-bound to the HTML.
Here is a sample which shows the data-binding by letting you actually change a data property dynamically:
It also shows data-linking to the src and title attributes of an <img> tag. See here for more information about different data-link targets.
var data = {notifications: "3", something_else: "some arbitrary data",
imgData: {img1: {src: "http://www.jsviews.com//icons/android-chrome-36x36.png",
desc: "some image"}}};
$.link(true, ".content", data, {replace: true});
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsviews/0.9.90/jsviews.js"></script>
<div class="content">
<div ><span data-link="notifications"></span> notifications</div>
<div >this is <span data-link="something_else"></span> to show</div>
<img data-link="src{:imgData.img1.src} title{:imgData.img1.desc}"/>
<br/>Edit: <input data-link="something_else"/>
</div>
While BorisMoore's answer addresses the question adequately, I crafted a "hack" that also appears to work with the ability to support attributes on almost all elements, though I don't know to what extent it is reliable.
However, this requires one to change the data structure to also indicate the type of element and even the part of it (attribute) where the data is to be inserted. The data would need to look like so:
{"notifications": "span:|3", "something_else": "span:|some arbitrary data", "avatar":"img.alt:|A"}
Then in JQuery, one could do something like so:
$.each(data, function(key, value) {
value = value.split(":|");
var element = value[0];
value = value[1];
if(element.indexOf('.') == -1){
var content = $(element + ':contains("{{:'+key+'}}")').last().html().replace("{{:"+key+"}}", value);
$(element + ':contains("{{:'+key+'}}")').html(content);
}else{
element = element.split('.');
var attribute = element[1];
element = element[0];
$(element + '['+attribute+'="{{:'+key+'}}"]').last().attr(attribute, value);
}
});
EDIT: The main drawback of this method is that it unbinds all attached events when an elements property is modifed this way.
I'm working on a project where I have to delete en element in a certain condition (if). My code generates a <li> in which data is inserted via an array. I need to delete that <li> if some conditions are met.
Since removeParent() doesn't exist I've found different methods saying to use
e.parentNode.parentNode.removeChild(e.parentNode);
Rings a bell, it works in certains cases of course.
BUT
In my case, this is dynamically rendered elements in an array inside the <li> and there is no parent of the parent, so I get an error.
My script looks for an element in the <li>, in my test I use a <div> found by its class.
$(node).find('.results-name')[0].parentNode.remove();
Does NOT work unfortunately so I'm looking for other ideas...
Any clue?
Thanks a lot!
The thing is the code is huge and I can't copy/paste everything here.
Here's more info:
- in the page I have a template that's used by the javascript to populate a div with all the contents. I have ~20 results.
The template starts like this :
<script id="itemtemplate" type="geowacht/template">
<div class="results-name">
<h4 class="show-more-name itemaponaam" data-target="#result"></h4>
<div class="d-block" id="result">
<div class="itemaddress"></div>
<div class="itemgeodesc"></div>
<div class="itemphone"></div>
The pages calls a script that queries an API and returns 20 results. These results are parsed and added to the page using the template.
Here's the beginning of the code:
apiconfig.default_populateItemNode = function(node, apotheekData, wachtPeriodeData, authenticationData) {
$(node).find('.itemaponaam')[0].setAttribute('data-target', '#result-' + itemPos);
$(node).find('.buttons')[0].setAttribute('id', 'result-' + itemPos + '-buttons');
$(node).find('.buttons')[0].setAttribute('data-apbnb', apotheekData.pharmacy.id);
At work I'm just starting out with JavaScript, MVVM, and Kendo's JS framework, all at once, and I have a fairly simple problem.
I've created a View Model that allows Superheroes to be registered.
The JSBin I'm working in: http://jsbin.com/gewu/3/edit?html,js,output
Here's the HTML(view):
<div id="view">
Superhero: <input data-bind="value: name" /><br/>
Superpower: <input data-bind="value:power"type="text">
<label for="">from Earth?<input type="checkbox" data-bind="checked:fromEarth"></label>
<button data-bind="click: registerHero" >Display User Info</button>
<div id="array-display"></div>
<p>Entries: <span data-bind="text: knownHeroes.length"></span></p>
</div>
And here's the JS (viewModel):
var viewModel = kendo.observable({
knownHeroes : [],
name: "Hulk",
power:"Stength",
fromEarth: true,
registerHero: function() {
var name = this.get("name");
var power = this.get("power");
var fromEarth = this.get("fromEarth");
this.knownHeroes.push({"name":name,"power":power,"fromEarth":fromEarth});
}
});
kendo.bind($("#view"), viewModel);
Now, I'm trying to get the View to loop through and display the array of knownHeroes. But it won't render anything. I know the data is being pushed to the array, because I can see the array.length increasing, and I can look up specific values in the array. I'm assuming the problem has to do with how I'm referencing the array in the view. But I'm not sure. Here's the template I've written:
HTML:
<script id="registry-view" type="text/x-kendo-template">
<ul>
# for (var i=0; i < knownHeroes.length; i++) { #
<li>
<ul>
<li>#= knownHeroes[i].name #</li>
<li>#= knownHeroes[i].power #</li>
<li>#= knownHeroes[i].fromEarth #</li>
</ul>
</li>
# } #
</ul>
</script>
<script type="text/javascript">
var template = kendo.template($("#registry-view").html());
$("#array-display").html(template); //Append the result
</script>
You have got some mistakes.
First of all you got script wrote in html portion of this jsbin as well as in javascript section. Html part executes first so the viewModel isn't defined yet (check console for errors)
Also the object you pass to the template is stored always in "data" variable.
Last mistake is when using your desing, anytime you add any new data row, whole template needs to be reloaded (including all previously added data rows)
I corrected some of your mistakes in following jsbin: http://jsbin.com/jomemuko/1/edit (actually you need to hit the Run with JS button to make it work - some script loading issue I don't have time for)
Ideally you should use a listView widget and assign it a template for only one item. Also in your viewModel you should create a kendo dataSource and pass it as an option to newly created listView. Then in the viewModel you should refine your registerHero function to make it add the hero to the dataSource. Widget should automatically refresh.
Hope it helps
In the moment I can add li tags to my list with script. But how can I add dynamically li tags in a function in .js? Hopefully I will see a good example.
Below is my code. Thanks!
<div data-role="page" id="searchPage" data-theme="b">
<div data-role="content">
<ul data-role="listview" data-filter="true" data-theme="b" id="searchListUl">
</ul>
</div>
<script type="text/javascript">
$("#searchListUl").append('<li data-filtertext="Apple">Apple</li>');
$("#searchListUl").listview('refresh');</script></div>
Your function would be something like:
var addItem = function(item){
$("#searchListUl").append('<li data-filtertext="'+item+'">'+item+'</li>');
}
You can call it with:
addItem("apple")
If you with "dynamic" mean that you want to be able to append list items without knowing the name ("Apple"), you could make a generic function, using the jQuery function used to create elements:
function add(name) {
var $li = $("<li>").attr("data-filtertext", name)
.appendTo("#searchListUI");
$("<a>").attr("href", "#")
.text(name)
.appendTo($li);
}
You could use it as follows:
<div data-role="page" id="searchPage" data-theme="b">
<div data-role="content">
<ul data-role="listview" data-filter="true" data-theme="b" id="searchListUl">
</ul>
</div>
<script type="text/javascript">
add("Apple");
$("#searchListUl").listview('refresh');</script></div>
It should be noted that using .append() in the way described in many of these answers will directly vulnerable to Cross Site Scripting (XSS) attacks. If strings are being pulled from a data source that can be influenced by users, then a user can put raw HTML, and when .append() is called, the raw HTML will get directly injected into the DOM. Script tags are especially dangerous. Once you can run arbitrary javascript in someones session, an attacker can grab their cookies, their private information, and even passwords in some circumstances, all without the victim knowing.
Always ALWAYS use .text() to set the text of an element. In jquery, .text() will properly HTMLEncode characters when it needs to. If you have experience with SQL, you can think of .append($RANDOM_STRING) the same way that you would think of conn.exec($RANDOM_STRING). By their very nature, they are vulnerable to injection and should be avoided at all costs.
I'm sure code from these examples have already been copied and pasted, and are now in use all over the internet, and that sucks. Please get the word out, .append() will append HTML, not just text, so improper use will let attackers inject raw HTML into your DOM.
I have some code which sets the html() of a div to be the result of a Mustache call against a particular piece of JSON
function render_bugs(json_file){
$.getJSON(json_file, function(json) {
$("#bug-list").html(Mustache.to_html($('#template').html(), json));
The first time the function is called, it is fine. However when I call it with a different "json_file". It fails with the following error:
haystack is null
return haystack.indexOf(this.otag + needle) != -1;
I initially thought the problem was with the JSON file so I swapped the paramter names around but that's not it, the json is fine.
I then added a test line where I just set the html before the mustache call, like so:
function render_bugs(json_file){
$.getJSON(json_file, function(json) {
$('#bug-list').html('<p>foo</p>');
$("#bug-list").html(Mustache.to_html($('#template').html(), json));
And it doesn't work at all.
It's like Mustache doesn't want to work if the element it is being rendered to has content in it already
Is there any way around this?
Worked it out
This was the markup for the page:
<div id="bug-list" />
<div id="template">
<p><strong>{{bugs}}</strong> bugs found in mingle</p>
{{#repos}}
<h2>{{repo-name}}</h2>
<ol>
{{#hotspots}}
<li>
<p class="file-name"><a>{{score}} - {{file}}</a></p>
<ol>
<li><em><span class="line-number">{{lines}}</span> lines of code</em></li>
<li><em><span class="traits">{{traits}}</span> traits mixed in</em></li>
{{#commits}}
<li>
<strong>{{date}}</strong> - {{message}}
<div class="code">{{{change}}}</div></li>
{{/commits}}
</ol>
</li>
{{/hotspots}}
</ol>
{{/repos}}
</div>
Turns out me closing the "bug-list" div with the xml short-hand doesn't play nice. I'm guessing it assumed that I hadn't closed it properly and so the template div lived inside bug list. Therefore the first render call would overwrite the template and that would make the second call fail.
By changing the first line to
<div id="bug-list"></div>
It worked
Stupid browsers