I am trying to write a test that validates and confirms add-ons in a cart flow. One page I'm having difficulty with loads the page, and has a checkbox I need to hit that's within a shadow root. I can identify some of the highest level components on the page and am stuck trying to get to one in particular.
I have a byebug where I'm trying in the flow of the test and at the page in question.
Running "sales_channels_reg.regsaver_section.regsaver_testtest.text(:all)". returns:
"Show Progress Welcome Who Are You Registering? Information Review Suggested Add-Ons Shopping Cart Checkout Confirmation Zach Partyka Demo Site Suggested Add-Ons Loading... Loading... #displayBodyHeader { display: none !important } $j(document).ready(function () { listenForEventsFromSalesChannelsElement(); function listenForEventsFromSalesChannelsElement() { document.getElementById('sales-channels-element').addEventListener('outputEvent', function(event) { switch (event.detail.type) { case 'registration': registerAnotherOrContinueToCheckout(event.detail); break; case 'analytics': handleAnalyticsEvents(event.detail); break; } }); } function registerAnotherOrContinueToCheckout(eventDetail) { if (eventDetail.action === 'registerAnother') $j('.js-register-another-button').click(); else if (eventDetail.action === 'continue') $j('.js-continue-button').click(); } function handleAnalyticsEvents(eventDetail) { const railsEnv = 'production'; if (railsEnv === 'development') return; // SeMParticle only works for staging and production. switch (eventDetail.action) { case 'pageView': setCookie(\"surveySport\", eventDetail.pageAttributes.survey_sport , 1); setCookie(\"surveyType\", encodeURIComponent(eventDetail.pageAttributes.survey_type), 1); setCookie(\"surveyRegistrantType\", eventDetail.pageAttributes.survey_registrant_type, 1); setCookie(\"surveyParticipantType\", eventDetail.pageAttributes.survey_participant_type, 1); setCookie(\"surveyParticipantDOB\", eventDetail.pageAttributes.survey_participant_dob , 1); setCookie(\"surveyParticipantGender\", eventDetail.pageAttributes.survey_participant_gender, 1); setCookie(\"bossOrganizationId\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_id), 1); setCookie(\"bossOrganizationName\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_name), 1); setCookie(\"bossOrganizationAddrState\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_addrState), 1); setCookie(\"bossOrganizationSports\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_sports), 1); window.SeMParticle.pageView(eventDetail.pageAttributes); break; case 'logImpression': window.SeMParticle.logImpression(eventDetail.pageAttributes, eventDetail.productAttributes, 'SalesChannelsImpression'); break; case 'addToCart': window.SeMParticle.addToCart(eventDetail.pageAttributes, eventDetail.productAttributes); break; } } function setCookie(name,value,exp_days) { var d = new Date(); d.setTime(d.getTime() + (exp_days*24*60*60*1000)); var expires = \"expires=\" + d.toGMTString(); document.cookie = name + \"=\" + value + \";\" + expires + \";path=/;domain=.sportngin.com\"; } });"
But running "sales_channels_reg.regsaver_section.regsaver_testtest.find(:css, "#sales-channels-element__container").text(:all)" returns only the "Loading... Loading..." text above the shadow-root div
The part I need help with is I don't know why I can't I dig to the div element that has the shadow-root? I've tried using find_element for :id, :css, lots of "sleep()" to let the page load, etc., but still stuck there
The Code for trying to target the test in particular:
-first file-
describe "Showing 4 max add-ons in reg flow", type: :feature, service: "sales_channels1" do
context "yadda yadda" do
subject(:sales_channels_reg) { SalesChannelsRegGenerals.new }
subject(:se_signup_page) { SELogin.new }
# subject(:gen_reg) { RegistrationHelper.new }
let(:form_number) { "848624247" }
it "in test flow does thing" do
couple of other steps first
byebug (where I'm at in the code now)
end
end
end
-different file-
require "./spec/page_models/sales_channels_ncsa_section.rb"
require "./spec/page_models/sales_channels_medsaver_section.rb"
require "./spec/page_models/sales_channels_regsaver_section.rb"
require "./spec/page_models/sales_channels_four_addons_section.rb"
class SalesChannelsRegGenerals < SitePrism::Page
set_url "https://zachpartyka#{SeleniumTest.ngin_site}/register/form/{/form_number}"
section :regsaver_section, RegSaverSection, "div#siteContainer2"
end
-different file-
class RegSaverSection < SitePrism::Section
element :regsaver_testtest, "#pageContentContainer"
end
The HTML for this section is (see attached pic for clearer picture):
<div id="pageContentContainer" class="clearfix" xpath="1">
::before[![enter image description here][1]][1]
<div id="panelOne">
<div class="subNavContainer">
<div id="sequenceMenu">
<div id="sub-nav-toggle" class="sub-nav-toggle">
<span class="sub-nav-toggle-text">Show Progress</span>
</div>
<div class="subNav noHeader">
<ul id="formsBin" class="steps">
<li class="enabled complete">Welcome</li>
<li class="enabled complete">Who Are You Registering?</li>
<li id="form_2959222" class="enabled complete">Information</li>
<li class="enabled complete">Review</li>
<li class="enabled selected"><span>Suggested Add-Ons</span></li>
<li class="enabled incomplete"><span>Shopping Cart</span></li>
<li class="enabled incomplete"><span>Checkout</span></li>
<li class="enabled incomplete"><span>Confirmation</span></li>
</ul>
</div>
</div>
</div>
</div>
<div id="panelTwo">
<div id="displayBodyContent">
<div id="flashNotice">
</div>
<div id="yieldContent">
<h2 id="displayBodyHeader" class=""><span>Zach Partyka Demo Site Suggested Add-Ons</span></h2>
<div id="sales-channels-element__container" style="">
<sales-channels-element id="sales-channels-element" context="{"multipleRegistrations":{"enabled":true,"buttonLabel":"Register Another person"},"mode":"user","userId":"49dd822a5d654085a5e4b435b83265cc","bossOrganization":{"id":230482,"name":"Sales Channels Test Org"},"guestKey":"9553ee6b1c99c47deda474f5fb09a074c8820b6188b1ac7031f4452546386b27","enabledSalesChannels":["MedSaver","Skills","PitchTrackerBaseball","PitchTrackerSoftball","RegSaver","Ncsa"],"participant":{"firstName":"baseballpitchtracker","lastName":"child","gender":"male","dob":"01/01/2006","address":{"address1":"45645 buttsville","address2":null,"state":"mn","city":"burning","zip":"55418","country":"US"},"email":"selenium+actionable_redirect#sportngin.com","phoneNumber":"6515555555","graduationYear":null},"registrant":{"firstName":"Actionable","lastName":"Redirect","dob":"01/01/1990","address":{"address1":"2345 Foo Bar Circle","address2":null,"state":"MN","city":"Testingville","zip":"55428","country":"US"},"email":"selenium+actionable_redirect#sportngin.com","phoneNumber":"6515555555"},"requestDetails":{"eventType":"Multiple","eventCost":10.0,"programStartDate":"12/22/2021","programEndDate":"12/22/2022","paymentDate":"12/21/2021","sport":"Baseball","submittedBy":"parent"},"analyticsPageAttributes":{"depth1":"Sitebuilder","depth2":"Registration","depth3":"SalesChannels","survey_sport":"Baseball","survey_type":"Event","survey_registrant_type":"parent","survey_participant_type":"athlete","survey_participant_dob":"01/01/2006","survey_participant_gender":"male"},"analyticsEventAttributes":{"event_action_sports":"Baseball","event_action_genders":"male","event_action_dobs":"01/01/2006","event_type":1,"action":"Click","bossOrganizationId":230482,"bossOrganizationName":"Sales Channels Test Org","bossOrganizationAddrState":"MN","bossOrganizationSports":"baseball"}}" ng-version="10.2.5" style=""></sales-channels-element>
<div style="display: none;">
<br>
<div class="pageElement">
<div class="surveyButtonWrap" style="margin-left:0;text-align: center;">
<div style="display: inline-block;text-align: right;">
<form class="button_to" method="post" action="/survey/register_another_person/591168"><input waiting_class="submitProcess pageElement" style="text-transform:capitalize;" class="button-large js-register-another-button" id="processing_link" type="submit" value="Register Another person"><input type="hidden" name="authenticity_token" value="lBrd9OZaA7siTgHe8WYjV3LSqYKUGthsNloEKiMMVeypQUcTUVq/AxLjEjQv0i+Y79R4YPQOMcrlGGwxos/gJw=="></form><div id="waiting_textprocessing_link" style="display:none;" class="pl-overlay"><div class="pl-progress__container"><p class="pl-progress__label">Loading...</p><div class="pl-progress"><div role="progressbar" class="pl-progress__bar--indeterminate"></div></div></div></div>
</div>
<div style="display: inline-block; text-align: left;">
<form class="button_to" method="post" action="/cart/add?from_step=85"><input class="button-large button-siteColor button-default js-continue-button" id="processing_link" type="submit" value="Continue"><input type="hidden" name="authenticity_token" value="lBrd9OZaA7siTgHe8WYjV3LSqYKUGthsNloEKiMMVeypQUcTUVq/AxLjEjQv0i+Y79R4YPQOMcrlGGwxos/gJw=="></form><div id="waiting_textprocessing_link" style="display:none;" class="pl-overlay"><div class="pl-progress__container"><p class="pl-progress__label">Loading...</p><div class="pl-progress"><div role="progressbar" class="pl-progress__bar--indeterminate"></div></div></div></div>
</div>
</div>
</div>
<div class="clearAll"></div>
</div>
</div>
<style>#displayBodyHeader { display: none !important }</style>
<script type="text/javascript" src="https://se-sales-channels.ui.sportsengine.com/sales-channels-element.js"></script>
<script>
$j(document).ready(function () {
listenForEventsFromSalesChannelsElement();
function listenForEventsFromSalesChannelsElement() {
document.getElementById('sales-channels-element').addEventListener('outputEvent', function(event) {
switch (event.detail.type) {
case 'registration':
registerAnotherOrContinueToCheckout(event.detail);
break;
case 'analytics':
handleAnalyticsEvents(event.detail);
break;
}
});
}
function registerAnotherOrContinueToCheckout(eventDetail) {
if (eventDetail.action === 'registerAnother') $j('.js-register-another-button').click();
else if (eventDetail.action === 'continue') $j('.js-continue-button').click();
}
function handleAnalyticsEvents(eventDetail) {
const railsEnv = 'production';
if (railsEnv === 'development') return; // SeMParticle only works for staging and production.
switch (eventDetail.action) {
case 'pageView':
setCookie("surveySport", eventDetail.pageAttributes.survey_sport , 1);
setCookie("surveyType", encodeURIComponent(eventDetail.pageAttributes.survey_type), 1);
setCookie("surveyRegistrantType", eventDetail.pageAttributes.survey_registrant_type, 1);
setCookie("surveyParticipantType", eventDetail.pageAttributes.survey_participant_type, 1);
setCookie("surveyParticipantDOB", eventDetail.pageAttributes.survey_participant_dob , 1);
setCookie("surveyParticipantGender", eventDetail.pageAttributes.survey_participant_gender, 1);
setCookie("bossOrganizationId", encodeURIComponent(eventDetail.pageAttributes.boss_organization_id), 1);
setCookie("bossOrganizationName", encodeURIComponent(eventDetail.pageAttributes.boss_organization_name), 1);
setCookie("bossOrganizationAddrState", encodeURIComponent(eventDetail.pageAttributes.boss_organization_addrState), 1);
setCookie("bossOrganizationSports", encodeURIComponent(eventDetail.pageAttributes.boss_organization_sports), 1);
window.SeMParticle.pageView(eventDetail.pageAttributes);
break;
case 'logImpression':
window.SeMParticle.logImpression(eventDetail.pageAttributes, eventDetail.productAttributes, 'SalesChannelsImpression');
break;
case 'addToCart':
window.SeMParticle.addToCart(eventDetail.pageAttributes, eventDetail.productAttributes);
break;
}
}
function setCookie(name,value,exp_days) {
var d = new Date();
d.setTime(d.getTime() + (exp_days*24*60*60*1000));
var expires = "expires=" + d.toGMTString();
document.cookie = name + "=" + value + ";" + expires + ";path=/;domain=.sportngin.com";
}
});
</script>
</div>
<div class="clearAll"></div>
</div>
</div>
</div>
You need to use evaluate_script rather than execute_script or the returned value won't get unwrapped into an element that can be used.
evaluate_script("arguments[0].shadowRoot", ...)
As it turns out, I didn't do a thorough enough investigation. The element was not visible, and I didn't try using visible? along with find_element. The solution to selecting the shadow root - and then digging within it was as follows:
sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false)).find('#addToCart--false')
I am building a mini online shopping CMS project for collage.
I finished the project and now I want to add a feature where I have limited quantity of products.
When the quantity is equal to 0 then product is shown as 'out of stock' and my 'Add to cart' button becomes disabled.
I have added a 'quantity' property to my product class and now I need to change something in the Index view of the product.
I tried few options and it does not seem to work.
Here is the code for the Index.cshtml page.
#model IEnumerable
<Product>
#{ ViewData["Title"] = "Products"; }
<h1 class="display-4 pb-5">All Products</h1>
<div class="row">
#foreach (var item in Model)
{
<div class="col-4">
<div class="ajaxbg d-none">
<img src="~/Images/ajax_loader.gif" />
<p class="lead alert alert-success text-center d-none">
The product has been added!
</p>
</div>
<img src="~/Media/Products/#item.Image" class="img-fluid" alt="" />
<h4>#item.Name</h4>
<div>
#Html.Raw(item.Description)
</div>
<p>
#item.Price.ToString("C2")
</p>
<p>
<a asp-controller="Cart" asp-action="Add" asp-route-id="#item.Id" data-id="#item.Id" id="addToCartButton" class="btn btn-primary addToCart">Add to cart</a>
</p>
#if (item.Quantity == 0) { }
</div>
}
#if (ViewBag.TotalPages > 1) {
<div class="d-flex w-100 justify-content-center">
<pagination page-count="#ViewBag.TotalPages" page-target="/products" page-number="#ViewBag.PageNumber" page-range="ViewBag.PageRange"></pagination>
</div>
}
</div>
#section Scripts {
<script>
function DisableBtn() {
document.getElementById("addToCartBUtton").disabled = true;
}
$(function() {
$("a.addToCart").click(function(e) {
e.preventDefault();
let ajaxDiv = $(this).parent().parent().find("div.ajaxbg");
ajaxDiv.removeClass("d-none");
let id = $(this).data("id");
$.get('/cart/add/' + id, {}, function(data) {
$("div.smallcart").html(data);
ajaxDiv.find("img").addClass("d-none");
ajaxDiv.find("p").removeClass("d-none");
setTimeout(() => {
ajaxDiv.animate({
opacity: 0
}, function() {
$(this).addClass("d-none").fadeTo(.1, 1);
$(this).find("img").removeClass("d-none");
$(this).find("p").addClass("d-none");
});
});
});
});
});
</script>
}
Try this
#If(Model!=null)
{
#foreach (var item in Model)
{
..........
<p>
#if (item.Quantity == 0) {
<a disabled class="btn btn-primary addToCart">Add to cart</a>
}
else
{
<a asp-controller="Cart" asp-action="Add" asp-route-id="#item.Id" data-id="#item.Id" id="addToCartButton" class="btn btn-primary addToCart">Add to cart</a>
}
</p>
}
}
i am trying to get an image uploader working inside my MVC project, but everytime i click the upload file button, i get "Error undefined", i am not sure where i am going wrong.
The page is a partial view, loaded inside a #Html.BeginForm . below is the code i am using.
Index.cshtml
#using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="container">
<div class="row">
<article class="col-sm-12 col-md-12 col-lg-12 sortable-grid ui-sortable">
<div id="wid-id-0">
<!-- widget div-->
<div role="content">
<!-- widget content -->
<div class="widget-body">
<div class="row" style="background: white">
<form id="wizard-1" novalidate="novalidate">
<div id="bootstrap-wizard-1" class="col-sm-12">
<div class="tab-content">
#*Tab 1 (Step 1)*#
<div class="tab-pane active" id="tab1">
<br>
#Html.Partial("Registration/_Step1")
<div class="form-actions">
<div class="row">
<div class="col-sm-12">
<ul class="pager wizard no-margin">
<li class="previous disabled">
Previous
</li>
<li data-target="#step2" class="next">
Next
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="form-bootstrapWizard">
<ul class="bootstrapWizard form-wizard" style="background: coral">
<li class="active" data-target="#step1">
<span class="step">1</span> <span class="title">Step 1: Profile Information</span>
</li>
<li data-target="#step2" class="">
<span class="step">2</span> <span class="title">Step 2: Profile Picture</span>
</li>
<li data-target="#step3" class="">
<span class="step">3</span> <span class="title">Step 3: Identification</span>
</li>
<li data-target="#step4">
<span class="step">4</span> <span class="title">Step 4: Submit Profile</span>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</form>
</div>
</div>
<!-- end widget content -->
</div>
<!-- end widget div -->
</div>
<!-- end widget -->
</article>
</div>
</div>
}
_Step1.cshtml
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(document).ready(function () {
$("#Upload").click(function (e) {
e.preventDefault();
var formData = new FormData();
var totalFiles = document.getElementById("FileUpload").files.length;
for (var i = 0; i < totalFiles; i++)
{
var file = document.getElementById("FileUpload").files[i];
formData.append("FileUpload", file);
}
$.ajax({
type: "POST",
url: '#Url.Action("UploadFiles", "Account")',
data: formData,
dataType: 'json',
contentType: false,
processData: false,
success: function (response) {
alert('succes!!');
},
error: function (error) {
alert("error: " + error.statusMessage);
}
});
});
});
</script>
<table align="center">
<tr>
<td style="text-align: center;"><label>Upload Profile Picture</label></td>
<td></td>
</tr>
<tr>
<td valign="top"><img id="imgPreview" src="~/Content/Images/upload_holder.PNG" class="img-holder" style="width: 180px; height: 180px;" /></td>
<td>
#*<input type="submit" class="btn btn-lg" style="width: 200px; background: #0091d9; color: white;" value="Upload Photo" />*#
#*<input id="input-4" name="input4[]" type="file" multiple class="file-loading" style="width: 200px; background: #0091d9; color: white;" value="Upload Photo">*#
<input type="file" id="FileUpload" multiple />
<input type="submit" id="Upload" value="Upload"/>
<br />
<br />
<label>Acceptable file formats: jpg, gif or png file</label>
<br />
<label>Under 1 MB</label>
</td>
</tr>
</table>
any help is greatly appreciated!
EdIT: Below is the controller function, but its never hit.
[HttpPost]
public ActionResult UploadFiles()
{
// Checking no of files injected in Request object
if (Request.Files.Count > 0)
{
try
{
// Get all files from Request object
HttpFileCollectionBase files = Request.Files;
for (int i = 0; i < files.Count; i++)
{
//string path = AppDomain.CurrentDomain.BaseDirectory + "Uploads/";
//string filename = Path.GetFileName(Request.Files[i].FileName);
HttpPostedFileBase file = files[i];
string fname;
// Checking for Internet Explorer
if (Request.Browser.Browser.ToUpper() == "IE" || Request.Browser.Browser.ToUpper() == "INTERNETEXPLORER")
{
string[] testfiles = file.FileName.Split(new char[] { '\\' });
fname = testfiles[testfiles.Length - 1];
}
else
{
fname = file.FileName;
}
// Get the complete folder path and store the file inside it.
fname = Path.Combine(Server.MapPath("~/Uploads/"), fname);
file.SaveAs(fname);
}
// Returns message that successfully uploaded
return Json("File Uploaded Successfully!");
}
catch (Exception ex)
{
return Json("Error occurred. Error details: " + ex.Message);
}
}
else
{
return Json("No files selected.");
}
}
What I want to achieve is once the data got saved into database, when it goes back to client, it will automatically update the observable array. But somehow I couldn't make it happen.
This is my Server side code:
[HttpGet]
public JsonResult GetTasks()
{
var tasks = context.ToDoTasks.ToList();
return Json(tasks.Select(c => new TaskViewModel(c)).ToList(), JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult AddTask(string text, string date)
{
var nTask = new ToDoTask()
{
Text = text,
Date = DateTime.ParseExact(date, "MM/dd/yyyy", System.Globalization.CultureInfo.InvariantCulture),
IsDone = false,
Order = 1,
};
context.ToDoTasks.Add(nTask);
context.SaveChanges();
return Json(new TaskViewModel(nTask), JsonRequestBehavior.AllowGet);
}
This is my cshtml file code:
<form>
<div class="controls controls-row" style="margin-top:40px;">
<input class="span7" type="text" placeholder="Task to do" style="margin-right:4px;" id="oText">
<div id="task-date" class="input-append date">
<input data-format="MM/dd/yyyy" type="text" placeholder="MM/dd/yyyy" name="taskDate" id="oDate" />
<span class="add-on">
<i data-time-icon="icon-time" data-date-icon="icon-calendar">
</i>
</span>
</div>
<button class="btn" type="submit" style="margin-top:-10px;" data-bind="click: save">+</button>
</div>
<div class="controls">
<label class="checkbox">
<input type="checkbox"> Mark all as complete
</label>
</div>
<div id="task-section" style="margin-top:20px;">
<ul data-bind="foreach: Tasks">
<!-- ko if: IsDone -->
<li>
<span><input type="checkbox" style="margin:-5px 5px 0px 0px;" data-bind="checked: IsDone" /></span>
<del><span data-bind="text: Text"></span></del>
<del><span class="task-date" data-bind="text: Date"></span></del>
</li>
<!-- /ko -->
<!-- ko ifnot: IsDone -->
<li>
<span><input type="checkbox" style="margin:-5px 5px 0px 0px;" data-bind="checked: IsDone" /></span>
<span data-bind="text: Text"></span>
<span class="task-date" data-bind="text: Date"></span>
</li>
<!-- /ko -->
</ul>
</div>
<div class="clearfix" style="margin-top:30px;">
<span class="pull-left" style="font-weight:bold;"><span data-bind="text: oItemLeft"></span> item left</span>
<span class="pull-right badge" style="cursor:pointer;" data-bind="click: remove">Clear # completed item</span>
</div>
</form>
And finally my JS:
var ViewModel = function (data) {
var self = this;
self.Tasks = ko.mapping.fromJS(data, {}, self.Tasks);
self.oItemLeft = ko.computed(function () {
var i = 0;
data.forEach(function (entry) {
if (!entry.IsDone) i++;
});
return i;
});
self.save = function () {
$.ajax({
url: "Home/AddTask",
type: "POST",
data: { text: $('#oText').val(), date: $('#oDate').val() },
success: function (response) {
ko.mapping.fromJS(response, ViewModel);
}
});
};
self.remove = function () {
alert('delete');
}
}
$(function () {
$.getJSON("/Home/GetTasks/", null, function (data) {
ko.applyBindings(new ViewModel(data));
});
// for datepicker
$('#task-date').datetimepicker({
language: 'pt-BR',
pickTime: false
});
});
self.save = function () {
$.ajax({
url: "Home/AddTask",
type: "POST",
data: { text: $('#oText').val(), date: $('#oDate').val() },
success: function (response) {
var task = ko.mapping.fromJS(response);
self.Tasks.push(task);
}
});
};
Also for oItemLeft you should be referring to self.Tasks instead of data:
self.oItemLeft = ko.computed(function () {
var i = 0;
self.Tasks().forEach(function (entry) {
if (!entry.IsDone) i++;
});
return i;
});
How can i use pagination for showing data dynamically in div in asp .net using ajax or jquery?
To be honest, it's hard to help you - you should be more specific, but maybe you are looking for sth like this:
jQuery pagination plugin
and
demo here
You haven't really asked a real question, but maybe this will help: https://github.com/TroyGoode/PagedList
You can create pagination on your div using bootstrap and jquery.
Controller
public ActionResult Index()
{
// Tab Data
ThumbnailViewModel model = new ThumbnailViewModel();
model.ThumbnailModelList = new List<ThumbnailModel>();
// Test Details Data
List<ThumbnailDetails> _detaisllist = new List<ThumbnailDetails>();
int count = 10;
for (int i = 1; i <= count; i++)
{
ThumbnailDetails obj = new ThumbnailDetails();
obj.Details1 = "Details- Main" + i;
obj.Details2 = "Details- Main-Sub" + i;
_detaisllist.Add(obj);
}
// batch your List data for tab view i want batch by 2 you can set your value
var listOfBatches = _detaisllist.Batch(2);
int tabNo = 1;
foreach (var batchItem in listOfBatches)
{
// Generating tab
ThumbnailModel obj = new ThumbnailModel();
obj.ThumbnailLabel = "Lebel" + tabNo;
obj.ThumbnailTabId = "tab" + tabNo;
obj.ThumbnailTabNo = tabNo;
obj.Thumbnail_Aria_Controls = "tab" + tabNo;
obj.Thumbnail_Href = "#tab" + tabNo;
// batch details
obj.ThumbnailDetailsList = new List<ThumbnailDetails>();
foreach (var item in batchItem)
{
ThumbnailDetails detailsObj = new ThumbnailDetails();
detailsObj = item;
obj.ThumbnailDetailsList.Add(detailsObj);
}
model.ThumbnailModelList.Add(obj);
tabNo++;
}
// Getting first tab data
var first = model.ThumbnailModelList.FirstOrDefault();
// Getting first tab data
var last = model.ThumbnailModelList.LastOrDefault();
foreach (var item in model.ThumbnailModelList)
{
if (item.ThumbnailTabNo == first.ThumbnailTabNo)
{
item.Thumbnail_ItemPosition = "first";
}
if (item.ThumbnailTabNo == last.ThumbnailTabNo)
{
item.Thumbnail_ItemPosition = "last";
}
}
return View(model);
}
View:
#model ThumbnailPagination.Models.ThumbnailViewModel
#{
ViewBag.Title = "Home Page";
}
<div class="container">
<div class="col-xs-10 col-md-6 col-xs-offset-1 col-md-offset-3">
<div class="row">
<nav aria-label="...">
<ul class="pager" role="tablist">
<li class="previous" onclick="goTo(1);"><span aria-hidden="true">←</span> Previous</li>
#{
foreach (var item in Model.ThumbnailModelList)
{
if (item.Thumbnail_ItemPosition == "first")
{
<li class="active" id="#item.Thumbnail_ItemPosition">
<a aria-controls="#item.Thumbnail_Aria_Controls" data-toggle="tab" href="#item.Thumbnail_Href" role="tab">#item.ThumbnailTabNo</a>
</li>
}
else if (item.Thumbnail_ItemPosition == "last")
{
<li id="#item.Thumbnail_ItemPosition">
<a aria-controls="#item.Thumbnail_Aria_Controls" data-toggle="tab" href="#item.Thumbnail_Href" role="tab">#item.ThumbnailTabNo</a>
</li>
}
else
{
<li>
<a aria-controls="#item.Thumbnail_Aria_Controls" data-toggle="tab" href="#item.Thumbnail_Href" role="tab">#item.ThumbnailTabNo</a>
</li>
}
}
}
<li class="next" onclick="goTo(2);">Next <span aria-hidden="true">→</span></li>
</ul>
</nav>
</div>
<!-- Tab panes -->
<div class="tab-content">
#{
foreach (var item in Model.ThumbnailModelList)
{
if (item.Thumbnail_ItemPosition == "first")
{
<div class="tab-pane active" id="#item.ThumbnailTabId" role="tabpanel">
#{
foreach (var detailsitem in item.ThumbnailDetailsList)
{
<div class="col-sm-6">
<div class="thumbnail">
<img alt="..." src="http://placehold.it/240x150">
<div class="caption">
<h3>#detailsitem.Details1</h3>
<p>
#detailsitem.Details2
</p>
<p>
<a class="btn btn-primary" href="#" role="button">
Read more
...
</a>
</p>
</div>
</div>
</div>
}
}
</div>
}
else
{
<div class="tab-pane" id="#item.ThumbnailTabId" role="tabpanel">
#{
foreach (var detailsitem in item.ThumbnailDetailsList)
{
<div class="col-sm-6">
<div class="thumbnail">
<img alt="..." src="http://placehold.it/240x150">
<div class="caption">
<h3>#detailsitem.Details1</h3>
<p>
#detailsitem.Details2
</p>
<p>
<a class="btn btn-primary" href="#" role="button">
Read more
...
</a>
</p>
</div>
</div>
</div>
}
}
</div>
}
}
}
</div>
</div>
</div>
<style>
.pager .active a {
background-color: #337AB7;
color: #FFF;
border: 0px;
}
</style>
<script>
function goTo(number) {
$('ul.pager li:eq(' + number + ') a').tab('show');
upgradePreNext(number);
}
function upgradePreNext(number) {
if (number > 1) {
$('ul.pager li:eq(0)').attr("onclick", "goTo(" + (number - 1) + ")");
$('ul.pager li:eq(0)').attr("class", "previous");
} else {
$('ul.pager li:eq(0)').attr("class", "disabled");
}
if (number < 5) {
$('ul.pager li:eq(6)').attr("onclick", "goTo(" + (number + 1) + ")");
$('ul.pager li:eq(6)').attr("class", "next");
} else {
$('ul.pager li:eq(6)').attr("class", "disabled");
}
}
$(document).ready(function () {
$('li a').on('click', function (e) {
goTo((e.target.innerHTML) - 0);
});
});
</script>
Out put will be:
You can also download the sample code.