Here's my javascript code:
<script type="text/javascript">
$(document).ready(function() {
var currentInput = '';
var currentLabel = '';
$("#userLookup").click(function() {
var url = "<%= Url.Action( "ListAll" , "User") %>";
currentInput = $("#User");
currentLabel = $("#lblUser");
openModal(url);
return false;
});
$("#locationLookup").click(function() {
var url = "<%= Url.Action( "ListAll" , "Location") %>";
currentInput = $("#Location");
currentLabel = $("#lblLocation");
openModal(url);
return false;
});
$(".selectable").live("click", function(e){
currentInput.val($(this).attr('id'));
var labelText = $(this).parent().parent().find('td').eq(2).html();
currentLabel.html(labelText);
$.fn.colorbox.close();
e.preventDefault();
});
});
function openModal(url){
$.fn.colorbox({
href:url
, open:true
, width: "400px"
, height: "300px"
});
}
</script>
And here's my HTML
<table width = "100%">
<tr>
<td>Asset User Id:</td>
<td><%= Html.TextBox("User", Model.User, (object)new{style = "width:100px", maxLength="20"})%>
<a id="userLookup" href="#">Lookup User</a>
<label id="lblUser"></label>
<%=Html.ValidationMessage("User")%></td>
</tr>
<tr>
<td>Location Id:</td>
<td><%= Html.TextBox("Location", Model.Location, (object)new{style = "width:100px", maxLength="20"})%>
<a id="locationLookup" href="#">Lookup Location</a>
<label id="lblLocation"></label>
<%=Html.ValidationMessage("Location")%></td>
</tr>
</table>
I have plenty of more lookup fields (which i omitted) similar to the two listed above and i was looking to see if anyone could help me come up with a cleaner/dry method of doing something like this?
Thanks
I would add the url for the modal box into the link itself. Then you could just add a class to that link to invoke the required functionality. It also means that you can have your JS in an external file and your JS doesn't have a dependence on the ASP.NET MVC Html helper methods.
Change your html to something like:
<table width = "100%">
<tr>
<td>Asset User Id:</td>
<td>
<%= Html.TextBox("User", Model.User, (object)new{style = "width:100px", maxLength="20"})%>
<%= ActionLink("Lookup User", "ListAll", "User", null, new { class="lookup-link" }) %>
<label id="lblUser"></label>
<%=Html.ValidationMessage("User")%>
</td>
</tr>
<tr>
<td>Location Id:</td>
<td>
<%= Html.TextBox("Location", Model.Location, (object)new{style = "width:100px", maxLength="20"})%>
<%= ActionLink("Lookup Location", "ListAll", "Location", null, new { class="lookup-link" }) %>
<label id="lblLocation"></label>
<%=Html.ValidationMessage("Location")%>
</td>
</tr>
</table>
Then you can simplify your jQuery to something like:
var currentInput = '';
var currentLabel = '';
$(".lookup-link").click(function() {
var url = $(this).attr("href");
currentInput = $(this).siblings("input")[0];
currentLabel = $(this).siblings("label")[0];
openModal(url);
return false;
});
I haven't tested any of this code, so there's probably a million errors. ;-)
HTHs,
Charles
You could have a sort of jquery plugin that attaches the click handlers for userLookup and locationLookup. It would probably take 3 arguments:
url
input
label
otherwise, you could have a function that takes 4 (the first being the item to bind the click handler to) and runs the exact code you have above.
Just don't go over the top. If you start adding more arguments for one-off solutions (e.g. boolean whether to show modal with "x" or "close" at the top), then probably you're just complicating things.
If you use a convention for naming all of your lookup elements you could create a general funciton that would work for all instances.
Something like:
OpenLookupModal(lookupId)
{
var inputName = lookupId.id.substr(0, lookupId.id.indexOf('Lookup'); //take all of the id before the word "lookup"
var currentInput = $("#" + inputName ));
var currentLabel = $("#lbl" + inputName);
openModal(url);
return false;
}
To use it:
$("#locationLookup").click(OpenLookupModal("#locationLookup"));
You could even get fancy and bind the click event of all id's ending in "Lookup" in one statement:
$("[id$='lookup']").click(OpenLookupModal(this));
Warning: This code is untested, but hopefully it gets the idea across.
Related
I'm an experienced C# and MVC programmer, but just starting out with AngularJS. I've been using ngTable (trying to anyway) with limited success, but one issue I cannot resolve - no matter what I do, I cannot get a particular table to refresh when the data has changed.
I'm getting data from a Web API via a factory - it's a list of suppliers associated with a particular Product. The first time the screen is loaded, the data is brought back and the table displays fine - any subsequent call, the data is returned, but the table is not updating. Other areas of the page are updating as expected.
I've done some console logging, and can see that the data is coming back. I've tried the tableParams.reload() function, and setting the tableParams.total() but nothing seems to trigger the table to refresh.
This is my function in the Controller:
function getStockSupplier() {
console.log("getStockSupplier()");
$scope.stockSupplierTableParams = {};
stockSupplier.getAll({ url: "localhost:52457", sku: $scope.model.sku })
.$promise.then(function (response) {
$scope.stockSupplier = response;
$scope.stockSupplierTableParams = new NgTableParams({
}, {
dataset: response
});
console.log("Got result");
console.log("Length: " + $scope.stockSupplierTableParams.settings().dataset.length);
$scope.stockSupplierTableParams.reload();
}, function (response) {
alert('no stock item');
$scope.stockSupplier = null;
});
}
And this is my HTML code:
<div ng-controller="stockController">
<div>
<table ng-table="stockSupplierTableParams" class="table table-condensed table-bordered table-striped">
<tr ng-repeat="issue in $data">
<td data-title="'Supplier1'">
{{issue.SupplierName}}
</td>
<td data-title="'On Order'">
{{issue.OnOrder}}
</td>
</tr>
</table>
</div>
</div>
I'm at a complete loss - it may be something fundamental I'm missing, but it's driving me crazy, so any help great appreciated.
EDIT: Here's the code for the Web API Service call, in case that has any relevance. Also, I should point out that the HTML above is used in a Directive, if that makes any difference.
var myService = angular.module('myService', ['ngResource']);
myService.factory('stockSupplier', [
'$resource',
function ($resource) {
return $resource('http://:url/api/StockInfo?Sku=:sku&StockSupplier=true',
{
url: '#url',
sku: '#sku'
},
{
getAll: {
method: 'GET',
isArray: true
},
});
}
]);
I have a simple solution, you can re-render table when data loaded:
HTML
<div ng-controller="stockController">
<div data-ng-if="tableIsReady">
<table ng-table="stockSupplierTableParams" class="table table-condensed table-bordered table-striped">
<tr ng-repeat="issue in $data">
<td data-title="'Supplier1'">
{{issue.SupplierName}}
</td>
<td data-title="'On Order'">
{{issue.OnOrder}}
</td>
</tr>
</table>
</div>
<div data-ng-if="!tableIsReady">
// you can put loader here
</div>
</div>
JS
function getStockSupplier() {
console.log("getStockSupplier()");
$scope.stockSupplierTableParams = {};
$scope.tableIsReady = false; // here we hide table, and show loader
stockSupplier.getAll({ url: "localhost:52457", sku: $scope.model.sku })
.$promise.then(function (response) {
$scope.stockSupplier = response;
$scope.stockSupplierTableParams = new NgTableParams({
}, {
dataset: response
});
console.log("Got result");
console.log("Length: " + $scope.stockSupplierTableParams.settings().dataset.length);
$scope.stockSupplierTableParams.reload();
$scope.tableIsReady = true; // here we show table again
$scope.$apply() // start digest
}, function (response) {
alert('no stock item');
$scope.stockSupplier = null;
});
}
Otherwise, you code is fine, you just may be forget put $scope.$apply() after $scope.stockSupplierTableParams.reload();
I got it working! Though I'm not 100% sure how, so if any experienced Angular developers could explain it, I would be grateful. There are two parts to it.
Basically - instead of setting the dataset to the response object of the API call, I set it to the variable $scope.stockSuppler. Then, I explicitly empty this variable before the update - code below:
function getStockSupplier() {
console.log("getStockSupplier()");
$scope.tableIsReady = false;
$scope.stockSupplierTableParams = {};
$scope.stockSupplier = [];
stockSupplier.getAll({ url: "localhost:52457", sku: $scope.model.sku })
.$promise.then(function (response) {
$scope.stockSupplier = response;
$scope.stockSupplierTableParams = new NgTableParams({
}, {
dataset: $scope.stockSupplier
});
console.log("Got result");
console.log("Length: " + $scope.stockSupplierTableParams.settings().dataset.length);
$scope.stockSupplierTableParams.reload();
$scope.tableIsReady = true;
}, function (response) {
alert('no stock item');
$scope.stockSupplier = null;
});
}
Also, I removed the ngController="stockController" from the directive HTML code - the directive is called in a div which already defined the controller. Having the controller defined twice appears to have been confusing it as well. I should emphasis that only be making both of these changes, I got to to work. One or the other only, it still doesn't work correctly.
The table is now updating as expected with the other parts on the page.
I'm not sure why using the scope variable rather than the response from the API makes a difference, but it's done the trick.
I'm relatively new with aspx and just tried to start moving my html page with youtube api with javascript into visual studio and aspx in order to move data into a database. In html the page seems to work fine and the next page button will successfully pass the pageToken. However, when I moved my html code to aspx, I noticed the page seems to refresh and drops my next page token, reloading the same front page. It also gives me a warning code that only occurs in aspx and not the html:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.
Setting 'XMLHttpRequest.withCredentials' for synchronous requests is deprecated.
I am not adding any scripts using my jquerys, which I believe is the main cause of this warning. My nextPage function however does recall the api for additional requests.
In terms of moving the html to aspx all I do is copy all my html code and put it into the header and body respectively. Am I missing a step in migrating? Do I have to change my code when using aspx instead of html?
Here is my current ASPX page:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %>
<DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<link href="StyleSheet1.css" rel="stylesheet" />
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="JavaScript1.js"></script>
<script type="text/javascript" src="https://apis.google.com/js/client.js?onload=onClientLoad"></script>
<style type="text/css">
#courses {
width: 566px;
}
.auto-style1 {
width: 556px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<div id="video-container">
<table><tr>
<td class="auto-style1">
<h1> Rutgers Accounting Digital Library Directory </h1>
</td>
<td align="right"><div id="summary"></div></td>
</tr>
<tr><td class="auto-style1"><form action="search_results.asp" method="Post"><input type="text" name="search1" placeholder="Search Here" size="20"><input type="Submit" name="Submit" value="Submit"></form></td><td><form method="link" action="my_uploads.html" ><input type="Submit" name="Clear" value="Clear Filters"></form></td></tr>
<tr><td colspan="2">
<form>
<select id="courses" onchange="show()">
<option selected disabled>Select a Course</option>
</select>
</form>
</td></tr>
<div class="button-container">
<tr>
<td class="auto-style1"><button id="prev-button" class="paging-button" onclick="previousPage();">Previous Page</button></td>
<td align="right"><button id="next-button" class="paging-button" onclick="nextPage();">Next Page</button></td>
</tr>
<tr>
<table id="results"></table>
</tr>
</table>
</div>
</div>
</form>
</body>
</html>
Here is my javascript page:
// Define some variables used to remember state.
var playlistId, nextPageToken, prevPageToken, status, cid;
var totalr = 0;
var rpp = 0;
var sum;
var dur = '';
function onClientLoad() {
gapi.client.load('youtube', 'v3', handleAPILoaded);
}
// After the API loads, call a function to get the uploads playlist ID.
function handleAPILoaded() {
gapi.client.setApiKey('APIKEY');
requestUserUploadsPlaylistId();
}
// Call the Data API to retrieve the playlist ID that uniquely identifies the
// list of videos uploaded to the currently authenticated user's channel.
function requestUserUploadsPlaylistId() {
// See https://developers.google.com/youtube/v3/docs/channels/list
var request = gapi.client.youtube.channels.list({
part: 'contentDetails',
forUsername: 'rutgersweb'
});
request.execute(function (response) {
cid = response.result.items[0].id;
dropdown(cid);
playlistId = response.result.items[0].contentDetails.relatedPlaylists.uploads;
requestVideoPlaylist(playlistId);
});
}
// Retrieve the list of videos in the specified playlist.
function requestVideoPlaylist(playlistId, pageToken) {
console.log(pageToken);
$('#results').html('');
console.log(pageToken);
var requestOptions = {
playlistId: playlistId,
part: 'snippet',
maxResults: 50
};
if (pageToken) {
requestOptions.pageToken = pageToken;
}
var request = gapi.client.youtube.playlistItems.list(requestOptions);
request.execute(function (response) {
totalv = response.pageInfo.totalResults;
rrp = response.pageInfo.resultsPerPage;
sum = '<td>Results Per Page: ' + rrp + '</td><td>Total Pages: ' + Math.ceil(totalv / rrp) + '<td>Total Videos: ' + totalv + '</td>';
$(sum).replaceAll('#summary');
// Only show pagination buttons if there is a pagination token for the
// next or previous page of results.
nextPageToken = response.result.nextPageToken;
var nextVis = nextPageToken ? 'visible' : 'hidden';
$('#next-button').css('visibility', nextVis);
$('#next-button2').css('visibility', nextVis);
prevPageToken = response.result.prevPageToken
var prevVis = prevPageToken ? 'visible' : 'hidden';
$('#prev-button').css('visibility', prevVis);
$('#next-button2').css('visibility', nextVis);
var playlistItems = response.result.items;
if (playlistItems) {
$.each(playlistItems, function (index, item) {
displayResult(item.snippet);
});
} else {
$('#results').html('Sorry you have no uploaded videos');
}
});
}
function getVideoDetails(mmp, ddp, yyyyp, dur, vidId, videoTitle, used) {
var request = gapi.client.youtube.videos.list({
part: 'contentDetails',
id: vidId
});
request.execute(function (response) {
var str = JSON.stringify(response.result.items[0].contentDetails.duration);
str = str.replace(/"/g, "");
str = str.replace(/PT/g, "");
str = str.replace(/H/g, ":");
str = str.replace(/M/g, ":");
str = str.replace(/S/g, "");
str = str.split(':');
var ftime;
if (str[1] < 10) {
ftime = str[1];
str[1] = '0' + ftime;
}
if (str[2] < 10) {
ftime = str[2];
str[2] = '0' + ftime;
}
displayOutput(mmp, ddp, yyyyp, str, vidId, videoTitle, used);
});
}
// Create a listing for a video.
function displayResult(videoSnippet) {
var videoTitle = videoSnippet.title;
var videoId = videoSnippet.resourceId.videoId;
var videoDescription = videoSnippet.description;
var videoPub = videoSnippet.publishedAt;
//dur =
//PublishedAt Formatting to compare
ar3 = videoPub.split("T");
uselessd = ar3[0];
re = new RegExp("-", "g");
uselessd2 = uselessd.replace(re, "/");
usedd = uselessd2.split("/");
ddp = usedd[2];
mmp = usedd[1];
yyyyp = usedd[0];
//Description trimming to leave time stamps only
ar = undefined;
useless = undefined;
useless2 = undefined;
used = undefined;
ar2 = undefined;
ar = videoDescription.split("Time Stamps:");
useless = ar[0];
useless2 = ar[1];
if (useless2 != undefined) {
ar2 = useless2.split("Summary");
ar = ar2[0];
re = new RegExp("\n", "g");
useless = ar.replace(re, "<br>");
used = useless;
}
else
used = " ";
// outputArray=[mmp, ddp, yyyyp, dur, videoId, videoTitle,used]
// displayOutput(outputArray);
getVideoDetails(mmp, ddp, yyyyp, dur, videoId, videoTitle, used)
}
function displayOutput(mmp, ddp, yyyyp, dur, videoId, videoTitle, used) {
//output = '<tr><td colspan="3" align="right">Published On: '+oarray[0]+'/'+oarray[1]+'/'+oarray[2]+'<br>'+dur+'</td></tr><tr><td><img src="http://img.youtube.com/vi/'+oarray[4]+'/1.jpg"></img></td><td>'+oarray[5]+'</td></tr><tr><td colspan="2" align="right">'+oarray[6]+'<hr>';
if (dur[2])
output = '<tr><td colspan="3" align="right">Published On: ' + mmp + '/' + ddp + '/' + yyyyp + '<br>Length: ' + dur[0] + ':' + dur[1] + ':' + dur[2] + '</td></tr><tr><td><img src="http://img.youtube.com/vi/' + videoId + '/1.jpg"></img></td><td>' + videoTitle + '</td></tr><tr><td colspan="2" align="right">' + used + '<hr>';
else
output = '<tr><td colspan="3" align="right">Published On: ' + mmp + '/' + ddp + '/' + yyyyp + '<br>Length: ' + dur[0] + ':' + dur[1] + '</td></tr><tr><td><img src="http://img.youtube.com/vi/' + videoId + '/1.jpg"></img></td><td>' + videoTitle + '</td></tr><tr><td colspan="2" align="right">' + used + '<hr>';
//Append to results listStyleType
$('#results').append(output);
}
// Retrieve the next page of videos in the playlist.
function nextPage() {
console.log(nextPageToken);
requestVideoPlaylist(playlistId, nextPageToken);
}
// Retrieve the previous page of videos in the playlist.
function previousPage() {
requestVideoPlaylist(playlistId, prevPageToken);
}
function dropdown(cid) {
var requestOptions = {
channelId: cid,
part: 'snippet',
maxResults: 50
};
var request = gapi.client.youtube.playlists.list(requestOptions);
request.execute(function (response) {
var plists = response.result.items;
if (plists) {
$.each(plists, function (index, item) {
var drp = document.getElementById("courses");
var optn = document.createElement("OPTION");
optn.text = item.snippet.title;
optn.value = item.id;
drp.add(optn);
});
} else;
});
}
function show() {
var e = document.getElementById('courses');
var txt = e.options[e.selectedIndex].value;
requestVideoPlaylist(txt);
}
I know the code is a little messy, still trying to get better coding habits, and I should probably fix some useless variables.
Set the type="button" on your button controls to prevent them from submitting the page each time they are pressed:
For example, in your ASPX page use:
<td class="auto-style1"><button type="button" id="prev-button" class="paging-button" onclick="previousPage();">Previous Page</button></td>
<td align="right"><button type="button" id="next-button" class="paging-button" onclick="nextPage();">Next Page</button></td>
I have something like this:
<template name ="products">
<br />
<h2>Products</h2>
<table>
<tr>
<td>Name</td>
<td>Price</td>
</tr>
{{#each products.items}}
<tr>
<td>{{name}}</td>
<td>{{price}}</td>
</tr>
{{/each}}
<tr>
<td>Total:</td><td>{{products.totalPrice}}</td>
</tr>
</table>
</template>
Template.products.helpers({
products: function () {
try {
var user = Session.get("user");
return JSON.parse(localStorage[user]); //this return*
} catch (e) {
}
}
});
*this returns something like this {totalPrice: 30, items:[{"productId1","name1","10"},{"productId2","name2","20"}]}
The question is: I need to have this info stored in localStorage and not in a Meteor.Collection as i dont want to go to the server until moment X (Doesn't matter really). But I cant make this thing auto update whenever I change localStorage value. Is there any way to do this?
Thanks in advance.
That's what Dependencies are for. Simplest example:
var array = [];
var arrayDep = new Deps.Dependency();
Template.name.helper = function() {
arrayDep.depend();
return array;
};
var change = function() {
// do things to Array contents
arrayDep.changed();
};
Store the info in the Session as it is reactive so your template will change every time the value in the Session changes.
You could also use the browser-store package that seems to make localstorage reactive.
I using the following function to delete a row in a table
//delete individual row
jQuery('.stdtable img.delete').click(function(){
var c = confirm('Continue delete?');
if(c) jQuery(this).parents('tr').fadeOut(function(){
jQuery(this).remove();
});
return false;
});
This code is in a separate js file and is common to all pages.
Now I would like to add an Ajax action that deletes the row from the database. But depending on which page I'm on, it must call different controller.
Example:
Product page must call delete in ProductController
ProductGroup page must call delete in ProductGroupController
How to handle this?
If you need some method of defining which controller to call, you could put a data attribute on the table. Something like this:
<table class="stdtable" data-remove-url="#Url.Action("DeleteRow", "MyController")">
<tr data-id="1">
AAA
<img class="delete" src="foo.jpg" />
</tr>
<tr data-id="2">
BBB
<img class="delete" src="foo.jpg" />
</tr>
</table>
Then in your jQuery you can get this value as the url parameter of your request, along with the id to delete.
jQuery('.stdtable img.delete').click(function(e) {
e.preventDefault();
if (confirm('Continue delete?')) {
var $el = $(this);
var $tr = $el.closest('tr');
var url = $el.closest('table').data('remove-url');
var id = $tr.data('id');
$tr.fadeOut(function() {
$el.remove();
$.post(url, { rowId = id }); // do the delete on the server
});
}
});
You could add a custom attribute to your table or row with contains the url off the controller you need to call. In your method you can read this custom attribute to get the url of your controller.Look here for the JQuery attr methode.
If I understand your question right, you want to click the image with class delete to make a call to delete that row in the database. The simplest solution would be
<img data-rowid="1" src="something.jpg"/>
jQuery('.stdtable img.delete').click(function(e){
e.preventDefault();
var c = confirm('Continue delete?');
var that = this;
if(c) {
var id = this.data("rowid");
jQuery.ajax({ url : "/resource/"+id, method : "DELETE"
statusCode: {
200: function() {
jQuery(that).parents('tr').fadeOut(function(){
jQuery(that).remove();
});
}
}});
}
return false;
});
In Asp.net mvc3 razor I have:
Ajax.ActionLink("Hello world", "Hello", "Say", new RouteValueDictionary(new { word = "Hello" }),new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "dynamic-container" })
it produces
<a href="...." ...>Hello world</a>
What I would like to get is
<a href="..." ...><my><html><content/></html></my></a>
How can I pass "" so that it will be inserted instead of the standard text?
I've just found a solution
http://forums.asp.net/post/4517653.aspx
It's not as elegant as I would expected but should do.
Anyone has a better idea?
the code:
Ajax.ActionLink("Hello world", "Hello", "Say", new RouteValueDictionary(new { word = "Hello" }),new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "dynamic-container" })
with the link provided results in a error. The link looks like this:
.../Say/Hello?Count=1&Keys=System.Collections.Generic.Dictionary%602%2BKeyCollection%5BSystem.String%2CSystem.Object%5D&Values=System.Collections.Generic.Dictionary%602%2BValueCollection%5BSystem.String%2CSystem.Object%5D
which is BAAADDDDD.... the issue is that this uses the wrong overload. Instead of:
new RouteValueDictionary(new { word = "Hello" })
where should be:
new { word = "Hello" }
when the code form the link in a previous answer works like a charm - hope it'll be helpful for somebody, anybody
You can use javascript / jQuery to force an insert into the item after rendering. This may be a bit 'quick and dirty' but it does get the job done without too much effort.
Attach a class using the HtmlAttributes object for each #Ajax.Action to allow you to select them later using JS:
#Ajax.ActionLink(..... new { #class = "AjaxLink btn btn-primary" })
Then use javascript / jQuery to find the link(s) by class, and replace the innerHTML of each. The following is a jQuery example, it could also be written in plain javascript. I personally like the jQuery was as it's more concise:
<script type="text/javascript">
//hack to insert a <span> inside the remove links so that the text can be styled
$('.AjaxLink ).html('<i class="fa fa-minus-circle"></i><span class="ActionLinkText">Your Text here</span>');
</script>
Hope this helps
The solution #4rchie pointing of using helper method is the best solution. But there is another way which keeps the data to be inserted with the link using a data-* attribute and use jquery to do rest of the work.
For example:
<div class="list-group">
#foreach (var item in Model.Things)
{
#Ajax.ActionLink(item.Name, "actionName", "controllerName", new { Id = item.Id }, new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "areaPanel", OnComplete = "" }, new { #class = "list-group-item", data_frequency = item.frequency})
}
</div>
Jquery code:
jQuery(document).ready(function () {
$("[data-frequency]").each(function () {
var linkText = $(this).html();
var attr = $(this).attr("data-frequency");
$(this).html("<span class ='badge'>" + attr + "</span>" + linkText);
})
}
The result is like http://getbootstrap.com/components/#list-group-badges