How to align text input elements within the boundaries of a table? - html

Working with some Jinja code in my Flask app, I have a
<table class="table table-hover">
<thead>
<th scope="col" class="text-primary">Name</th>
<th scope="col" class="text-primary">Description</th>
</thead>
<tbody class="text-secondary">
{% for item in char.inventory %}
<tr id="{{item.itemid}}_row">
<div>
<td id="{{item.itemid}}_name">{{item.name}}</td>
</div>
<div>
<td id="{{item.itemid}}_description">{{item.description}}</td>
</div>
<td class="text-right"><button id="{{item.itemid}}" class="btn btn-outline-info" onclick="itemeditor(this)">edit</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
A loop that generates a table that's formatted with some bootstrap. The button when clicked on, leads to some JQuery code that transforms the item and description table entries into text inputs, containing the values of the table entries.
function itemeditor(elem) {
self = $(elem);
itemid = self.attr("id");
itemnamenode = $("#" + itemid + "_name")
itemdescriptionnode = $("#" + itemid + "_description")
itemnamefield = $('<input type="text" style="display : inline;" size="50" class="form-control" />')
itemnamefield.val(itemnamenode.text())
itemdescfield = $(('<input type="text" style="display : inline;" size="50" class="form-control" />'))
itemdescfield.val(itemdescriptionnode.text())
itemnamenode.replaceWith(itemnamefield)
itemdescriptionnode.replaceWith(itemdescfield)
}
The issue is that when the JQuery updates the table entries with their text inputs, the text inputs default to talking up the full width of the table and going under each other rather than spawning next to each other, in the original location of the table entries.
Before the button gets clicked
After the button is clicked
How can I set up the Bootstrap/ CSS such that the text inputs line up with the table?
Thank you very much for all and any help

Your table is not correct.
You can't put div as a closest child of tr.
Let try with this
<tbody class="text-secondary">
{% for item in char.inventory %}
<tr id="{{item.itemid}}_row">
<td>
<div id="{{item.itemid}}_name">{{item.name}}</div>
</td>
<td>
<div id="{{item.itemid}}_description">{{item.description}}</div>
</td>
<td class="text-right"><button id="{{item.itemid}}" class="btn btn-outline-info"
onclick="itemeditor(this)">edit</button>
</td>
</tr>
{% endfor %}
</tbody>
I'm thinking another way.
You don't need to replace the element.
Try to use input only, disabled as default, and only enable it when in editing mode, and also rememeber to style disabled input as a text.

Related

Applying an ngClass on only 1 element of a table in angular

In my table, I'm attempting to add a read more button to each row that has a paragraph. The button is displayed for each row, and when I click one, it operates concurrently for each row.
Here is my code:
<table class="table">
<thead>
<tr>
<th scope="col">Text</th>
<th scope="col">Date</th>
<th scope="col">Comments</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of posts[0].posts">
<div [ngClass]="{'limitTextHeight': isReadMore}">
<td>{{p.post_text}}</td>
</div>
<button type="button" (click)="showText()" style="margin-top:15px">
{{ isReadMore ? 'Read More': 'Read Less' }}
</button>
<td>{{p.post_date}}</td>
<td>{{p.post_comments.length}} comment</td>
</tr>
</tbody>
</table>
and my showText() function:
showText() {
this.isReadMore = !this.isReadMore
}
This is happening because you're using single boolean variable i.e, isReadMore for all the looped tr tag.
What you can do is posts[0].posts map this posts with one more key that can be anything for example isReadMore in your case, then you will get unique instance to handle for each para, like this.
{{ p?.isReadMore ? 'Read More': 'Read Less' }}.
Hope you understood what I'm trying to say.
You are using a common variable for all the rows. That's why when you click one they all change. You should use local ones, one for each row like so:
<table class="table">
<thead>
<tr>
<th scope="col">Text</th>
<th scope="col">Date</th>
<th scope="col">Comments</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of posts[0].posts">
<div [ngClass]="{'limitTextHeight': isReadMore}">
<td>{{p.post_text}}</td>
</div>
<button type="button" (click)="showText(p)" style="margin-top:15px">
{{ p.isReadMore ? 'Read More': 'Read Less' }}
</button>
<td>{{p.post_date}}</td>
<td>{{p.post_comments.length}} comment</td>
</tr>
</tbody>
</table>
and the showText() function:
showText(post) {
post.isReadMore = !post.isReadMore
}
You could also add an *ngIf to check if there is text long enough before showing the button:
<button type="button" *ngIf="p.post_text?.length > 20" (click)="showText(p)" style="margin-top:15px">
{{ p.isReadMore ? 'Read More': 'Read Less' }}
</button>

How to dynamically add HTML element/content - Angular

