Change td style based on value in angular - css

I have a column where the style of each td needs to be changed based on value. There are five status's, so there has to be five different border-colors and font-colors. How do I do this in angular script without hard-coding (new to angular)?
What I am doing is:
In html:
<table st-table = "tenants" class="table table-striped table-bordered">
<tbody>
<tr dir-paginate="t in tenants | orderBy:sortColumn:reverseSort | filter:searchText | itemsPerPage:itemsPerPage" current-page="currentPage" >
<td ng-if="t.status.color==2"><b class = 'td_status'>{{t.status.id}}<b></td>
In css:
.td_status {
border-radius: 20px;
width: auto;
height: auto;
min-width: 100px;
display: inline-block;
text-align: center;
border: 1px solid red;
padding: 5px;
}
In js:
datax.forEach(function(obj,i){
if (obj.last_paid) {
if (obj.stripe_status == "active") {
obj.status = {
'id' : "Paid: Last Paid " + $filter('date')(obj.last_paid, 'MM-dd-yyyy'),
'value': "Paid: Last Paid " + $filter('date')(obj.last_paid, 'yyyy-MM-dd HH:mm:ss Z'),
'color' : 0
}
}
else if (obj.stripe_status == "past_due" || obj.stripe_status == "unpaid") {
obj.status = {
'id' : "Past Due: Last Paid " + $filter('date')(obj.last_paid, 'MM-dd-yyyy'),
'value': "Past Due: Last Paid " + $filter('date')(obj.last_paid, 'yyyy-MM-dd HH:mm:ss Z'),
'color' : 4,
}
}....

I made an example since it is a little slow at work.
Here you can see my use of ng-class. I give it a function and pass the status which is defined in $scope.data item.status comes from my ng-repeat
<div ng-app="TestApp" ng-controller="TestController">
<p ng-repeat="item in data" ng-class="getBorderColor(item.status)">
{{item.name}}
</p>
</div>
Below I have my controller and some sample data. The getBorderColor runs through its conditions and returns className based off of status.
var app = angular.module('TestApp', []);
app.controller('TestController', function($scope) {
$scope.data = [
{
name:'Ronnie',
status:1
},
{
name:'Chance',
status:2
},
{
name:'Mike',
status:1
},
{
name:'Mark',
status:3
}];
$scope.getBorderColor = function(status) {
var className = '';
if (status == 1) {
className = 'status1';
} else if (status == 2) {
className = 'status2';
} else if (status == 3) {
className = 'status3';
}
return className;
};
});
And my simple css is:
.status1 {
border:1px solid red;
}
.status2 {
border:1px solid blue;
}
.status3 {
border:1px solid green;
}
https://jsfiddle.net/ojzdxpt1/7/

Related

How to pass variables to SuiteQL

I am trying to pass parameters from the current record to an embedded Suitelet though cannot find the right syntax to do this anywhere
My starting point was using the Suitelet code here: https://timdietrich.me/blog/netsuite-suiteql-query-results-custom-tabs/
The result from my modified version is per this screencap
When the page loads, the SuiteQL record field is populated using the current record's internal Id.
What I want to do is pass this value as a variable to the embedded SuiteQL Suitelet so that the search results are only in relation to the stated record (in this case, the record with the internal id=24486).
Is this actually possible?
If it is possible, how do I pass this value to the SQL query parameters?
This is the full modified version of the code:
/**
* #NApiVersion 2.1
* #NScriptType UserEventScript
* #NModuleScope Public
*/
/*
*/
var log, query, serverWidget;
define([
"N/log",
"N/query",
"N/ui/serverWidget",
"N/record",
"N/runtime",
"N/recordContext",
], main);
function main(
logModule,
queryModule,
serverWidgetModule,
recordModule,
runtimeModule,
recordContextModule
) {
log = logModule;
query = queryModule;
serverWidget = serverWidgetModule;
record = recordModule;
runtime = runtimeModule;
recordContext = recordContextModule;
return {
beforeLoad: beforeLoad,
};
}
function beforeLoad(context) {
if (context.type !== context.UserEventType.VIEW) {
return;
}
var suiteqlTab = context.form.addTab({
id: "custpage_sql_tab",
label: "SuiteQL Tab",
});
context.form.insertTab({
tab: suiteqlTab,
nexttab: "items",
});
var recordId = runtime.getCurrentScript();
log.debug({
title: "recordId",
details: recordId,
});
var parms = context.request.parameters;
log.debug("params", parms);
log.debug("recordid", parms.id);
var id = parms.id;
var recordField = context.form.addField({
id: "custpage_suiteql_record",
type: serverWidget.FieldType.TEXT,
label: "SuiteQL Record",
container: "custpage_sql_tab",
});
recordField.defaultValue = id;
var suiteqlField = context.form.addField({
id: "custpage_suiteql_field",
type: serverWidget.FieldType.TEXT,
label: "SuiteQL Query Results",
container: "custpage_sql_tab",
});
var records = sqlQueryRun();
context.newRecord.setValue({
fieldId: "custpage_suiteql_field",
value: sqlResultsTableGenerate(records),
});
}
function sqlQueryRun() {
var sql = `
SELECT
Transaction.type
FROM
Transaction
Where Transaction.type like 'SalesOrd'
`;
return query.runSuiteQL({ query: sql, params: [] }).asMappedResults();
}
function sqlResultsTableGenerate(records) {
if (records.length === 0) {
return "<div><p>No records were found.</p></div>";
}
let thead = `
<thead>
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Email</th>
<th>Phone #</th>
</tr>
</thead>`;
var tbody = "<tbody>";
for (r = 0; r < records.length; r++) {
var record = records[r];
tbody += `
<tr>
<td></td>
<td></td>
<td>${record.email}</td>
<td>${record.phone || ""}</td>
</tr>`;
}
tbody += "</tbody>";
let stylesheet = `
<style type = "text/css">
/* Styled Table */
/* https://dev.to/dcodeyt/creating-beautiful-html-tables-with-css-428l */
.styled-table {
border-collapse: collapse;
margin: 25px 0;
font-size: 0.9em;
font-family: sans-serif;
min-width: 400px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
width: 100%;
}
.styled-table th,
.styled-table td {
padding: 6px;
}
.styled-table thead tr {
background-color: #607799;
color: #ffffff;
text-align: left;
}
.styled-table tbody tr {
border-bottom: thin solid #dddddd;
}
.styled-table tbody tr:nth-of-type(even) {
background-color: #f3f3f3;
}
.styled-table tbody tr.active-row {
font-weight: bold;
color: #009879;
}
.styled-table tbody tr:hover {
background-color: #ffff99;
}
</style>
`;
return `
${stylesheet}
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.25/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.js"></script>
<div style="margin-top: 6px; border: 1px solid #ccc; padding: 24px;">
<table id="sqlResultsTable" class="styled-table" style="width: 100%;">
${thead}
${tbody}
</table>
</div>
<script>
window.jQuery = window.$ = jQuery;
$('#sqlResultsTable').DataTable( { "pageLength": 10, "lengthMenu": [ 10, 25, 50, 75, 100 ] } );
</script>
`;
}
The original author of the SuiteQL tool: Timothy Dietrich

Change background color of a single mat-card in Angular

This is the HTML code of my mat-card list in my Angular application:
<div [ngClass]="verticalResultsBarStyle">
<div class="innerDiv">
<mat-card [ngClass]="barStyle" *ngFor="let card of obs | async | paginate: { id: 'paginator', itemsPerPage: 10, currentPage: currentPage, totalItems: totalItems }" (click)="highlightCard()">
<mat-card-header>
<mat-card-title>{{card[0].title}}</mat-card-title>
<mat-card-subtitle>{{card[0].subtitle}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>{{card[0].info1}}</p>
<p>{{card[0].info2}}</p>
<p>{{card[0].info3}}</p>
<p>{{card[0].info4}}</p>
<p>{{card[0].info5}}</p>
</mat-card-content>
</mat-card>
<pagination-controls (pageChange)="OnPaginateChange($event)" id="paginator"></pagination-controls>
</div>
</div>
this is the CSS:
.verticalResultsBarContainerEnabled {
background-color: #eeeeee;
width: 16%;
float: right;
overflow: visible;
}
.verticalResultsBarContainerDisabled {
display: none;
}
.card {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
}
.cardHighlight {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
background-color: #c12400;
color: white;
}
.innerDiv {
height: 755px;
overflow: auto;
}
.paginator {
margin: auto;
width: 92%;
}
and finally this is a snippet of TS :
highlightCard() {
if (this.barStyle === 'card') {
this.barStyle = 'cardHighlight';
} else {
this.barStyle = 'card';
}
}
I would to click on a single mat card and highlight only the selected mat card, but when I click on a single card, the entire list changes color. How can I fix that?
You need to use index for each of your cards. Your issue is normal.
I see barStyle is one generall for all your cards. You need to update barStyle for each card, when your cards come from backend.
First, in your .ts :
service.subscribe(cards => {
let i = 0
cards.forEach(card => {
this.barStyle[i] = // your default barStyle;
i++;
});
If you have an id for each car, better, you can avoid to use i.
then, in template:
<mat-card [ngClass]="barStyle[i]" *ngFor="let card of obs; let i = index | async | paginate: { id: 'paginator', itemsPerPage: 10, currentPage: currentPage, totalItems: totalItems }" (click)="highlightCard(i)">
I see you use async function in template. So, in ts, you expect and Observable. To update style of each card, you need to subscribe at your service and update objects. So, need to do some changes:
cards: Observable will become cards: Card[];
loadingData = true;
call the service as:
getData() {
this.loadingData = true;
service().subscribe(cards => {
cards.forEach(card => {
this.barStyle[card.id] = // your default barStyle;
this.cards = cards;
this.loadingData = false; // this is set false only after data is loaded
});
}
highlightCard(id: number) {
if (this.barStyle[id] === 'card') {
this.barStyle[id] = 'cardHighlight';
} else {
this.barStyle[id] = 'card';
}
}
in template:
<mat-card ***ngIf="!loadingData"**
[ngClass]="barStyle[card.id]"
*ngFor="let card of obs | async |
paginate: { id: 'paginator',
itemsPerPage: 10,
currentPage: currentPage,
totalItems: totalItems }"
(click)="highlightCard(card.id)">
Somethimes, your template will render before to load data, so you need to use a boolean to wait for it.
**To keep clean code, for pagination you can use a variable initialized in .ts*

How to add color to React tic-tac-toe Tutorial?

Hello I am trying to learn react and just followed this amazing tutorial on the website. The code solution is here on CodePen
I was trying to figure out a way to show the 3 in a row that one to the user but I realize that takes css changes on just specific letters.
Is there anyway to do that in the calculateWinner function or do I need to add a class in the button attrib in the Square function and pass some sort of boolean value to add a class?
Square function:
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
calculateWinners Function:
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] ===
squares[c]) {
return squares[a];
}
}
return null;
}
You can do so with this small change :
Css add :
button.square[data-pro="X"] {
color: red;
}
button.square[data-pro="O"] {
color: green;
}
JS just change function Square(props) to :
function Square(props) {
return (
<button className="square" /* adding this */ data-pro={props.value} onClick={props.onClick}>
{props.value}
</button>
);
}
see example :
function Square(props) {
return (
<button className="square" data-pro={props.value}onClick={props.onClick}>
{props.value}
</button>
);
}
class Board extends React.Component {
renderSquare(i) {
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
render() {
return (
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [
{
squares: Array(9).fill(null)
}
],
stepNumber: 0,
xIsNext: true
};
}
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? "X" : "O";
this.setState({
history: history.concat([
{
squares: squares
}
]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext
});
}
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0
});
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move}>
<button onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
});
let status;
if (winner) {
status = "Winner: " + winner;
} else {
status = "Next player: " + (this.state.xIsNext ? "X" : "O");
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={i => this.handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(<Game />, document.getElementById("root"));
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol,
ul {
padding-left: 30px;
}
.board-row:after {
clear: both;
content: "";
display: table;
}
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
/* new css */
button.square[data-pro="X"] {
color: red;
}
button.square[data-pro="O"] {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="errors" style="
background: #c00;
color: #fff;
display: none;
margin: -20px -20px 20px;
padding: 20px;
white-space: pre-wrap;
"></div>
<div id="root"></div>
SHORT: if you need only mark ALL user choices (w/o css)
const styles = {}
if(!! props.value) { // not undefined
styles.color = props.value === "X" ? 'green': 'red'
}
return (
<button className="square" onClick={props.onClick} style={styles} >
{props.value}
</button>
)
LONGER: if you want to learn sth about react (props passing) and mark only winning line
f.e. calculateWinner can return object:
return {winner: squares[a], match: lines[i]}
and later
const result = calculateWinner(this.state.squares);
const winner = result.winner;
pass result.match to renderSuare and add class when i exist in 'match' array
Of course both ways can be combined - good excercise?

Animate a quizz app with AngularJS

I had done one quiz application, But i want to add some animations
like fadein/fade-out, when click the prev/next button. Can any one
help me do the same. something need to change the css something need to change the CSS something need to change the css something need to change the css?
* {}
body {}
.question {
width: 70%;
margin: 0 auto;
height: auto;
display: block;
background: #eeeeee;
}
.question h1 {
text-align: center;
padding-top: 30px;
color: #666666;
}
.question h2 {
width: 100%;
font-size: 22px;
color: #0c1e5c;
padding: 1% 3% 0% 3%;
}
.question ul:nth-child(odd) {
background: #d0dff6;
width: 30%;
padding: 8px;
margin: 1% 9%;
display: inline-block;
color: #0c1e5c;
}
.question ul:nth-child(even) {
background: #d0dff6;
width: 30%;
padding: 8px;
margin: 1% 9%;
display: inline-block;
color: #0c1e5c;
}
.button {
text-align: center;
margin: 1% 0;
}
.btn {
background: #8bf8a7;
padding: 5px;
}
<html ng-app="quiz">
<head>
<meta charset="utf-8">
<title>Basic Quiz</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body ng-controller="quizCtrl">
<div class="question">
<h1>QUIZ APPLICATION</h1>
<h2>{{questions.question}}</h2>
<ul ng-repeat="option in questions.options">
<li style="list-style:none">
<input type="{{buttonType}}">{{option.text}}</li>
</ul>
</div>
<div class="button">
<input type="button" value="previous" class="btn" ng-show="isPrevious" ng-click="previousQuestion()">
<input type="button" value="next" class="btn" ng-show="isNext" ng-click="nextQuestion()">
</div>
</body>
<script>
var app = angular.module("quiz", [])
app.controller("quizCtrl", function($scope) {
$scope.data = [{
question: "1)Which of the following selector matches a element based on its id?",
type: "single",
options: [{
text: "The Id Selector"
},
{
text: "The Universal Selector"
},
{
text: "The Descendant Selector"
},
{
text: "The Class Selector"
}
]
},
{
question: "2)Which of the following defines a measurement as a percentage relative to another value, typically an enclosing element?",
type: "multiple",
options: [{
text: "%"
},
{
text: "cm"
},
{
text: "percentage"
},
{
text: "ex"
}
]
},
{
question: "3)Which of the following property is used to set the background color of an element?",
type: "single",
options: [{
text: "background-color"
},
{
text: "background-image"
},
{
text: "background-repeat"
},
{
text: "background-position"
}
]
},
{
question: "4)Which of the following is a true about CSS style overriding?",
type: "multiple",
options: [{
text: "Any inline style sheet takes highest priority. So, it will override any rule defined in tags or rules defined in any external style sheet file."
},
{
text: "Any rule defined in tags will override rules defined in any external style sheet file."
},
{
text: "Any rule defined in external style sheet file takes lowest priority, and rules defined in this file will be applied only when above two rules are not applicable."
}
]
}
];
$scope.index = 0;
$scope.questions = $scope.data[$scope.index];
$scope.buttonType = $scope.questions.type == 'single' ? 'radio' : 'checkbox';
$scope.isPrevious = false;
$scope.isNext = true;
$scope.nextQuestion = function() {
if ($scope.index < 3) {
$scope.index = $scope.index + 1;
$scope.questions = $scope.data[$scope.index];
$scope.buttonType = $scope.questions.type == 'single' ? 'radio' : 'checkbox';
$scope.isPrevious = true;
if ($scope.index == 3) {
$scope.isNext = false;
}
} else {
// disble next botton logic
$scope.isNext = false;
}
}
$scope.previousQuestion = function() {
if ($scope.index > 0) {
$scope.index = $scope.index - 1;
$scope.questions = $scope.data[$scope.index];
$scope.buttonType = $scope.questions.type == 'single' ? 'radio' : 'checkbox';
$scope.isNext = true;
if ($scope.index == 0) {
$scope.isPrevious = false;
}
} else {
// disble next botton logic
$scope.isPrevious = false;
}
}
});
</script>
</html>
Check out ng-animate, basically what it does is it adds classes that you can style accordingly on showing dom and on hiding dom, like this:
/* The starting CSS styles for the enter animation */
.fade.ng-enter {
transition:0.5s linear all;
opacity:0;
}
/* The finishing CSS styles for the enter animation */
.fade.ng-enter.ng-enter-active {
opacity:1;
}
And to use that functionality you would have to use ng-repeat in your html, something like this:
<div ng-repeat="item in data" ng-if="index === $index">
//Your question html here
</div>
Where data and index are $scope.data and $scope.index.
That would be the angular way of doing things.
However I see that you are using the same div, only changing scope data, that would require you to set
transition: 1s all ease;
On the question class, and then to do something like this in javascript:
angular.element('.question').css('opacity', 0);
$timeout(function() {
// change question..
angular.element('.question').css('opacity', 1);
}, 1000);

Turn cell content to editable input box

When creating a fluid layout, where content can be dragged around and edited inside a table I ran into a problem.
After clicking on any of the <a></a> hyperlinks the cell content should be replaced by an editable input box.
This gets done, but the cell changes its size and wrecks the original layout.
The cell size should not change after click. It should be possible to achieve this by editing the CSS and adding Bootstrap classes.
var viewModel = function() {
var self = this;
self.gridItems = ko.observableArray(
[{
"rowItems": [{
"name": "Item 1"
}, {
"name": "Item 2"
}, {
"name": "Item 3"
}]
}, {
"rowItems": [{
"name": "Item 4"
}, {
"name": "Item 5"
}]
}]
);
self.selectedRowItem = ko.observable();
};
//connect items with observableArrays
ko.bindingHandlers.sortableList = {
init: function(element, valueAccessor, allBindingsAccessor, context) {
$(element).data("sortList", valueAccessor()); //attach meta-data
$(element).sortable({
update: function(event, ui) {
var item = ui.item.data("sortItem");
if (item) {
//identify parents
var originalParent = ui.item.data("parentList");
var newParent = ui.item.parent().data("sortList");
//figure out its new position
var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
if (position >= 0) {
originalParent.remove(item);
newParent.splice(position, 0, item);
}
ui.item.remove();
}
},
connectWith: '.sortable-container'
});
}
};
//attach meta-data
ko.bindingHandlers.sortableItem = {
init: function(element, valueAccessor) {
var options = valueAccessor();
$(element).data("sortItem", options.item);
$(element).data("parentList", options.parentList);
}
};
//control visibility, give element focus, and select the contents (in order)
ko.bindingHandlers.visibleAndSelect = {
update: function(element, valueAccessor) {
ko.bindingHandlers.visible.update(element, valueAccessor);
if (valueAccessor()) {
setTimeout(function() {
$(element).focus().select();
}, 0); //new RowItems are not in DOM yet
}
}
}
ko.applyBindings(new viewModel());
//$(".sortable").sortable({});
.sortable {
list-style-type: none;
margin: 0;
padding: 0;
width:100%;
}
.sortable li {
margin: 0 3px 3px 3px;
padding: 0.4em;
padding-left: 1.5em;
font-size: 1.4em;
height: 18px;
cursor: move;
}
.sortable li span {
position: absolute;
margin-left: -1.3em;
}
.sortable li.fixed {
cursor: default;
color: #959595;
opacity: 0.5;
}
.sortable-grid {
width: 100% !important;
}
.sortable-row {
height: 100% !important;
padding: 0 !important;
margin: 0 !important;
display: block !important;
}
.sortable-item {
border: 1px solid black;
margin: 0 !important;
}
.sortable-item > a {
display: block;
margin: 0 !important;
}
.sortable-item input {
display: block;
margin: 0 !important;
}
.sortable-container {
margin: 0 !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.0-beta.1/themes/smoothness/jquery-ui.css" rel="stylesheet" />
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul class="sortable sortable-grid" data-bind="template: { name: 'gridTmpl', foreach: gridItems, templateOptions: { parentList: gridItems} }, sortableList: gridItems">
</ul>
<script id="gridTmpl" type="text/html">
<li class="sortable-row">
<table style="width:100%">
<tbody>
<tr class="sortable sortable-container" data-bind="template: { name: 'rowTmpl', foreach: rowItems, templateOptions: { parentList: rowItems} }, sortableList: rowItems">
</tr>
</tbody>
</table>
</li>
</script>
<script id="rowTmpl" type="text/html">
<td class="sortable-item" data-bind="sortableItem: { item: $data, parentList: $data.parentList }">
<input data-bind="value: name, visibleAndSelect: $data === $root.selectedRowItem()" />
</td>
</script>
On your table, set table-layout to fixed. Another improvement would be to make the inputs take up the entire space of the cell.
Here are the css changes to make:
.sortable-item input {
display: block;
margin: 0 !important;
width: 100%; /* Added this property */
}
/* Added this rule */
.sortable-row > table {
table-layout: fixed;
}

Resources