Can't get footable to display - asp.net-mvc-5

I'm new to ASP and MVC so I'm learning. I'm trying to implement footable in my ASP.NET MVC 5 test project (I ultimately want to use the pagination and sorting features, but one step at a time).
I have added the .js files and .css files to the project and included them in the BundleConfig.cs file (see below). I'm not sure if that is all I need to do in order to use them in my project.
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js",
"~/Scripts/footable.min.js",
"~/Scripts/footable.js"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css",
"~/Content/footable.bootstrap.min.css",
"~/Content/footable.bootstrap.css"));
I'm retrieving data from a MySQL database and passing that to my view but I'm just getting a standard html table, what am I doing wrong ?
Here is the view code :-
#model IEnumerable<MVC5Footable.Models.radacct>
#{
ViewBag.Title = "Index";
}
<table class="footable">
<thead>
<tr>
<th>#Html.DisplayNameFor(model => model.username)</th>
<th>#Html.DisplayNameFor(model => model.framedipaddress)</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.username)</td>
<td>#Html.DisplayFor(modelItem => item.framedipaddress)</td>
</tr>
}
</tbody>
</table>
<!-- Initialize FooTable -->
<script>
jQuery(function ($) {
$('footable').footable();
});
</script>
Hoping someone can help. Thanks.

A little late, but for the record: you forgot to include the dot.
$('.footable').footable();

Related

ElementUI table not rendering in a Jest test

