*ngIf=item.activityStatus!=("Done") as angular2 conditional visibility - html

I want to toggle the visibility of a button based of a value of an item
Here is my code:
<tbody>
<tr *ngFor="let item of statusConfig | slice:0:9; let i = index;" >
<td class="col-md-1">
{{item.id}}
</td>
<td class="col-md-3">
{{item.activityName}}
</td>
<td class="col-md-2">
{{item.activityStatus}}
</td>
<td class="col-md-3">
{{item.activityDateEpoch | date: 'yyyy-MM-dd HH:mm:ss'}}
</td>
<td class="col-md-3">
<button *ngIf=item.activityStatus!=("Done") type="button" class="btn btn-primary" (click)="saveStatus(item.id, i)">Mark as done</button>
</td>
</tr>
</tbody>
why is this a syntax error and how should I fix this?
directive_normalizer.js:82 Uncaught Error: Template parse errors:
Unexpected closing tag "button" ("tyStatus!="Done" type="button" class="btn btn-primary" (click)="saveStatus(item.id, i)">Mark as done[ERROR ->]</button>
</td>
"): ReportComponent#45:145
and
activityStatus = "Done" for now

I'm in shock how many mistakes you managed to make in one line of code. :-)
First: NgIf directive accepts string as expression value, which means it has to have the following form:
*ngIf="expression here"
Second: You can't use " two times, if you want to use string in ", you should use single quotes then (')
Third: Brackets are not needed: ("Done")
Anyway, your expression should look like this:
*ngIf="item.activityStatus != 'Done'"
You can read more about NgIf directive here.

It should be,
*ngIf="(item.activityStatus!='Done')"

Related

Is there a simple way of using the th:data* and th:each attributes together in Thymeleaf?

My intention is to obtain an object inside one of the rows of the table, so I could forward it to a form using a simple JavaScript function. I am not familiar with jQuery so I thought I’ll take an as-simple-as-possible approach. I was thinking of doing this by using the th:data* attribute in combination with th:each, like this:
<tr th:each="employee : ${empDetailsList}" th:data-employee="${employee}">
<td scope="row" th:text="${employee.id}">ID</td>
<td scope="row" th:text="${employee.firstName}">firstName goes here</td>
<td scope="row" th:text="${employee.lastName}">lastName goes here</td>
<td scope="row" th:text="${employee.email}">email goes here</td>
<td scope="row" th:text="${employee.username}">user name goes here</td>
<td th:data-employee="${employee}">
<button type="button" class="btn btn-info" th:onclick="showEditForm(this.getAttribute('data-employee'));">Details</button>
</tr>
I also tried this approach, moving the th:data-employee from the 'tr' tag to the 'td' tag but the result is the same:
<tr th:each="employee : ${empDetailsList}">
...
<td th:data-employee="${employee}">
<button type="button" class="btn btn-info" th:onclick="showEditForm(this.getAttribute('data-employee'));">Details</button>
</tr>
The JS function:
function showEditForm(employee){
console.log('Employee object here: ');
console.log(employee.firstName);
console.log(employee.lastName);
}
As I mentioned I get the same result in both cases. The browser consoles response is:
Employee object here:
TypeError: employee is null
So, what am I doing wrong here?
This worked for me:
<p th:each="testScript:${testScripts}">
<a th:href="|/scripts/start/${testScript}|" th:text="'Start '+ ${testScript}"/>
<a th:href="|/scripts/stop/${testScript}|" th:text="'Stop' + ${testScript}"/>
<button id="searchButton" name="searchButton" th:onclick="start(this.getAttribute('data_p1'));" type="button"
th:data_p1="|${testScript}|"
th:text="|${testScript}|">Start</button>
</p>

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">

show a string from backend-server as icon

I get a response from backend server for my angular 2/4 application. The response includes an attribute "connectionStatus", which indicates the status of a database-connection with "UP" or "DOWN". I show the retrieved status and other informations in my view with the following code:
<tbody>
<tr *ngFor='let database of environment.databases'>
<td>{{database.connectionName}}</td>
<td>{{database.dbSourceType}}</td>
<td>{{database.username}}</td>
<td>{{database.password}}</td>
<td>{{database.connectionUrl}}</td>
<td>{{database.creationDate}}</td>
<td>{{database.connectionStatus}}</td>
<td>
<button (click)='onEditDatabase(environment,database)'class="btn btn-primary btn-sm oi oi-cog"></button>
<button (click)='onDeleteDatabase(database)'class="btn btn-danger btn-sm oi oi-trash"></button>
</td>
</tr>
</tbody>
Instead of showing the status as "UP" or "DOWN", i want to use openiconics. If the state is "UP", then use the check-icon, if its down, the flash-icon. How can i realize that? i appreciate all help.
Make use of ngClass
<td [ngClass]="(database.connectionStatus ==='UP')?'upclass':'downclass'"></td>
in the css define the class and icons
.upclass{check-icon}
.downclass{flash-icon}
You can use *ngIf or [hidden] to display one of the status as per your condition.
<td *ngIf="database.connectionStatus">your icon UP html tag here</td>
<td *ngIf="!database.connectionStatus">your icon DOWN html tag here</td>