What would be the best way to dynamically add an HTML element, such as another column onto a basic HTML table?
I want to have a button below the table, and if the user were to click the button, the event would add the same amount of rows already in the table and add another column. I would want to support about 5 extra columns being added to the table.
Here's my HTML table as of right now:
<table>
<thead>
<tr>
<th id="row-tag"></th>
<th id="option-column">Option 1</th>
</tr>
</thead>
<tbody>
<tr>
<td id="row-tag">P</td>
<td id="option-column">{{ p }}</td>
</tr>
<tr id="row-tag">
<td>
<app-a-p (saveButtonClick)="toggleAP($event)"
[modModalValues]="modModalValues">
</app-a-p>
</td>
<td id="option-column">
<div class="input-group input-group-sm">
$<input
*ngIf="toggleAP==true"
type="text"
name="aP"
size="10"
disabled
value=""
/>
<input
*ngIf="toggleAP==false"
type="text"
name="aP"
size="10"
[ngModel]="con.pA"
(ngModelChange)="con.pA = $event" appFormatP
(blur)="checkAP($event.target.value);
inputTracker.addModifiedValue('Option 1', $event.target.value)"
/>
</div>
</td>
</tr>
<tr>
<td id="row-tag">L</td>
<td id="option-column">${{l}}</td>
</tr>
<tr>
<td id="row-tag">R</td>
<td id="option-column">${{r}}</td>
</tr>
</tbody>
</table>
I think in Angular, you define table based on your data. for example, you have fields array defining columns, and data array defines the what's actually in the table.
<table >
<thead>
<tr>
<th *ngFor='let key of this.fields'>{{key}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor='let row of this.data ' >
<td scope="row" *ngFor='let key of this.fields'> {{row[key]}} </td>
</tr>
</tbody>
</table>
when you need a new column, just push a new field into fields. like
fields.push('newcolumn');
when you need a new row, just do:
data.push({col1: '', col2:'', newcolumn: ''});
Look into the insertRow() and insertCell() functions in JavaScript. This alongside an onClick function that you write will let you edit the table.
A good way of generating a table on UI when using angular would be to use 2D-array (row*column) use can have a button using which you can dynamically add values to this array and have another row/column to the table.

Filling all input fields

There is a markup:
<div class="scroll">
<table class="table table-striped">
<thead>
<tr>
<th>Image</th>
<th>Name</th>
<th>Author</th>
<th>Year</th>
<th>ISBN</th>
<th>Price</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let book of bookService.bookList">
<td><img src="../../{{book.ImagePath}}" width="100" height="150"></td>
<td>{{book.Name}}</td>
<td>{{book.Author}}</td>
<td>{{book.Year}}</td>
<td>{{book.ISBN}}</td>
<td>{{book.Price}}</td>
<td>{{book.Count}}</td>
<td>
<input type="text" name="Count" [(ngModel)]="Count">
<button class="btn btn-block btn-outline-success" (click)="onAdd(book, Count)"><i class="fa fa-plus"></i></button>
</td>
</tr>
</tbody>
The last column looks like this:
The problem is the following: when filling one TextBox, all the TextBoxes in the column are filled.
How can I solve this problem? Tried to give unique names to text fields and to thrust this cell in form, but all the same did not work.
You need to give unique name to the input field using angular template
<input [name]="'count' + i" >
Where i is the index from the *ngFor
But I think the major issue you have is that you need to bind book.Count instead of just Count
In latter case you'll have one variable called Count and you bind the same variable to all of the input fields. You need to attach the variable to the book itself so it is unique.
All your inputs have the same [(ngModel)]="Count" and the same name so if you update one, all of them will be updated
You can fix that if you have an array of count instead. So it will be something like
<tr *ngFor="let book of bookService.bookList; let i = index">
...
<input type="text" [name]="'count' + i" [(ngModel)]="count[i]">
People are giving you the HTML way, I'm giving you the Angular way : trackBy functions.
*ngFor="let book of bookService.bookList; trackBy: book.Name"
This one should work but I've never tested it.
The one should work in any case :
*ngFor="let book of bookService.bookList; trackBy: customTB"
customTB(item, index) {
return `${index}-${item.Name}`;
}
You can't use the same name for your inputs.
To fix this, you can add an id populate by the index of the loop *ngFor
Try this :
<tr *ngFor="let book of bookService.bookList; let i = index">
<input type="text" name="Count_${i}" [(ngModel)]="count">

Angular directive nested in table body renders above table

Okay so, I'm using Angular 1.5.7, and I'm trying to do some table rendering with ng-repeat and stuff. This is what my table markup looks like:
<table class="table table-hover">
<thead>
<tr>
<td>Property name</td>
<td>Property value</td>
</tr>
</thead>
<tbody>
<adm-preset-property
ng-repeat="(propertyName, definition) in
componentDefinition.component_properties"
property-name="propertyName"
property-value="component.component_properties"
property-definition="definition"></adm-preset-property>
</tbody>
</table>
The <adm-preset-property> directive has a replace: true property and is rendered from a root <tr></tr> tag.
Now the loop works fine, BUT, instead of the table rows being rendered inside the table body, where they are nested, they are rendered ABOVE the table. I end up with
<tr>
{{ content }}
</tr>
<tr>
{{ content }}
</tr>
<tr>
{{ content }}
</tr>
<table>...</table>
What's worse, I can't seem to reproduce this on JSFiddle. What am I doing wrong?
EDIT: As requested, here's the template for the <adm-preset-property>
Template:
<tr>
<td>
<span data-toggle="tooltip" ng-attr-title="{{ ::propertyDefinition.description }}">{{ ::propertyDefinition.name }}</span>
</td>
<td ng-switch="propertyDefinition.editor_type">
<div ng-switch-when="select">
<ui-select append-to-body="true" ng-model="propertyValue[propertyName]" theme="bootstrap">
<ui-select-match placeholder="Select option...">{{ $select.selected.value }}</ui-select-match>
<ui-select-choices repeat="option.key as (key, option) in propertyDefinition.editor_properties.options | filter:{'value':$select.search} track by $index"
ng-value="key">
{{ option.value | highlight: $select.search }}</ui-select-choices>
</ui-select>
</div>
<div ng-switch-when="boolean">
<input type="checkbox" ng-model="propertyValue[propertyName]">
</div>
<div ng-switch-when="float">
<input type="range" step="0.01" ng-model="propertyValue[propertyName]" min="{{propertyDefinition.editor_properties.min}}"
max="{{propertyDefinition.editor_properties.max}}"> {{ propertyValue[propertyName] }}
</div>
<div ng-switch-when="color">
<input type="color" style="width: 75%;" ng-model="propertyValue[propertyName]">
</div>
<div ng-switch-when="int">
<input type="number" style="width: 75%;" ng-model="propertyValue[propertyName]" min="{{propertyDefinition.editor_properties.min}}"
max="{{propertyDefinition.editor_properties.max}}"> <br/>
<small>{{::propertyDefinition.editor_properties.min}} - {{::propertyDefinition.editor_properties.max}}</small>
</div>
<div ng-switch-default>
<input type="text" style="width: 75%;" ng-bind="propertyValue[propertyName]" />
</div>
</td>
</tr>
Directive:
(function() {
"use strict";
angular
.module('adomee.admin')
.directive('admPresetProperty', admPresetProperty);
/* #ngInject */
function admPresetProperty($log)
{
return {
restrict: 'E',
replace: true,
scope: {
propertyName: '=',
propertyValue: '=',
propertyDefinition: '='
},
templateUrl: 'app/preset/components/preset-property.tmpl.html',
link: function($scope, $element, $attributes) {
if ($scope.propertyDefinition.editor_type === 'select' && typeof($scope.propertyDefinition.defaultValue) === 'number') {
$scope.propertyValue[$scope.propertyName] = String($scope.propertyDefinition.defaultValue);
}
}
}
}
})();
Okay, after some research and consulting with my boss, we have concluded that Angular, naturally, compiles the directive's template into the DOM after the primary markup has been loaded.
Since the table expects a <tr> tag followed by <td>, but it runs into my custom tag instead, it removes the custom tag and places it outside of the table, which then compiles to my template afterwards, resulting in table rows on top of the actual table.
What I did was:
Change restrict: 'E' to restrict: 'A' in the directive.
Remove the replace property from it.
Remove the root <tr> tag and leave two <td> tags.
Place the directive into the table onto a <tr> and ng-repeat it.
Here's what it looks like now.
<tr adm-preset-property
ng-repeat="(propertyName, definition) in componentDefinition.component_properties"
property-name="propertyName"
property-value="component.component_properties"
property-definition="definition"></tr>

Display two lines in a row

I want the value in the controller to be displayed in two lines of the same column.
{{ngapp}}.controller("Controller", function($scope, $http, $modalInstance){
$scope.selected=[];
$scope.items=[
{
value:'Impact to Safety, regulatory compliance environment',
key:10,
color:'red'
},
{
value:'Reliability Impact',
key:9,
color:'brown'
}
]
});
code in html:
<div class="well form-group" >
<table class="table" cellspacing="1">
<tr>
<th>Rank</th>
<th>Criteria: Type of loss incurred</th>
</tr>
<tr ng-repeat="item in items" onclick="selected.item= item" ng-click="sel(item)">
<td ng-style="{ 'background-color': item.color }">{{item.key}}</td>
<td> {{item.value}} </td>
</tr>
</table>
//<label>{{selected.item}}</label>
{% endverbatim %}
</div>
I want the Value:'Impact to Safety' to be displayed in one line. And 'regulatory compliance environment' in a new line for the same column and the same key. Anybody can help? Thanks. I'm new here.
For other parts of the code, refer to this Question,
Display value in a button by linking two modals
You can use https://www.npmjs.com/package/angularjs-filters to do this.
Replace the ',' by '<br />' and it should appear on the next line.
{{item.value | string.replace : ',': '<br />'}}