I am working on a small application that displays a "users" JSON in an HTML5 table. I use Bootstrap 3, Axios and Vue.js 2 for this purpose.
The items displayed are paginated. Here is the code for all that:
var app = new Vue({
el: '#app',
data: {
users: [],
loading: true,
errored: false,
url: "https://randomuser.me/api/?&results=100&inc=name,location,email,cell,picture",
page: 1,
perPage: 10,
pages: [],
},
methods: {
getUsers() {
axios
.get(this.url)
.then(response => {
this.users = response.data.results
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
},
setPages() {
var numberOfPages = Math.ceil(this.users.length / this.perPage);
for (var index = 1; index <= numberOfPages; index++) {
this.pages.push(index);
}
},
paginate(users) {
var page = this.page;
var perPage = this.perPage;
var from = (page * perPage) - perPage;
var to = (page * perPage);
return users.slice(from, to);
}
},
created() {
this.getUsers();
},
watch: {
users() {
this.setPages();
}
},
computed: {
displayedUsers() {
return this.paginate(this.users);
}
},
filters: {
lowercase(value) {
return value.toLowerCase();
},
capitalize(value) {
return value.charAt(0).toUpperCase() + value.slice(1);
}
}
});
.table-container {
margin: 10px;
}
.table-container .panel-heading {
font-weight: bold;
}
.table-container .panel-body {
padding: 0;
}
.table-container table {
margin-bottom: 0;
border: none;
}
.table-container table tr:last-child td {
border-bottom: none;
}
.table-container table tr th {
font-weight: bold;
}
.table-container table tr th:first-child,
.table-container table tr td:first-child {
border-left: none;
}
.table-container table tr th:last-child,
.table-container table tr td:last-child {
border-right: none;
}
.table-container table tr td {
padding: 2px 8px !important;
vertical-align: middle;
}
.table-container table tr td .picture {
padding-right: 10px;
}
.table-container table tr td img {
max-height: 30px;
width: auto;
border: 1px solid #c7c7c7;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app" class="container">
<div class="panel panel-default table-container">
<div class="panel-heading">Users</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-bordered" id="dataTable">
<thead>
<tr>
<th class="text-right">#</th>
<th>Name</th>
<th>Email</th>
<th>City</th>
</tr>
</thead>
<tbody>
<tr v-for="(user, index) in displayedUsers">
<td class="text-right">{{perPage * (page - 1) + index + 1}}</td>
<td>
<span class="picture">
<img :src="user.picture.thumbnail" :alt="user.name.first + ' ' + user.name.last" class="img-circle">
</span>
<span>{{user.name.first | capitalize}} {{user.name.last | capitalize}}</span>
</td>
<td><a :href="'mailto:' + user.email | lowercase">{{user.email | lowercase}}</a></td>
<td>{{user.location.city | capitalize}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<nav class="text-center" aria-label="Page navigation">
<ul class="pagination pagination-sm">
<li>
<a href="#" #click="page = 1" aria-label="First">
<span aria-hidden="true">«</span>
</a>
</li>
<li>
<a href="#" v-if="page != 1" #click="page--" aria-label="Previous">
<span aria-hidden="true">‹</span>
</a>
</li>
<li v-for="pageNumber in pages.slice(page-1, page+4)" :class="{'active': page === pageNumber}">{{pageNumber}}</li>
<li>
<a href="#" #click="page++" v-if="page < pages.length" aria-label="Next">
<span aria-hidden="true">›</span>
</a>
</li>
<li>
<a href="#" #click="page = pages.length" aria-label="Last">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/axios#0.18.0/dist/axios.min.js"></script>
I have ran into a problem after adding a search/filter functionality to he application. I have added this to the template/view:
<div class="panel-heading clearfix">
<h2 class="pull-left">Users</h2>
<div class="searchbox">
<input type="text" v-model="search" class="form-control" placeholder="Search...">
</div>
</div>
and replaced <tr v-for="(user, index) in displayedUsers"> with <tr v-for="(user, index) in searchResults">.
Then, to computed section of the script the script, I added:
searchResults() {
return this.users.filter((user) => {
return user.name.first.match(this.search);
});
}
The search (through names) works, but neither the entire JSON, nor the search results are paginated.
I have failed to make them work together. The broken application can be seen HERE.
What is missing?
In order to make the pagination work together with the filtering you need to combine the pagination logic with your searchResults in displayedUsers
displayedUsers() {
return this.paginate(this.searchResults);
},
Then you will need to use displayedUsers everywhere where you are interested the combined result, so in your template:
<tr v-for="(user, index) in displayedUsers">
There is one more thing to fix in your code: the number of pages currently always uses the original user count, which has to be updated to use the "current" user count:
setPages(users) {
this.pages.length = 0; //we need to clear the previously set pages
var numberOfPages = Math.ceil(users.length / this.perPage);
for (var index = 1; index <= numberOfPages; index++) {
this.pages.push(index);
}
},
And update the pages whenever the dispayedUsers are changed:
watch: {
displayedUsers() {
this.setPages(this.searchResults);
}
},
If you also want to reset the page when on search you just need to set the page in searchResults
searchResults() {
this.page = 1;
return this.users.filter((user) => {
return user.name.first.match(this.search);
});
}
Working JSFiddle.
Related
I've been trying to find ways to trigger the dropdown (of class "update-message-button") using jquery when I click in the blank spaces of the parent <td> (of class "message-row") and even when clicked on its corresponding <td> (class "message-type-row").
I have attached my jquery code, but the dropdown doesn't get triggered.
Thanks in advance
$(".message-row").on("click", function () {
$(".update-message-button").trigger("click");
return false;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<tr>
<td class="message-row" scope="row">
<div class="dropdown dropleft">
<a class="update-message-button" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{this.message}}
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item update-message-id" href="#" data-toggle="modal" data-target="#exampleModal" data-id="{{this._id}}">Update</a>
<a class="dropdown-item" href="#">Remove</a>
</div>
</div>
</td>
<td class="message-type-row">
{{this.messageType}}
</td>
</tr>
Your script works,
BUT since .update-message-button is within .message-row
a click within .message-row triggers your trigger which in turn triggers the parent on('click'), and enters into an endless loop.
You might have to rethink your design, or use maybe: event.stopPropagation() to stop the execution after the first click.
Edit:
This works:
$(document).on("click", ".message-row",function () {
$(".update-message-button").triggerHandler("click");
});
https://codepen.io/ron7/pen/VweGVyb
It says you're new...so welcome to Stack!
If you don't use a JavaScript Framework at your workplace...jQuery is a perfectly fine approach
In looking at your HTML & explanation...it isn't really "that clear" what you are trying to accomplish. And, if I am correct, I feel your approach has some issues. That said...I will "try" to stay within the boundaries of what I believe you are asking (and try not to change it too much).
It "looks" like you want to...
Display a "dropdown" (of sorts) when the parent is 'clicked'
Manage the "update & remove" options when chosen
In anayzing the HTML you provide...you may also be wanting to notify other components of the users choice (so I added that too).
SIDE NOTES:
I "compressed" the CSS & HTML down a bit for you
Message...is your 'Record Class'
MessagePublisher...is your 'HTML Template Class'
SubscriptionController...is the 'Subscriber Class' (this is optional)
logger...logs to the console (handy...but also optional)
Cheers!
var logger = (function () {
return {
log: function () {
if (console) {
var args = Array.prototype.slice.call(arguments);
console.log.apply(console, args);
}
}
}
})();
function Message(id, messageType) {
this.id = id;
this.messageType = messageType;
}
function MessagePublisher(record) {
var _record = record,
_$message = null,
_$messageOptions = null,
_$messageType = null,
_$dropdown = null,
_$updater,
_$destroyer;
return {
dataBind: function (ele) {
// Elements
_$message = $(ele);
_$messageOptions = $('.message-options', _$message);
_$messageType = $('.message-type', _$message);
_$dropdown = $('.dropdown', _$messageOptions);
_$updater = $('.update-message', _$dropdown);
_$destroyer = $('.destroy-message', _$dropdown);
// Values
_$messageType.text(_record.messageType);
_$updater.attr('data-id', _record.id)
_$destroyer.attr('data-id', _record.id)
// Events
_$messageOptions.on('click', this.on.click.messageOptions);
_$destroyer.on('click', { publisher: this }, this.on.click.removeMessage);
_$updater.on('click', { publisher: this }, this.on.click.updateMessage);
},
on: {
click: {
messageOptions: function (e) {
e.preventDefault();
if (!_$dropdown.is(':visible')) {
_$dropdown.show();
}
},
removeMessage: function (e) {
e.preventDefault();
$(e.data.publisher).trigger('destroy::record', [_record])
},
updateMessage: function (e) {
e.preventDefault();
$(e.data.publisher).trigger('update::record', [_record])
}
}
}
};
}
function SubscriptionController() {
return {
on: {
destroy: function (e, record) {
logger.log('DESTROY', [record]);
},
update: function (e, record) {
logger.log('UPDATE', [record]);
}
}
};
}
$(document).ready(function () {
// Subscriber
window.subscriber = new SubscriptionController();
// Publishers
var $messages = $('.message');
$.each($messages, function (index, ele) {
var id = index+1;
var record = new Message(id, 'Awesome Message Type');
var controller = new MessagePublisher(record);
controller.dataBind(ele);
// Apply Subscriptions
$(controller).on('destroy::record', window.subscriber.on.destroy);
$(controller).on('update::record', window.subscriber.on.update);
});
});
table { width: 600px; }
table tbody tr.message { }
table tbody tr.message td { border: 1px solid #e3e2e1; padding: 5px; }
table tbody tr.message td { width: 50%; }
.message { }
.message .message-options { cursor: pointer; }
.message .message-options .dropdown { display: none; }
.message .message-options .dropdown a { display: block; }
.message .message-options .dropdown .sub-menu { }
.message .message-options .dropdown .sub-menu .menu-item { cursor: pointer; }
.message .message-type { }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tbody>
<tr class="message">
<td valign="top" class="message-options">
<div class="dropdown">
<a class="dropdown-message">Please Choose an Option</a>
<div class="sub-menu">
Update
Remove
</div>
</div>
</td>
<td valign="top" class="message-type">
</td>
</tr>
</tbody>
</table>
Im working with Vue js and I have multiple lists rendered but i only want to select(highlight) one element. At the moment multiple items are highlighted with each click. Hope that makes sense. Here is my code snipets below
<template>
<div>
<div class='list-group'>
<a v-for='(product, idx) in adslCapped' class='list-group-item'
v-on:click='toggleActiveIndex(idx)'
:class="{'active': idx == activeIndex}" >
{{product.name}}
</a>
</div>
<div class='list-group'>
<a v-for='(product, idx) in fibre' class='list-group-item'
v-on:click='toggleActiveIndex(idx)'
:class="{'active': idx == activeIndex}" >
{{product.name}}
</a>
</div>
</div>
</template>
data: {
activeIndex: null
},
methods: {
toggleActiveIndex: function(index){
this.activeIndex = index
}
}
So as you can see I have two lists, but when I click on the first item of the first list, then it highlights the first item in both lists. PLease note that these are only code snippets in relation to the issue i'm having.
With your current app structure, if you want only one item to be highlighted among all the lists, you can add another variable which represents the active list.
Then you need to change the condition for the active class and check if the index is the active index AND if the list is the active list.
HTML
<div id="app">
<div class='list-group'>
<a v-for='(product, idx) in adslCapped' class='list-group-item'
v-on:click='toggleActiveIndex(adslCapped, idx)'
:class="{'active': idx == activeIndex && adslCapped == activeList}" >
{{product}}
</a>
</div>
<div class='list-group'>
<a v-for='(product, idx) in fibre' class='list-group-item'
v-on:click='toggleActiveIndex(fibre, idx)'
:class="{'active': idx == activeIndex && fibre == activeList}" >
{{product}}
</a>
</div>
</div>
Script
new Vue({
el: "#app",
data() {
return {
activeIndex: null,
activeList: null,
adslCapped: ['a', 'b', 'c'],
fibre: ['1244', '125215', '02150']
}
},
methods: {
toggleActiveIndex: function(list,index){
this.activeIndex = index;
this.activeList = list;
}
},
})
Demo here
Hope it helps!
I know this is overkill for this simple case but you can abstract list into a component, store selected products separately.
Take a look at the example.
var productList = Vue.component('product-list', {
props: {
value: Object,
products: Array
},
template: `
<div class="product-list"><a class="ist-group-item" :class="{'active': product.id === value?.id}" #click="selectProduct(product)" :key="product.id" v-for="product in products">{{product.name}}</a></div>`,
methods: {
selectProduct(product) {
this.$emit('input', product)
}
}
})
var app = new Vue({
el: "#app",
data() {
return {
selected1: null,
selected2: null,
products: Array(5).fill(0).map((pr, id) => ({
id: id + 1,
name: `Product ${id + 1}`
}))
}
},
mounted() {
//console.log(this.products)
},
methods: {}
});
.product-list {
border: 1px solid black;
padding: 5px;
margin-top: 5px;
}
.ist-group-item {
display: block;
transition: all .3s ease-in;
}
.ist-group-item:hover {
background: lightgray;
}
.ist-group-item.active {
background: black;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<product-list v-model="selected1" :products="products">
</product-list>
<product-list v-model="selected2" :products="products">
</product-list>
</div>
The issue is that activeIndex is only one variable - which can't be reused for setting the index in the both lists.
The issue is that - you should be able to separate which list you want to activate when clicking within the v-for loop.
new Vue({
el: "#app",
data: {
list1: [{
name: "Learn JavaScript",
done: false
},
{
name: "Learn Vue",
done: false
},
{
name: "Play around in JSFiddle",
done: true
},
],
list2: [{
name: "Learn JavaScript",
done: false
},
{
name: "Learn Vue",
done: false
},
{
name: "Play around in JSFiddle",
done: true
},
],
active1Index: null,
active2Index: null
},
methods: {
toggleActive: function(list, index) {
if (list === "list1") {
this.active1Index = index
} else {
this.active2Index = index
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
.active {
background: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class='list-group'>
<a v-for='(product, idx) in list1' class='list-group-item' v-on:click='toggleActive("list1", idx)' :class="{'active': idx == active1Index}">
{{product.name}}
</a>
</div>
<div class='list-group'>
<a v-for='(product, idx) in list2' class='list-group-item' v-on:click='toggleActive("list2", idx)' :class="{'active': idx == active2Index}">
{{product.name}}
</a>
</div>
</div>
Thanks everyone who answered you guys on stackoverflow are the best. Julia's solution is the one that worked for me.
I have to implement it throughout my whole app and lists now.
Thanks Julia
I have a table using jquery datatable plugin and for some reason the header appears like this, if i click sort or some other action the header automatically resizes to max-width.
here is the code
<div class="card-body">
<div class="container-fluid">
<div id="content" class="d-none">
<div class="table-responsive">
<table id="tblParametros" class="table custom-table text-center">
<thead>
<tr>
<th>Id</th>
<th>Referência</th>
<th>UAP</th>
<th>Nº de Dias</th>
<th>Nº de Turnos</th>
<th>Nº de PAB(s)</th>
<th>Alcance de Abastecimento</th>
<th>Quantidade Mínima</th>
<th>Quantidade Máxima</th>
<th></th>
</tr>
</thead>
</table>
</div>
<!-- Modal structure -->
<div id="modal" class="iziModal custom-modal" data-iziModal-fullscreen="true" data-iziModal-title="Parâmetro" data-iziModal-subtitle="Edição de parâmetros" data-iziModal-icon="fas fa-edit">
<section>
<!-- Modal content from partial view -->
<div class="container-fluid p-5 iziModal-content">
<partial name="_EditPartial" for="Parametro" />
</div>
</section>
</div>
</div>
<div id="loading" class="text-center">
<img src="~/images/ajax/loader-ellipsis.svg" />
</div>
</div>
</div>
js
$(document).ajaxStart(function () {
$("#loading").show();
$("#content").addClass('d-none');
}).ajaxStop(function () {
$("#loading").hide();
$("#content").removeClass('d-none');
});
$(function () {
var table = $('#tblParametros').DataTable({
scrollY: "60vh",
ajax: {
url: '#Url.Action("List","Parametros")',
dataSrc: ""
},
columns: [
{ data: "id" },
{ data: "referencia" },
{ data: "uap" },
{ data: "numDias" },
{ data: "numTurnos" },
{ data: "numPAB" },
{ data: "alcanceAbastecimento" },
{ data: "qtdMin" },
{ data: "qtdMax" },
{
data: "id",
render: function (data, type, row) {
return '<button class="btn" onclick="EditParametro('+data+')" data-izimodal-open="#modal" data-izimodal-transitionin="fadeInDown">' +
'<i class="fas fa-edit"></i>' +
'</button>';
}
}
]
});
});
and some custom styles i apply
.custom-table {
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
width:100%;
}
.custom-table > thead {
background-color: #3e5977;
color: white;
}
.custom-table > tbody {
background-color: #97c7fb;
color: white;
font-weight: bold;
}
.custom-table > tbody > tr > td > button {
background-color: #3e5977;
border-color: #ddd;
}
.custom-table > tbody > tr > td > button:hover {
background-color: #7191b4;
border-color: #ddd;
}
.custom-table > tbody > tr > td > button > svg {
color: #fdba9f;
}
.custom-table > tbody > tr:nth-child(even) {
background-color: #43515f75;
}
.custom-table > tbody > tr:nth-child(odd) {
background-color: #2c3b4b8f;
}
I don't understand why does it do this, i thought it was 'cause of the class "table-responsive" but wasn't the guilty one
Im just trying to build a basic site for linking tenants and properties, and I am coming into some issues with a collapsible web grid. This javascript comes from a tutorial, have repurposed it to fit my task. The issue is my javascript simply doesnt seem to be running. Find below my cshtml and also my shared layout.
Cshtml
#model IEnumerable<HousingProject.Models.Property>
#{
ViewBag.Title = "Index";
WebGrid grid = new WebGrid(source: Model, canSort: false);
}
<style type="text/css">
th, td {
padding: 5px;
}
th {
background-color: rgb(248, 248, 248);
}
#gridT, #gridT tr {
border: 1px solid #0D857B;
}
#subT, #subT tr {
border: 1px solid #f3f3f3;
}
#subT {
margin: 0px 0px 0px 10px;
padding: 5px;
width: 95%;
}
#subT th {
font-size: 12px;
}
.hoverEff {
cursor: pointer;
}
.hoverEff:hover {
background-color: rgb(248, 242, 242);
}
.expand {
background-image: url(/Content/themes/base/images/pm.png);
background-position: -22px;
background-repeat: no-repeat;
}
.collapse {
background-image: url(/Content/themes/base/images/pm.png);
background-position: -2px;
background-repeat: no-repeat;
}
</style>
<h2>Index</h2>
<p>
#using (Html.BeginForm("Index", "Property", FormMethod.Get))
{
#Html.TextBox("filter")
<input type="submit" value="Search" />
}
</p>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.HouseNumber)
</th>
<th>
#Html.DisplayNameFor(model => model.StreetName)
</th>
<th>
#Html.DisplayNameFor(model => model.Town)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.Postcode)
</th>
<th>
#Html.DisplayNameFor(model => model.MaxOccupancy)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.HouseNumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.StreetName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Town)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.Postcode)
</td>
<td>
#Html.DisplayFor(modelItem => item.MaxOccupancy)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
<div id="main" style="padding:25px; background-color:white;">
#grid.GetHtml(
htmlAttributes: new { id = "gridT", width = "700px" },
columns: grid.Columns(
grid.Column("HouseNumber", "House Number"),
grid.Column("StreetName","Street Name"),
grid.Column("Town", "Town"),
grid.Column("City", "City"),
grid.Column("Postcode", "Postcode"),
grid.Column("MaxOccupancy", "Max Occupancy"),
grid.Column("CurrentOccupancy", "Current Occupancy"),
grid.Column(format: (item) =>
{
WebGrid subGrid = new WebGrid(source: item.CurrentTenants);
return subGrid.GetHtml(
htmlAttributes: new { id = "subT" },
columns: subGrid.Columns(
subGrid.Column("FirstName", "First Name"),
subGrid.Column("Surname", "Surname"),
subGrid.Column("Age", "Age"),
subGrid.Column("Employer", "Employer")
)
);
})
)
)
</div>
<script type="text/javascript">
$(document).ready(function () {
var size = $("#main #gridT > thead > tr >th").size(); // get total column
$("#main #gridT > thead > tr >th").last().remove(); // remove last column
$("#main #gridT > thead > tr").prepend("<th></th>"); // add one column at first for collapsible column
$("#main #gridT > tbody > tr").each(function (i, el) {
$(this).prepend(
$("<td></td>")
.addClass("expand")
.addClass("hoverEff")
.attr('title', "click for show/hide")
//.attr('onclick', 'toggleWebGrid()')
);
//Now get sub table from last column and add this to the next new added row
var table = $("table", this).parent().html();
//add new row with this subtable
$(this).after("<tr><td></td><td style='padding:5px; margin:0px;' colspan='" + (size - 1) + "'>" + table + "</td></tr>");
$("table", this).parent().remove();
});
//by default make all subgrid in collapse mode
$("#main #gridT > tbody > tr td.expand").each(function (i, el) {
$(this).toggleClass("expand collapse");
$(this).parent().closest("tr").next().slideToggle(100);
});
});
//toggle expand and collapse
$(function () {
$("#main #gridT > tbody > tr td.collapse").on('click', function () {
alert('test');
$(this).toggleClass("expand collapse");
$(this).parent().closest("tr").next().slideToggle(100);
});
});
</script>
Shared Layout
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>#ViewBag.Title - My ASP.NET Application</title>
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
#Html.ActionLink("Application name", "Index", "Home", null, new { #class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>#Html.ActionLink("Home", "Index", "Home")</li>
<li>#Html.ActionLink("About", "About", "Home")</li>
<li>#Html.ActionLink("Contact", "Contact", "Home")</li>
<li>#Html.ActionLink("Properties","Index","Property")</li>
</ul>
#Html.Partial("_LoginPartial")
</div>
</div>
</div>
<div class="container body-content">
#RenderBody()
<hr />
<footer>
<p>© #DateTime.Now.Year - My ASP.NET Application</p>
</footer>
</div>
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
</body>
</html>
Im sure you guys will figure it out, been banging my head for a while now, cant even get the debugger to step into it so I'm sure its something wrong in config not the javascript. Please note the tabled part of the page is simply a legacy thing, I dont desperately want to remove it untill this new webgrid works. Ta.
I simply needed to move the
#Scripts.Render("~/bundles/jquery")
#RenderSection("scripts", required: false)
to head section of the sharedLayout. No idea why MVC defaults them to being at the bottom. Seems unhelpful.
I am trying to pass table id, div id and nested class names into a generic function that's used across the programme code.
The tricky part is, this function calls another function which is wrapped within an anchor tag's onclick event. Before adding the two additional parameters, the function removeItem worked and now no longer fires. These two parameters are required in order to make the function reusable.
The jsfiddle of the expected programme.
Both ids (the parameters) are passed as strings quoted,
var tableid = "'#tble1200'"
var otherid = "'div.class1 .total'"
function AddProduct(elment, event, tableid, otherid)
{
//do something
//the hyperlink is remove
remove: '<a href="#" class="remove"
onclick="removeProduct(this, event,tableid,otherid)">X</a>'
}
function removeProduct(element, event, tblid, othid)
{
//do something
$(tblid).datagrid('loadData', data);
$(othid).html('Total: $'+subTotal);
}
I am trying out the code from this post's jsfiddle here. : The original code.
function addProduct(name,price){
function add(){
for(var i=0; i<data.total; i++){
var row = data.rows[i];
if (row.name == name){
row.quantity += 1;
return;
}
}
data.total += 1;
data.rows.push({
name:name,
quantity:1,
price:price,
remove: 'X'
});
}
add();
totalCost += price;
$('#cartcontent').datagrid('loadData', data);
$('div.cart .total').html('Total: $'+totalCost);
}
For multiple carts to work independently you have to store cart data in and array with index. And pass the index to addProduct and removeProduct functions. And for cart content use class instead of id
Updated code is as below:
HTML
<!doctype html>
<html>
<head>
<title>Multiple Carts</title>
<link rel="stylesheet" type="text/css" href="http://www.jeasyui.com/easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="http://www.jeasyui.com/easyui/themes/icon.css">
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="http://www.jeasyui.com/easyui/jquery.easyui.min.js"></script>
</head>
<body style="margin:0;padding:0;height:100%;background:#fafafa;">
<ul class="products">
<li>
<a href="#" class="item">
<img src="images/shirt1.gif"/>
<div>
<p>Balloon</p>
<p>Price:$25</p>
</div>
</a>
</li>
<li>
<a href="#" class="item">
<img src="images/shirt2.gif"/>
<div>
<p>Feeling</p>
<p>Price:$25</p>
</div>
</a>
</li>
<li>
<a href="#" class="item">
<img src="images/shirt3.gif"/>
<div>
<p>Elephant</p>
<p>Price:$25</p>
</div>
</a>
</li>
<li>
<a href="#" class="item">
<img src="images/shirt4.gif"/>
<div>
<p>Stamps</p>
<p>Price:$25</p>
</div>
</a>
</li>
<li>
<a href="#" class="item">
<img src="images/shirt5.gif"/>
<div>
<p>Monogram</p>
<p>Price:$25</p>
</div>
</a>
</li>
<li>
<a href="#" class="item">
<img src="images/shirt6.gif"/>
<div>
<p>Rolling</p>
<p>Price:$25</p>
</div>
</a>
</li>
</ul>
<div class="cart">
<h1>Shopping Cart 1</h1>
<div style="background:#fff">
<table class="cartcontent" fitColumns="true" style="width:300px;height:auto;">
<thead>
<tr>
<th field="name" width=140>Name</th>
<th field="quantity" width=60 align="right">Quantity</th>
<th field="price" width=60 align="right">Price</th>
<th field="remove" width=60 align="right">Remove</th>
</tr>
</thead>
</table>
</div>
<p class="total">Total: $0</p>
<h2>Drop here to add to cart</h2>
</div>
<div class="cart">
<h1>Shopping Cart 2</h1>
<div style="background:#fff">
<table class="cartcontent" fitColumns="true" style="width:300px;height:auto;">
<thead>
<tr>
<th field="name" width=140>Name</th>
<th field="quantity" width=60 align="right">Quantity</th>
<th field="price" width=60 align="right">Price</th>
<th field="remove" width=60 align="right">Remove</th>
</tr>
</thead>
</table>
</div>
<p class="total">Total: $0</p>
<h2>Drop here to add to cart</h2>
</div>
</body>
</html>
CSS
.products{
list-style:none;
margin-right:300px;
padding:0px;
height:100%;
}
.products li{
display:inline;
float:left;
margin:10px;
}
.item{
display:block;
text-decoration:none;
}
.item img{
border:1px solid #333;
}
.item p{
margin:0;
font-weight:bold;
text-align:center;
color:#c3c3c3;
}
.cart{
position:fixed;
top: 0;
right:0;
width:300px;
height:50%;
background:#ccc;
padding:0px 10px;
}
.cart:nth-child(odd){
top:50% !important;
}
h1{
text-align:center;
color:#555;
}
h2{
position:absolute;
font-size:16px;
left:10px;
bottom:20px;
color:#555;
}
.total{
margin:0;
text-align:right;
padding-right:20px;
}
JS
var data = [];
$(function () {
$('.cartcontent').datagrid({
singleSelect: true
});
$('.item').draggable({
revert: true,
proxy: 'clone',
onStartDrag: function () {
$(this).draggable('options').cursor = 'not-allowed';
$(this).draggable('proxy').css('z-index', 10);
},
onStopDrag: function () {
$(this).draggable('options').cursor = 'move';
}
});
$('.cart').droppable({
onDragEnter: function (e, source) {
$(source).draggable('options').cursor = 'auto';
},
onDragLeave: function (e, source) {
$(source).draggable('options').cursor = 'not-allowed';
},
onDrop: function (e, source) {
var $source = $(source);
var name = $source.find('p:eq(0)').text();
var price = $source.find('p:eq(1)').text();
addProduct($('.cart').index($(e.currentTarget)), name, parseFloat(price.split('$')[1]));
}
});
});
function loadData(cartIndex, event) {
var $cart = $('.cart:eq(' + cartIndex + ')');
$cart.find('.cartcontent').datagrid('loadData', data[cartIndex]);
$cart.find('.total').text('Total: $' + data[cartIndex].total);
}
function addProduct(cartIndex, name, price) {
function add() {
if (!data[cartIndex]) {
data[cartIndex] = {
length: 0,
total: 0,
rows: []
};
}
for (var i=0; i < data[cartIndex].length; i++) {
var row = data[cartIndex].rows[i];
if (row.name === name) {
row.quantity += 1;
return;
}
}
data[cartIndex].length++;
data[cartIndex].rows.push({
name: name,
quantity: 1,
price: price,
remove: 'X'
});
}
add();
data[cartIndex].total += price;
loadData(cartIndex);
}
function removeProduct(cartIndex, el) {
var tr = $(el).closest('tr');
var name = tr.find('td[field=name]').text();
var price = tr.find('td[field=price]').text();
var quantity = tr.find('td[field=quantity]').text();
for(var i = 0; i < data[cartIndex].length; i++){
var row = data[cartIndex].rows[i];
if (row.name == name) {
data[cartIndex].rows.splice(i, 1);
data[cartIndex].length--;
break;
}
}
data[cartIndex].total -= price * quantity;
loadData(cartIndex);
}
JS Fiddle
functoin AddProduct(elment, event, tableid, otherid)
functoin is spelled incorrectly, it should be function.
Here is a solution that will work:
remove: 'X'
// later
$("#item-" + unique_nr).click(function (event) {
removeProduct(this, event, table_id, cart_id);
});
Using the id you can fetch the item later and bind a scope listener.
See: http://jsfiddle.net/Vwu37/135/
Right now I'm passing table_id and cart_id, you could consider passing the actual table and cart, ie. $("#cartcontent") and $(".cart")
Keep in mind that ".cart" is not unique, so if you want to have two lists you will need to make it unique.