I am trying to implement a feature where a user would be able to add new columns and rows to a table using vue js. I am able to push a tag to the table however the method I'm using to add the I don't think its the right way especially since the td's are being added to the object but only to the view itself.
I am pulling the default html table content from laravel's helper file
(object)array(
'label' => 'Table',
'field_name' => '',
'type' => 'table',
'properties' => (object)array(
'headers' => [
'Header 1',
],
'rows' => [
(object) array(
'value' => 'Row 1 content',
),
],
)
)
Html(vue js)
...
<div v-if="field.type == 'table'">
<pre>{{field.properties.headers}}</pre><br>
<pre>{{field.properties.rows}}</pre>
<table :class="`table ` + field.properties.style" :id="`table`+index">
<thead>
<tr>
<th v-for="(header, index) in field.properties.headers" v-if="header.length > 0">
{{header}}
<i class="fa fa-trash"></i>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in field.properties.rows" :key="index" id="default-row">
<td>{{row.value}}</td>
</tr>
</tbody>
</table>
<table>
<tr>
<td>
Add Column
Add Row
</td>
<td></td>
<td></td>
</tr>
</table>
</div>
...
Script(vue js)
...
methods: {
addTableColumn(properties, index) {
properties.headers.push('Column Heading');
$(`#table${index} #default-row`).append(`<td>Row Content</td>`);
},
removeTableColumn(properties, index) {
properties.headers.splice(index, 1);
properties.rows.splice(index, 1);
},
addTableRow(properties, index) {
// let columnCount = properties.headers.length;
// for(let i = 0; i < columnCount; i++) {
// properties.rows.push({'value': 'Row content'});
// }
// console.log(columnCount);
// console.log(index);
// rows.push({ 'value': 'Row column' });
$(`#table${index}`).append(`<tr>${$('#default-row').html()}</tr>`);
}
},...
View
As seen in the picture below, I clicked the "add column" button and only the heading itself is pushed to the array and not the new td content(Row content).
Related
I have a cypress test that checks the table on a page, however there are two different tables that could render depending on the table type conditional. If the type of the table is product one table will render, if the type is equipment a different table will render.
How can I write this cypress test to check IF that specific table is on the page, then run a certain set of checks and vis versa?
Test description:
The conditional is if the selectedIssueKind is Product or Equipment. The next page will have tables with different column headers and info.
My thought was to have the test check the data-class and then do the tests for that data-class table. So:
if ('[data-cy=product-issue-tracker-table]') { product table tests} else {equipment table tests}
However, if I click on an equipment issue I get an error that it cannot find Part ID, but that's because it's trying to check the wrong table (product), so my if ('[data-cy=product-issue-tracker-table]') check, doesnt seem to work.
Any tips for working with conditionals would be greatly appreciated! If you need anymore information please let me know!
Cheers!
Cypress test
it.only('Issue Tracker table exists, column headers are correct and there is data', () => {
if ('[data-cy=product-issue-tracker-table]') {
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Issue ID')
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Part ID')
cy.get('[data-cy=issue-tracker-table] table').contains(
'th',
'Station Name'
)
cy.get('[data-cy=issue-tracker-table] table').contains(
'th',
'Description'
)
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'SN')
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Timestamp')
cy.contains('td', /\w/g).should('be.visible')
} else {
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Issue ID')
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Part')
cy.get('[data-cy=issue-tracker-table] table').contains(
'th',
'Station Name'
)
cy.get('[data-cy=issue-tracker-table] table').contains(
'th',
'Description'
)
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'Start Time')
cy.get('[data-cy=issue-tracker-table] table').contains('th', 'End Time')
cy.contains('td', /\w/g).should('be.visible')
}
})
HTML
<div data-cy="issue-tracker-table">
<div class="vgt-wrap issue-tracker-table-style cursor-pointer mb-12" data-cy="product-issue-tracker-table" pagination="[object Object]">
<!--v-if--><div class="vgt-inner-wrap"><!--v-if--><!--v-if--><!--v-if-->
<div class="vgt-fixed-header"><!--v-if-->
</div>
<div class="vgt-responsive">
<table id="vgt-table" class="vgt-table condensed">
<colgroup>
<col id="col-0">
<col id="col-1">
<col id="col-2">
<col id="col-3">
<col id="col-4">
<col id="col-5">
<col id="col-6">
</colgroup>
<!-- Table header -->
<thead>
<tr><!--v-if--><!--v-if-->
<th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-0" style="min-width: auto; width: auto;">
<span>Issue ID</span>
<!--v-if-->
</th>
<th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-1" style="min-width: auto; width: auto;">
<span>Part ID</span>
<!--v-if-->
</th>
<th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-2" style="min-width: auto; width: auto;">
<span>Station Name</span><!--v-if-->
</th>
<th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-3" style="min-width: auto; width: auto;">
<span>Description</span><!--v-if-->
</th>
<th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-4" style="min-width: auto; width: auto;">
<span>SN</span>
<!--v-if-->
</th>
<th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-5" style="min-width: auto; width: auto;">
<span>Timestamp</span>
<!--v-if-->
</th>
<th scope="col" class="vgt-left-align" aria-sort="descending" aria-controls="col-6" style="min-width: auto; width: auto;">
<span></span>
<!--v-if-->
</th>
</tr><!--v-if-->
</thead><!-- Table body starts here -->
<tbody><!-- if group row header is at the top --><!--v-if--><!-- normal rows here. we loop over all rows -->
<tr class="">
<!--v-if--><!--v-if-->
<td class="vgt-left-align">
<span>1</span>
</td>
<td class="vgt-left-align">
<span></span>
</td>
<td class="vgt-left-align">
<span></span>
</td>
<td class="vgt-left-align">
<span> Connect to DUT by SSH</span>
</td>
<td class="vgt-left-align">
<span></span>
</td>
<td class="vgt-left-align">
<span>Mar 28 2022 - 10:01 AM</span>
</td>
<td class="vgt-left-align"><!--v-if--><!--v-if-->
<div data-cy="issue-tracker-view-data-button">
<button class="primary-button">View Data</button>
</div><!--v-if-->
</td>
</tr><!-- if group row header is at the bottom --><!--v-if-->
</tbody><!--v-if-->
</table>
</div><!--v-if--><!--v-if-->
</div>
</div><!--v-if--><!-- Issue Data Modals --><!--v-if--><!--v-if--><!-- Issue Data Modals --><!-- Add New Analysis section -->
<div class="flex">
<span class="tab font-normal text-card-orange bg-card-orange bg-opacity-20 py-1 px-4 mb-3 mr-9 rounded-full w-max">Analysis Records</span>
<!--v-if--><!--v-if-->
<div data-cy="add-new-analysis-button">
<button class="primary-button">Add New Analysis</button>
</div><!--v-if--></div>
</div>
There's a bit of repetition in the test that may be reduced by getting an array of table headers.
You may be testing more than headers, but this gives you the idea.
const headers = ['Issue ID', ...] // add all the common headers
cy.get('[data-cy="issue-tracker-table"]')
.then($table => {
return $table.find('[data-cy=product-issue-tracker-table]').length > 0
})
.then(console.log) // for checking isProduct value
.then(isProduct => {
if (isProduct) {
return headers.concat(['SN', 'Timestamp'])
} else {
return headers.concat(['Start Time', 'End Time'])
}
})
.then(console.log) // for checking headers array
.then(headers => {
// One test here
cy.get('[data-cy=issue-tracker-table] table thead th')
.each(($th, index, $list)) => {
// last header is empty, presume you don't want to test it
if (index < $list.length -1 ) {
expect($th.text()).to.eq(headers[index])
}
})
})
Breaking down the steps is useful as well, you can incrementally check each step by (temporarily) logging the result of .then().
I notice the <!--v-if--> which shows you are using a Vue app.
If you want to get really sophisticated you can have the app tell Cypress which kind of table is being displayed. This is an app-action and it reduces risks with asynchronous loading.
The exact code depends on your Vue component, but the pattern is
// in Vue component
if (window.Cypress) {
window.tableType = tableType
}
// in test
cy.window()
.then(win => {
return win.tableType === 'Product'
})
.then(console.log) // for checking isProduct value
.then(isProduct => {
...
Testing all <td> has content and visibility
cy.get('[data-cy=issue-tracker-table] table tbody td')
.each($td => {
cy.wrap($td)
.contains(/\w/)
.should('be.visible')
})
Alternative step: tableType instead of isProduct
cy.get('[data-cy="issue-tracker-table"]')
.then($table => {
if ($table.find('[data-cy=product-issue-tracker-table]').length > 0) {
return 'Product'
} else {
return 'Equipment'
}
})
.then(console.log) // for checking tableType value
.then(tableType => {
if (tableType === 'Product') {
return headers = headers.concat(['SN', 'Timestamp'])
} else {
return headers = headers.concat(['Start Time', 'End Time'])
}
})
.then(console.log) // for checking headers array
.then(headers => {
// One test here
cy.get('[data-cy=issue-tracker-table] table thead th')
.each(($th, index, $list) => {
// test header here
if (index < $list.length -1 ) {
expect($th.text().trim()).to.eq(headers[index])
}
})
})
So, this sort of indeterminate testing is not a great practice. The ideal would be to know for sure that you are testing one table or the other.
The conditional is if the selectedIssueKind is Product or Equipment. The next page will have tables with different column headers and info.
Based on that description, I'd think that we could easily split this into two (or more) tests.
// pseudo-code
describe('table tests', () => {
beforeEach(() => {
// shared setup
})
it('tests the product table', () => {
// in the product test, we'll select the `selectedIssueKind` of product
// unclear from the HTML/Cypress how you're doing that, so very vague line below
cy.get('product').click();
// code to test the product table
});
it('tests the equipment table', () => {
// in the equipment test, we'll select the `selectedIssueKind` of equipment
// same as above, very vague example
cy.get('equipment').click();
// code to test the equipment table
});
});
Also, if every test in your spec needed a selectedIssueKind, you could move that logic into your beforeEach(), set an environment variable in your it(), and not repeat that in each test.
// psuedo-code
describe('test', () => {
beforeEach(() => {
// other setup
// grab the cypress environment variable as your selector
cy.get(Cypress.env('selectedIssueKind')).click();
})
it('product test', { env: selectedIssueKind: 'product' } }, () => {
// code
});
it('equipment test', { env: selectedIssueKind: 'equipment' } }, () => {
// code
});
});
currently i am working on order report i am fetching all order records but except status i have last_status column id which is defined by id with every order but i want to show status with order please help me how can i do that ? Does anyone have an idea please help me thanks.
status codes
'0' = 'Pending',
'1' = 'Info Received',
'2' = 'In Transit',
'3' = 'Out for Delivery',
'4' = 'Failed Attempt',
'5' = 'Delivered',
'6' = 'Exception',
'7' = 'Expired',
'8' = 'Canceled',
'9' = 'Void',
reportController
public function report(Request $request)
{
$data = [];
// sets the start date at index 0 and ending date at index 1
$date = explode(' - ', $request->date);
$Order = Order::with('users');
$order = $Order->whereBetween('created_at', $date)
->orderBy('created_at', 'desc')
->get();
$data = [
'orders' => $order,
];
// return $data['orders'];
return view('cms.reports.report-list', $data);
}
html view
<table class="table table-bordered table-striped" cellspacing="0" cellpadding="0">
<thead>
<tr>
<th>Order Id</th>
<th>Customer Name</th>
<th>Status</th>
<th>Order Received Date</th>
</tr>
</thead>
<tbody>
#foreach($orders as $order)
<tr>
<td>{{$order->id}}</td>
<td>{{$order->users->fname ?? null}} {{$order->users->lname ?? null}}</td>
<td>{{$order->last_status}}</td>
<td>{{$order->created_at}}</td>
</tr>
#endforeach
</tbody>
<tfoot>
</tfoot>
</table>
Rather than having to add a CASE statement every time you query, you could add the status to a config file and reference from there.
config/orders.php
<?php
return [
'status' => [
0 => [
'label' => 'Pending',
'color' => 'grey',
],
1 => [
'label' => 'Info Received',
'color' => 'teal',
],
2 => [
'label' => 'In Transit',
'color' => 'blue',
],
...
],
];
create a blade component:
resources/views/components/status-label.blade.php
<span
class="text-sm font-medium bg-{{ config('orders.status.'.$status.'color') }}-100 py-1 px-2 rounded text-{{ config('orders.status.'.$status.'color') }}-500 align-middle">
{{ config('orders.status.'.$status.'label') }}
</span>
Then in your blade file, you can do:
<x-status-label :status="$order->last_status" />
you can do this way...
in order model class
public function orderStatus()
{
switch($this->order_status){
case '1':
return 'Info Received';
break ;
case '2':
return 'In Transit';
break ;
default:
return 'Pending';
}
}
in view
{{ $order->orderStatus() }}
solution with html markup
in order model class
public function orderStatus()
{
switch($this->order_status){
case '1':
return '<span class="label label-warning">Info Received</span>';
break ;
case '2':
return '<span class="label label-info">In Transit</span>';
break ;
default:
return '<span class="label label-danger">Pending</span>';
}
in view
{!! $order->orderStatus() !!}
and css class
.label {
color:white;
display:inline-bolck;
paddign:3px;
text-align: center;
line-height:25px;
min-width: 50px;
}
.label-info{
background:blue;
}
.label-danger{
background:red;
}
i have a table with checkbox as array and textbox as also array. what i want to achieve is when the user checked a checkbox it should validate that input textbox are not be empty.
public function roombooking(Request $request)
{
$messsages = array(
'check.required'=>'No room was selected.Please select room to proceed for booking !',
'txtnos.required|numeric'=>'Please enter no of persons',
);
$rules = array(
'check'=>'required',
'txtnos'=>'required_with:data', //txtnos is a array input filed and data is a array checkbox
);
$validator = Validator::make($request->all(), $rules,$messsages
);
if ($validator->fails()) {
return Redirect::back()
->withErrors($validator)
->withinput();
}
}
Html code
<table class="table table-hover" data-toggle="table" id="table"
data-click-to-select="true">
<thead>
<tr>
<th style="width:10%;" data-field="ActivityId">Select</th>
<th style="width:30%;" data-field="ActivityName">Activity Name</th>
<th style="width:30%;" data-field="Rate">Rate/Person</th>
<th style="width:30%;">Nos. of person</th>
</tr>
</thead>
<tbody>
#foreach($loadactivity as $key=>$activity)
<tr>
<td>
<input type="checkbox" name="data[]" value="0;{!! $activity->ActivityId !!};{!! $activity->Rate !!};0;0;{!! $activity->ActivityName !!}" />
</td>
<td>{!! $activity->ActivityName !!}</td>
<td>{!! $activity->Rate !!}</td>
<td >{!! Form::text('txtnos[]','',['class' => 'form-control small-textbox ','txtnoid'=>$activity->ActivityId]) !!}</td>
</tr>
#endforeach
</tbody>
</table>
please help me
Change your form:
...
<td><input type="checkbox" name="row[{{$key}}][data]" value="0;{!! $activity->ActivityId !!};{!! $activity->Rate !!};0;0;{!! $activity->ActivityName !!}" />
</td>
<td>{!! $activity->ActivityName !!}</td>
<td>{!! $activity->Rate !!}</td>
<td>{!! Form::text('row[{{$key}}][txtnos]','',['class' => 'form-control small-textbox ','txtnoid'=>$activity->ActivityId]) !!}</td>
...
So the only thing that's changed is the name of data and txtnos, it will give you the following:
$exampleResult = [
'row' => [
// old $key as new key
0 => [
'txtnos' => 'entered value',
'data' => '1', // But only if checked
],
1 => [
'txtnos' => 'entered value',
'data' => '1', // But only if checked
],
]
];
Validation rules
$rules = [
'row.*.txtnos' => 'required_with:row.*.data'
];
In the example, txtnos on each row is required if data on the same row isset.
Validation message
$messages = [
'row.*.txtnos.required_with' => 'Enter a value or uncheck the checkbox..'
];
Important:
The validation for .*. was added in Laravel 5.2, you didn't specify your exact version so I'm not sure if it will work for you. Anyway, there is another way to do this.
For versions < 5.2, loop the input rows and replace the * in my example with the current key.
I have a MultiSelect belongs to Kendo ListView EditorTemplates. When I want to submit selected MultiSelect values, I got list of items selected, but all populated with 0 value. I cant get correct value of selected items.
Here is my ListView:
#(Html.Kendo().ListView<esCMS.Modules.C2C.Domain.c2cFieldRangeList>()
.Name("listView")
.TagName("div")
.ClientTemplateId("template")
.DataSource(dataSource => dataSource
.Model(model => model.Id(x => x.FieldRangeID))
.PageSize(6)
.Create(create => create.Action("AddRange", "Field"))
.Update(update => update.Action("UpdateRange", "Field"))
.Read(read => read.Action("ReadRange", "Field", new { id = Model.FieldID }))
))
And here is my EditorTemplates:
<table>
<tr>
<td>
#Html.LabelFor(x => x.RangeValue)
</td>
<td>
:
</td>
<td>
#Html.TextBoxFor(x => x.RangeValue, new { #class = "k-textbox", style = "width:180px" })
<span data-for="RangeValue" class="k-invalid-msg"></span>
</td>
</tr>
<tr>
<td>
#Html.LabelFor(x => x.SelectedValues)
</td>
<td>
:
</td>
<td>
#(Html.Kendo().MultiSelectFor(x=> x.SelectedValues)
.HtmlAttributes(new { style = "width:180px" })
.BindTo(new SelectList(ViewBag.Fields, "FieldID", "Title")))
</td>
</tr></table>
Edit: ViewBag.Fields contains List of c2cFieldList model and I expect to get List of FieldID when I submit view.
ViewBag.Fields = Entities.c2cField.Where(x => !x.IsGeneral && !x.IsTemp).Select(x => new c2cFieldList { Title = x.Title, FieldID = x.FieldID});
Any advice will be helpful.
I want to add an id to the "tr" elements of the mvccontrib grid I build:
<tr id="0"/>
<tr id="1"/>
so if the table contains 10 rows, the ids are 0 through to 9.
One way is to add an additional item to my entity to store this value and then create this as a hidden column with the id as the value of this item - not very elegant.
Is there a more elegant way to do this?
Thanks
I've got this far but now it complains at the RenderUsing line, any ideas?
#model IEnumerable<Tens.Models.UserPreviousNamesView>
<div class="demo_jui">
#{
var userId = 0;
foreach (var item in Model)
{
userId = item.Id;
break;
}
#(Html.Grid(Model.Select((item,index) => new { Item = item, Index = index}))
.Columns(col =>
{
col.For(p => p.Item.Title);
col.For(p => p.Item.Name);
col.Custom(#<text>
#Ajax.ActionLink("Delete", "DeleteUserPreviousName", "Summary", null, null, new { id = item.Item.Id, #class = "deleteUserPreviousName" })
</text>).Encode(false);
})
.RowAttributes(p => new Hash(Id => "id"+p.Item.Index.ToString()))
.Attributes(Id => "userPreviousNamesTable")
.Empty("You currently have no Previous Names.")
.RenderUsing(new Tens.GridRenderers.UserPreviousNamesGridRenderer<Tens.Models.UserPreviousNamesView>()));
}
You could transform the model to add it a row index and then use the RowAttributes method:
#model IEnumerable<MyViewModel>
#(Html
.Grid(Model.Select((item, index) => new { Item = item, Index = index }))
.Columns(column =>
{
column.For(x => x.Item.Foo);
column.For(x => x.Item.Bar);
})
.RowAttributes(x => new Hash(id => string.Format("id{0}", x.Item.Index)))
)
Also I have pre-pended the id with the id keyword as ids in HTML cannot statr with a number as shown in your example.
Sample output:
<table class="grid">
<thead>
<tr>
<th>Foo</th>
<th>Bar</th>
</tr>
</thead>
<tbody>
<tr id="id0" class="gridrow">
<td>foo 1</td>
<td>bar 1</td>
</tr>
<tr id="id1" class="gridrow_alternate">
<td>foo 2</td>
<td>bar 2</td>
</tr>
<tr id="id2" class="gridrow">
<td>foo 3</td>
<td>bar 3</td>
</tr>
</tbody>
</table>
You can always show hide columns without adding id to particular row or columns like below
$(".mvcGridDollarHeader th:nth-child(16)").hide();
$(".mvcGrid td:nth-child(16)").hide();
Where mvcGrid is tableStyle and mvcGridDollarHeader is header style.
#grid1.GetHtml(
tableStyle: "mvcGrid",
displayHeader: true,
emptyRowCellValue: "",
headerStyle: "mvcGridDollarHeader",