I have noticed that some of my components that use ElementUI tables are failing their tests, with Jest and Vue-test-utils.
Looking into it some more, when I reproduce a simple example and use console.log(wrapper.html) some of the inner parts of the table are simply not being rendered.
I am by no means a frontend expert (more of a Python backend person), and working on this to help another team out and so I am not 100% convinced that I'm not doing something stupid, but I have explored a range of possibilities and I can't figure it out. What's more, the tests are not working, but the component itself works absolutely fine within the application - the table is rendered properly. So it's frustrating because it appears to work, but the test suite disagrees....
This is not the actual code, but it's a trimmed down example to reproduce the issue.
BasicTable.vue
<template>
<div>
<el-table
:data="tdata"
>
<el-table-column #default="{ row }">
{{ row.item }}
</el-table-column>
<el-table-column #default="{ row }">
{{ row.size }}
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: 'BasicTable',
data () {
return {
tdata: [{ item: 'A', size: 'L' },
{ item: 'B', size: 'M' },
{ item: 'C', size: 'XS' },
{ item: 'D', size: 'XXL' },
],
}
},
}
</script>
BasicTable.spec.js
import BasicTable from './BasicTable.vue'
import ElementUI from 'element-ui'
import { mount, createLocalVue } from '#vue/test-utils'
test('Render for basic table', () => {
const localVue = createLocalVue()
localVue.use(ElementUI)
const wrapper = mount(BasicTable, {
localVue
}
console.log(wrapper.html())
})
So I would expect the output of the wrapper.html() to just render the table, with the four rows, and two columns, as would be expected from the data.
However, what I actually get is
<div>
<div class="el-table el-table--fit el-table--enable-row-hover el-table--enable-row-transition">
<div class="hidden-columns">
<div></div>
<div></div>
</div>
<div class="el-table__header-wrapper">
<table cellspacing="0" cellpadding="0" border="0" class="el-table__header">
<colgroup></colgroup>
<thead class="">
<tr class=""></tr>
</thead>
</table>
</div>
<div class="el-table__body-wrapper is-scrolling-none">
<table cellspacing="0" cellpadding="0" border="0" class="el-table__body">
<colgroup></colgroup>
<tbody>
<tr class="el-table__row"></tr>
<tr class="el-table__row"></tr>
<tr class="el-table__row"></tr>
<tr class="el-table__row"></tr>
<!---->
</tbody>
</table>
<!---->
<!---->
</div>
<!---->
<!---->
<!---->
<!---->
<div class="el-table__column-resize-proxy" style="display: none;"></div>
</div>
</div>
So between the <tbody></tbody> tags, the correct number of rows are present (4) but then they seem to have no internal content.
A few things I initially considered but discounted as the issue:
I made sure I was mounting and not shallowMounting
using LocalVue and using ElementUI does appear to be working, the ElementUI components are being rendered as basic html, with the relevant classes added (e.g. <table class="el-table">)
Some of the actual components use a computed() to calculate the data that goes into the table, or have it passed via a prop. I thought this might be causing some issue, but I set up this basic example with the most simple case I could think of (i.e just hardcoded data in the component) and i still see the same result.
I have struggled to find much specific ElementUI/Jest/Vue-test-utils help online, so any suggestions would be greatly appreciated! And if I have done something stupid, please let me know because as I said this is not my main area of expertise.
Versions of stuff I am using (from package.json)
"dependencies": {
"element-ui": "^2.4.5",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"core-js": "^3.6.5",
"#vue/cli-service": "~4.5.0"
},
Note: The project is setup with Vue-cli and I believe Jest is bundled with that.
"#vue/cli-plugin-unit-jest": "~4.5.0",
I run the tests with npm run test:unit
I seem to have found an answer, so will post it here if anyone else stumbles across this.
The solution appears to be waiting for nextTick() for the DOM elements to properly render. I'll be honest, I am not 100% sure why this is the reason - I might expect this if the data was changing during the test (and I had used that approach for other tests when updating props or triggering a click for example) but in this case the data is passed in while mounting... If you come across this answer and have a more concrete explanation, please feel free to add it here!
So for completeness, something like
test('Render for basic table', async () => {
const localVue = createLocalVue()
localVue.use(ElementUI)
const wrapper = mount(BasicTable, {
localVue
}
await wrapper.vm.$nextTick() <-- added this line (also made the test async)
console.log(wrapper.html())
})
Appears to solve my problem!

how to display image in angular saved on node server

I have a nodejs app running on localhost:3000, I uploaded images using multer, so they are in ./uploads/ folder. In the Angular app running on locahost:4200 I want to retrieve those images.
in my for loop:
<tr *ngFor="let data of empData;let i=index">
<td>{{i+1}}</td>
<td>{{data.emp_name}}</td>
<td><img src="./uploads/{{data.emp_image}}"></td>
</tr>
I have all data in following result variable:
this.emp.viewData().subscribe(
res => {
this.empData = res;
},
err => {
console.log(err);
},
)
In addition to the answer above by Software Person, ensure you include this to you node application to enable you access the uploads directory in your nodejs application.
const express = require('express');
const app = express();
app.use(express.static('uploads'));
Happy Coding.
You could include your full url like
<tr *ngFor="let data of empData;let i=index">
<td>{{i+1}}</td>
<td>{{data.emp_name}}</td>
<td><img src="http(s)://localhost:3000/uploads/{{data.emp_image}}"></td>
</tr>
Or even better you could define a variable in component and use it like this:
<tr *ngFor="let data of empData;let i=index">
<td>{{i+1}}</td>
<td>{{data.emp_name}}</td>
<td><img src="{{SERVER_UPLOAD_DIR}}/{{data.emp_image}}"></td>
</tr>

MVC 5 get value of DropDownList within a table row

I have a view with a table of products that can be added to a shopping cart. Each row has a DropDownList with allowed quantities that can be ordered along with a button to add to cart. Everything is populating and displaying properly. I know how to pass the item ID in the ActionLink but how can I get the value of the DownDownList associated with the table row of the ActionLink that was clicked?
I am guessing possibly using JQuery that fires when the ActionLink is clicked?
I also thought of making every row a form but that seems overkill.
Is there an easy MVC way to do this?
In prepping more info for a proper question and went ahead and solved it. Thank you Stephen for the nudge and info.
I tried putting a Html.BeginForm around each <tr> tag in the details section. This did indeed work for me. I was able to easily get the unique form info to POST for each individual row. However, when I would enable JQuery DataTables the submit would break. DataTables must be capturing the submit or click somehow. Haven't figured that out but it made me try JQuery which seems a much better way to do it.
Here is how I construct the table data row:
#foreach (var item in Model)
{
<tr>
<td>
<img src="#item.GetFrontImage()" width="100" />
</td>
<td>
<strong>#Html.DisplayFor(modelItem => item.DisplayName)</strong>
</td>
<td>
#Html.DisplayFor(modelItem => item.CustomerSKU)
</td>
<td>
#Html.DropDownList("OrderQty", item.GetAllowedOrderQuantities(), htmlAttributes: new { #class = "form-control" })
</td>
<td>
<a class="btn btn-default pull-right" data-id="#item.ID">Add to Cart</a>
</td>
</tr>
}
This creates a select with id of OrderQty and I embedded the item ID in data-id attribute of the link. I then used this JQuery to capture the info and POST it to my controller. Just have a test div displaying the results in this example:
// Add to Cart click
$('table .btn').click(function () {
// Gather data for post
var dataAddToCard = {
ID: $(this).data('id'), // Get data-id attribute (Item ID)
Quantity: $(this).parent().parent().find('select').val() // Get selected value of dropdown in same row as button that was clicked
}
// POST data to controller
$.ajax({
url: '#Url.Action("AddToCart","Shopping")',
type: 'POST',
data: JSON.stringify(dataAddToCard),
contentType: 'application/json',
success: function (data) { $('#Result').html(data.ID + ' ' + data.Quantity); }
})
});
The JQuery function receives the reference to the link being clicked so I can extract the Item ID from the data-id attribute. I can then get a reference to the dropdown (select) that is in the same row by using .parent.parent (gets me to the <tr> tag) and then just finding the next 'select' tag. Probably pretty obvious to a lot of you.
This works great for my purposes. I can also update other elements with data returned from the POST.
Thank you
Karl
for the table in html:
<div class="table-responsive">
<table id="employeeTable"class="table table-bordered">
<thead>
<tr>
<th class="text-center">ُُُEmpId</th>
<th class="text-center">Name</th>
<th class="text-center">Absense State</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>#item.Id</td>
<td>#item.Name</td>
<td class="text-center">#Html.DropDownList("DDL_AbsentStatus", new SelectList(ViewBag.statusList, "Id", "Name"), new { #class = "form-control text-center" })</td>
</tr>
}
</tbody>
</table>
</div>
in javascript to get the selected value:
//Collect Date For Pass To Controller
$("#btn_save").click(function (e) {
e.preventDefault();
if ($.trim($("#datepicker1").val()) == "") {
alert("ادخل تاريخ يوم صحيح!")
return;
}
var employeesArr = [];
employeesArr.length = 0;
$.each($("#employeeTable tbody tr"), function () {
employeesArr.push({
EmpId: $(this).find('td:eq(0)').html(),
EntryDate: $.trim($("#datepicker1").val()),
StatusId: $(this).find('#DDL_AbsentStatus').val()
});
});
$.ajax({
url: '/Home/SaveAbsentState',
type: "POST",
dataType: "json",
data: JSON.stringify(employeesArr),
contentType: 'application/json; charset=utf-8',
success: function (result) {
alert(result);
emptyItems();
},
error: function (err) {
alert(err.statusText);
}
});
})

How to wrap #Html.DisplayFor() with #Html.ActionLink()

I just need to wrap column into hyperlink. So that a user can click on item of Number Column and can be redirected.
Here is my current View:-
#foreach (var item in Model) {
<tr>
<th>
#Html.ActionLink("Read", "Read", new { id = item.id})
</th>
<td>
#Html.DisplayFor(modelItem => item.Number)
</td>
</tr>
Trying to do something like this. I know its not right but need to know the right way to do it. I am new to MVC
#Html.ActionLink(#Html.DisplayFor(modelItem => item.Number).ToString(), "Read", new { id = item.id })
You can't really, but you can just use Url.Action instead:
<a href="#Url.Action("Read", new { id = item.id })">
#Html.DisplayFor(modelItem => item.Number)
</a>
I don't know if there's a way to do this with ActionLink (I suspect there isn't, at least not in any way I'd want to support in the code.) But you can manually craft an a tag and still keep its URL dynamic by using Url.Action() instead:
<a href="#Url.Action("Read", new { id = item.id })">
#Html.DisplayFor(modelItem => item.Number)
</a>

Best way to open dialog in MVC 4 application

I'm just starting to learn MVC so try to bear with me.
I have a table that has a few options to the side that you can edit, delete and show the details of.
If I click the Details button now It will take me to another page (Details.cshtml)
which is located in the same Controller as the Index.cshtml which displays the table above.
This is the code for the table (Index.cshtml)
#model Collusus.Models.IndexModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<h2>Hello #Html.ActionLink(Model.userLoggedIN, "getProfile")</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table id="myTable" class="tablesorter">
<thead>
<tr>
<th>Change ID</th>
<th>Owner</th>
<th>Priority</th>
<th>Disposition Date</th>
<th>Completion Date</th>
<th>Do what?</th>
</tr>
</thead>
<tbody>
#for(int i=0; i<Model.changes.Count(); i++)
{
<tr>
<td>#Model.changes[i].ID</td>
<td>#Model.changes[i].Owner</td>
<td>#Model.changes[i].Priority</td>
<td>#Model.changes[i].DispositionDate.ToShortDateString()</td>
<td>#Model.changes[i].ActualCompletionDate.ToShortDateString()</td>
<td>#if (Model.changes[i].Owner == Model.userLoggedIN)
{
#Html.ActionLink("Delete", "Delete", new { id=Model.changes[i].ID })
#Html.ActionLink("Edit", "Edit", new { id=Model.changes[i].ID })
}
#Html.ActionLink("Details", "Details", new { id=Model.changes[i].ID })
</td>
</tr>
}
</tbody>
</table>
As you can see because of the code below, it will just take me to another page.
#Html.ActionLink("Details", "Details", new { id=Model.changes[i].ID })
What I want to do:
Open Delete,Edit or Details view in a dialog instead of another page.
Be able to still have the same functionality as if they were just opening another page.
I don't know if that makes too much sense. I've tried to explain it the best I could and been frustrated searching Google / trying code from other solutions but can't get it to work.
If you suggest another way besides the JQUERY dialog I'm willing to go that option too. All help is appreciated since I've been so frustrated.
I'm assuming you want to open them into a modal dialog. To accomplish this you can return partial views from your controller.
You can add a class to your action links like this:
#Html.ActionLink("Details", "Details", new { id=Model.changes[i].ID }, new { #class = "details-modal" })
Your Details action method:
public ActionResult Details(int id)
{
// Your code here
return PartialView("_Details", myModel); // return the partial view with the model
}
jQuery (off the top of my head so it may not be 100% correct):
$('#my-dialog').dialog({
autoOpen: false,
width: 400,
resizable: false,
modal: true
});
$('.details-modal').click(function() {
var theURL = $(this).attr('href');
$('#my-dialog').load(theURL, function() {
$(this).dialog('open');
});
return false; // ensures the browser is not redirected to the link's URL
});

Resources