How do I reuse HTML User Interface elements? - javascript

I have the following HTML:
<div id="addFieldUI" style="display: none">
<div>
Field Name:
<input type="text" />
</div>
<div>
Field Value
</div>
<div>
Data Type:
<select>
<option>Price</option>
</select>
</div>
I would like to reuse the HTML in at least two other jQuery modals. If I use $('#otherElem').html($('#addFieldUI').html()) to insert the HTML into an HTML element I have the problem of duplicate elements if I use input ids for the fields. Should I rather use input names? Should I use a 'script' code block instead of a div? How do I create reusable HTML?
EDIT
I am aware of jQuery data templates but in this case I just want to reuse HTML within MODAL dialogs. E.g. if I used the same form for creating and editing data.

Something like this, perhaps:
var counter = 0;
$('#otherElem').html(
$('#addFieldUI')
.clone()
.attr('id', 'addFieldUI-' + counter++)
.html()
);

You can use a class name as a hook, and than before appending to the other div use Js to dynamically add IDs

There is a library called jQuery Templates and you can always give that a go, I used often in ASP.NET MVC, but works everywhere.
in Git you have some demos
example:
<script id="movieTemplate" type="text/x-jquery-tmpl">
<li>
<b>${Name}</b> (${ReleaseYear})
</li>
</script>
<ul id="movieList"></ul>
<script type="text/javascript">
var movies = [
{ Name: "The Red Violin", ReleaseYear: "1998" },
{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
{ Name: "The Inheritance", ReleaseYear: "1976" }
];
$( "#movieTemplate" ).tmpl( movies ).appendTo( "#movieList" );
</script>

Related

Trouble setting the HTML of a variable in JQuery

What I've done is loaded some HTML from a file and I am attempting to modify some elements within that HTML.
The initialization looks like this:
var id = player_info["ID"];
$("#main_container").append(
$("<div />").attr({class: "player_container", id: "player_" + id}).css("display", "none")
);
// Add all information to the player container
var player_container = $("#player_" + id);
player_container.load("player_layout.html");
With player_layout.html looking like this:
<div class="player_name">
</div>
<div class="player_chips">
Chips:
<br/>
<span class='bidding'></span>/<span class='chips'></span>
</div>
<div class="player_stats">
Wins / Losses
<br/>
<span class="wins"></span>/<span class="losses"></span>(<span class="total_games"></span>)
<br/><br/>
Chips Won / Chips Lost
<br/>
<span class="chips_won"></span>/<span class="chips_lost"></span>
</div>
<button class="player_won">Player Has Won</button>
I then want to modify some of the elements, specifically classes. An example of the way I was initially doing this is:
player_container.find(".player_name").text(player_info['username']);
This wasn't working so I then tried to switch find with children and text with html but that didn't seem to work. I then tried this:
$('> .player_name', player_container).html(player_info['username']);
but that also didn't work. I understand that I can use DOM to grab the childNodes and compare the class names but there are a lot of classes that need modifying and I'd also like to know if this is possible in JQuery. Thanks in advance for any help.
You need to use complete callback method of .load()
var player_container = $("#player_" + id);
player_container.load("player_layout.html", function(){
player_container.find(".player_name").text(player_info['username']);
});

AngularJS nested ng-repeat click & show/hide

I've done a ton of reading and research on this topic the past few days and have found some good answers, but for some of the answers I question performance and necessity.
My question pertains to nested ng-repeat scopes. I'm wondering what the best way to achieve an "add item" scenario for adding an item to the nested foreach.
My Code
My HTML is simply 2 ng-repeats and my goal is to be able to add an item to the second (nested) ng-repeat
<div ng-app="myApp">
<div class="nav" ng-controller="FoodsController as vm">
<div class="level1" ng-repeat="foods in vm.foodGroups">{{foods.Name}}
<button type="button" ng-click="vm.addNewFood()">add new food</button>
<div ng-show="vm.newFoodBeingAdded">
<input type="text">
</div>
<div class="level2" ng-repeat="food in foods.FoodsInGroup">{{food.Name}}</div>
</div>
</div>
</div>
My Angular controller looks like this:
app.controller('FoodsController', function () {
var vm = this;
vm.foodGroups = [{
"Name": "Grains",
"FoodsInGroup": [{
"Name": "Wheat"
}, {
"Name": "Oats"
}]
}, {
"Name": "Fruits",
"FoodsInGroup": [{
"Name": "Apple"
}, {
"Name": "Orange"
}]
}];
vm.newFoodBeingAdded = false;
vm.addNewFood = function () {
vm.newFoodBeingAdded = true;
};
});
What should happen
The general work flow would be a user clicks an Add New button and it shows a text box with a "save" button. The text box & button would be within the parent foreach. Once a user saves the item it would then be added to the nested foreach (that logic isn't shown).
The issue
The issue is that when I click "Add New Food" (which should just show 1 of the text boxes & save buttons), all of the text boxes show. How do I ensure I am "scoping" this correctly and that only the text box/button within that parent are shown?
Possible solution
One answer I found was to create a child controller for each nested item. For example I'd have a FoodGroupsController which would manage all the logic for the nested foreach (because there will be a lot more going on than just adding a new item in a real app, so it could be justified).
jsFiddle
Here's a jsFiddle with the code that currently does not function correctly.
There is the forked Fiddle
I made just few changes. The fact is that you were binding the ng-show with a single var in your controller. It was a show me all or show me nothing possibility.
So the fix it, you have to bind this, in your food item, not in the controller himself.
Html :
<button type="button" ng-click="vm.addNewFood(foods)">add new food</button>
<div ng-show="foods.newFoodBeingAdded" class="add-new-food">
<input type="text" placeholer="add a new food">
<button type="button">save new food</button>
</div>
Controller :
vm.addNewFood = function (foods) {
foods.newFoodBeingAdded = true;
};
With this code, you pass the food in param of your function, so you can change the boolean of your food only. And then, your ng-show is just binding on this boolean.

Filtering data in Javascript

I have a list of elements (DIVs) on my html page as below.
There is a list of hashtags on the same page.
I need that when the user clicks on the hashtag (e.g. #bacteria) only those DIVs that contain that hashtag are shown.
What would be the most lightweight and easy way to implement that?
<div class='entry'>
<p>#antibiotics destroy #e-coli and that's not good!!!!</p>
<!-- Hashtags: #antibiotics #eColi -->
<!-- UID: 755a2a60-972e-11e3-a464-872f2fc4dea2 -->
</div>
<div class='entry'>
<p>#bacteria can be #friendly, such as #e-coli for example</p>
<!-- Hashtags: #bacteria #friendly #eColi -->
<!-- UID: 6cc66d00-972e-11e3-a464-872f2fc4dea2 -->
</div>
<div class='entry'>
<p>#antibiotics can fight #bacteria</p>
<!-- Hashtags: #antibiotics #bacteria -->
<!-- UID: b37992c0-9686-11e3-8b2c-c97ae6645b3b -->
</div>
I know that Angular is powerful for this kind of stuff, but I'd like to use something lightweight and easy. Like maybe it's possible to do it with jQuery or something...
FYI the whole thing runs on Node.Js / Express.Js with EJS rendering.
Thank you!
UPDATE
Suppose now I have several hashtags I need to check for. Like as if contains variable is not a string but an array and i need to only show entries that contain ALL of this array's values. How would I change the code? Trying to do that, but can't manage... Thank you so much!
Use the :contains jquery selector
$(document).ready(function(){
$('.entry').hide();
$('.links').on('click','a',function(e){
var $ctx = $(e.target);
var contains = $ctx.text();
$('.entry').hide();
$('.entry:contains('+contains+')').show();
return false;
});
});
Sample : http://jsfiddle.net/LA3tD/
EDIT
you can use text with commas and then split, or use data attribute with some separator and split it afterwards for a concatenated filter selector
$(document).ready(function(){
$('.entry').hide();
$('.links').on('click','a',function(e){
var $ctx = $(e.target);
var contains = $ctx.text();
$('.entry').hide();
if(contains.indexOf(',')!=-1){
var tags = contains.split(',');
var filt = '';
$.each(tags,function(i,el){
filt += ':contains('+el+')';
});
// :contains can be concatenated multiple times f.e.: ":contains(foo):contains(bar)"
$('.entry'+filt).show();
}else{
$('.entry:contains('+contains+')').show();
};
return false;
});
});
Updated sample: http://jsfiddle.net/LA3tD/1/
Ideally, you'd incorporate your hash-tag data into the divs themselves...perhaps with the data-* attribute:
<div class='entry' data-hashtags='antibiotics bacteria'>
Then via jQuery you could loop through them hiding the ones that don't match:
var clickedHashtag = x //get the clicked on hashtag however you like
$('.entry').each(function(){
if($(this).data('hashtags').indexOf(clickedHashtag)>=0){
$(this).hide()
}
})
Fiddle: http://jsfiddle.net/Jz3gZ/
Untested:
$('.entry').each(function(item) { $(item).show($item.is('.' + hashtag)); });
You would have to add the hashtag as a class, of course:
<div class="entry antibiotics">

How to create html dynamically using jquery

I want to create a html page dynamically according to a JSON object and I implemented a method and its working fine my current code is given below
$(function(){
var result =JSON.parse(response);
var markup="";
for (var i=0;i<result.length;i++) {
markup+="<div id='nameDiv'>"+result[i].name+"</div>";
}
$('.divLoadData:last').after(markup);
});
But my actual markup is like this
<div class="divLoadData">
<div class="name" id="nameDiv">
Name
</div>
<div class="numberb">
<div id="age">
Age
</div>
</div>
<div class="numberb dob">
<div id="dob">
DOB
</div>
</div>
</div>
and it will grow eventually so my current method is not capable for creating this kind markup so is there any other way to do the same.
I think jQuery Tmpl (jQuery Template) is what you need.
https://github.com/BorisMoore/jquery-tmpl
You can just setup the template then bind it with JSON data. It will build the html for you.
for simple example
$.tmpl( "<li>${Name}</li>", { "Name" : "John Doe" }).appendTo( "#target" );
suit your case example
var movies = [
{ Name: "The Red Violin", ReleaseYear: "1998" },
{ Name: "Eyes Wide Shut", ReleaseYear: "1999" },
{ Name: "The Inheritance", ReleaseYear: "1976" }
];
var markup = "<li><b>${Name}</b> (${ReleaseYear})</li>";
// Compile the markup as a named template
$.template( "movieTemplate", markup );
// Render the template with the movies data and insert
// the rendered HTML under the "movieList" element
$.tmpl( "movieTemplate", movies )
.appendTo( "#movieList" );
Hope this help.

dynamic id ng-repeat

I am trying to set a dynamic id to my div within my ng-repeat. Let me show an example.
<div id="$index" ng-repeat="repeat in results.myJsonResults">
<div id="$index" ng-click="saveID($index)" ng-repeat="subRepeat in results.myJsonResults.subresults">
</div>
My problem is, when I click on my child div, I want to get my parent id name, but looks like angular doesn't set the ID properly to the div. Is it possible to set a dynamic ID in this concept?
PS: I tried, in the past, create a counter method on my controller and set an index, but it turns out that angular only recognizes the last value of this ID instead.
To answer your question, try this:
<div id="{{$index}}" ...>
While the above should work, this might be not what you want really (!). Please note that this is rather rare with AngularJS to manipulate elements by referring those by id.
You should focus on your model, declarative describe UI and let AngularJS do the rest without doing low-level DOM manipulations "by hand".
A use case I can think of is associating <label> elements with their respective <input> elements, as seen in http://jsfiddle.net/YqUAp/
Applied there is a pkozlowski.opensource's method
<label for="{{ 'textField-' + $index }}">Option {{ $index }}</label>
<input type="text" id="{{ 'textField-' + $index }}" ng-model="field"/>
Though I'm unsure if this is the most effective method. (Even if only for readability)
<div id="{{$index}}" ...>
Works well, but you can also put a dynamic id with the field repeated if - for instance - subRepeat would have an id field.
That would be:
<div id="subRepeat{{subRepeat.id}}" ...>
That will put ids like subRepeat1, subRepeat2, ... on your repeated div
you need to hold the index in your objects
results = { myJsonResults : [
{ name: "abc", id: 1, subResults: [
{ subName: "123", id: 1 },
{ subName: "456", id: 2 }] }
{ name: "xyz", id: 2, subResults: [
{ subName: "789", id: 1 },
{ subName: "098", id: 2 }] }
] };
Also your repeat refers to results, which disconnects them, instead use thisResult:
<div id="thisResult.id" ng-repeat="thisResult in results.myJsonResults">
{{ thisResult.name }}
<div id="subResult.id" ng-click="saveID(subResult.id)" ng-repeat="subResult in thisResult.subResults"> {{ subResult.subName }} </div>
</div>

Categories