Populate multiple g:select with an array variable

I am trying to have two multiple select lists that I am trying to do the typical add/remove between the two. I am able to add and remove initially and save the array of values from the second g:select but I cannot seem to repopulate the second g:select when I try to show the user an updated value.
<table class="threecols">
<tr>
<td style="width: 10%">
<g:select multiple="true" id="select1" name="select1" from="${['User','Department','School','Class','User','Id','District']}" disabled="${disabled}"/>
</td>
<td style="width: 10%">
<button type="button" style="margin-left: 12px">Add >>
</button>
<button type="button"><< Remove </button>
</td>
<td style="width: 10%">
<g:select multiple="true" id="select2" name="paramList" from="${report?.paramList}" value="${report?.paramList}" disabled="${disabled}"/>
</td>
</tr>
</table>
and the variable declaration is: String[] paramList
Any help is appreciated.
You need to use JQuery to do this:
$(document).ready(function() {
$('#add').click(function(){
$('#select2 option:selected').each( function() {
$('#select1').append("<option value='"+$(this).val()+"'>"+$(this).text()+"</option>");
$(this).remove();
});
});
$('#remove').click(function(){
$('#select1 option:selected').each( function() {
$('#select2').append("<option value='"+$(this).val()+"'>"+$(this).text()+"</option>");
$(this).remove();
});
});
});
More Info here: Link
I was being stupid and was using report?.paramList when I should have used reportInstance?.paramList since report is just the class where reportInstance was a singular object. Woops.

Knockout.js - Ideas on transforming JSON data to HTML

I have an observableArray self.CustomerOrders which I populate with
self.CustomerOrders.push(new CustomerOrder(self.getOrderId(), today.toLocaleDateString() , self.selectedCustomer2(), JSON.stringify(self.cart(),null,4)));
where
self.getOrderId() is a method to get an Id for the order,
today.toLocaleDateString() prints today's date,
self.selectedCustomer2 is the selected customer of the order and
self.cart is another observableArray which includes all ordered items.
Here is how I populate self.cart
self.cart.push(new orderedItem(product.id, product.name, product.price, product.quantity()));
and here is my foreach
<tbody data-bind="foreach: CustomerOrders">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: date"></td>
<td data-bind="text: customer"></td>
<td data-bind="text: details"></td>
<td data-bind="click: $parent.selectedOrder"><a class="btn btn-primary" data-toggle="modal" data-target="#display-order">View</a>
</td>
<td data-bind="click: $parent.selectedOrder"><a class="btn btn-primary" data-toggle="modal" data-target="#edit-order">Edit</a>
</td>
<td data-bind="click: $parent.selectedOrder"><a class="btn btn-primary" data-toggle="modal" data-target="#delete-order">Delete</a>
</td>
</tr>
</tbody>
</table>
</div>
I succeed in saving all those data to the CustomersOrders observable array and then I print them in my UI using foreach. My problem is that the self.cart items are printed as JSON and I do not want to display JSON to the user but HTML.
How to implement this ?
Any ideas ?
Ok, so don't JSON.stringify your cart. Then, assuming your Details binding is where the cart part is supposed to end up, and it's supposed to be an array, you can just nest foreach bindings like this:
<td>
<ul data-bind="foreach: details">
<li data-bind="text: someProperty"></li>
</ul>
</td>
where someProperty is whatever property of the cart you want to display.
Of course, you can choose whatever html elements suit your requirements.
My problem is that the self.cart items are printed as JSON
Well, that's not surprising.
self.CustomerOrders.push(
new CustomerOrder(
self.getOrderId(),
today.toLocaleDateString(),
self.selectedCustomer2(),
JSON.stringify(self.cart(),null,4) /* guess what that does */
)
);
Just do
self.CustomerOrders.push(
new CustomerOrder(
self.getOrderId(),
today.toLocaleDateString(),
self.selectedCustomer2(),
self.cart
)
);
and use regular knockout bindings in your view to display the cart.