Use custom data source in angular UI template - html

I am using ui-bootstrap-tpls to render datepicker in my angular view. I have customized the template in this way :
customDatePicker.html
<script id="template/datepicker/day.html" type="text/ng-template">
<table role="grid" aria-labelledby="{{uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th>
<button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1">
<i class="glyphicon glyphicon-chevron-left"></i>
</button>
</th>
<th colspan="{{5 + showWeeks}}">
<button id="{{uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" tabindex="-1" style="width:100%;">
<strong>{{title}}</strong>
</button>
</th>
<th>
<button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1">
<i class="glyphicon glyphicon-chevron-right"></i>
</button>
</th>
</tr>
<tr>
<th ng-show="showWeeks" class="text-center"></th>
<th ng-repeat="label in labels track by $index" class="text-center">
<small aria-label="{{label.full}}">{{label.abbr}}</small>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">
<button type="button" style="width:100%;" class="btn btn-default btn-sm" ng-click="select(dt.date); openCustomDialog(dt.date)" ng-disabled="dt.disabled" tabindex="-1">
<span>{{dt.label}}</span>
</button>
</td>
</tr>
</tbody>
</table>
</script>
<datepicker ng-model="dt" min-date="minDate" show-weeks="true" class="well well-sm"></datepicker>
It's working fine. The problem I am facing is that I have to use custom data in the template in
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">
<button type="button" style="width:100%;" class="btn btn-default btn-sm" ng-click="select(dt.date); openCustomDialog(dt.date)" ng-disabled="dt.disabled" tabindex="-1">
<span>{{dt.label}}</span>
</button>
</td>
</tr>
</tbody>
For ex. I have to add class(to change color) for some kind of event.
Please help.

This is best solved using a directive inserted in the template. (Heres an updated plunker) Notice the highlight-day="dt" directive inserted here. This will bring each day into our custom directive to determine if we need to highlight the day. I prefer this method of highlight as opposed to performing surgery on third party javascript.
<button highlight-day="dt" ng-class="{selected: dt.highlighted}" type="button" style="width:100%;" class="btn btn-default btn-sm" ng-click="select(dt.date); openCustomDialog(dt.date)" ng-disabled="dt.disabled" tabindex="-1">
<span>{{dt.label}}</span>
</button>
Once we have that, we can add a directive that looks like follows. Notice all the logic is done in the link function.
app.directive("highlightDay", ["myCustomObj", "monthMapper", function(myCustomObj, monthMapper){
return {
restrict: "A",
//This brings the value of attribute into our current scope as an object, not just a DOM string
scope: {
highlightDay: "="
},
link: function(scope, element, attrs, ctrls) {
//Make the native date object as a local variable
var dt = scope.highlightDay.date;
//Find out what the month name should be
var monthName = monthMapper[dt.getMonth()];
//Loop through all the possible selected dates
for(var i in myCustomObj){
var entry = myCustomObj[i];
//If the month and day match
var isMatch = entry.month === monthName && entry.day === dt.getDate();
if(isMatch) {
scope.highlightDate.highlighted = isMatch
break;
}
}
}
};
}]);
You also notice the two other dependencies, myCustomObj and monthMapper. These are defined elsewhere angular and could be as I have done below.
app.constant("monthMapper", [
"january",
"february",
"march",
"april",
"may",
"june",
"july",
"august",
"september",
"november",
"december"
]);
app.value("myCustomObj", [{
"month" : 'june',
"day" : 19
},
{
"month" : 'june',
"day" : 28
}
]);
As a side note, you could speed up time determining if the day should be selected by reorganizing myCustomObj maybe something like this.
{
june: [19, 28]
}

I think that the best and fastest way to change the template is first copy the same template and make the adjustments on him as the template already has all the events and necessary classes binded.
The second solution for you is to take all the parts of ng-{{event}} (ng-class, ng-click, ng-...) and to connect them to your template in the same place.
Hope it make sense to you.

Related

Refresh data table after edit row in to database

I have data table in the manage.component.html (portion of code):
<table
class="table table-striped table-hover table-responsive-lg"
[mfData]="links$ | async"
#mf="mfDataTable"
[mfRowsOnPage]="10">
<thead>
<tr>
<th style="width: 5%">
<mfDefaultSorter by="id" #sortId>
ID
<span *ngIf="sortId.isSortedByMeAsc" class="cil-arrow-top" aria-hidden="true"></span>
<span *ngIf="sortId.isSortedByMeDesc" class="cil-arrow-bottom" aria-hidden="true"></span>
</mfDefaultSorter>
</th>
<th style="width: 50%">
<mfDefaultSorter by="linkCategory" #sortCategory>
Category
<span *ngIf="sortCategory.isSortedByMeAsc" class="cil-arrow-top" aria-hidden="true"></span>
<span
*ngIf="sortCategory.isSortedByMeDesc"
class="cil-arrow-bottom"
aria-hidden="true"
></span>
</mfDefaultSorter>
</th>
<th style="width: 50%"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let link of mf.data">
<td>{{ link.id }}</td>
<td>{{ link.linkCategory }}</td>
<td>
<button type="button" class="btn btn-primary mr-2 btn-edit" (click)="editLink(link.id)">
Edit
</button>
<button type="button" class="btn btn-danger ml-2 btn-delete" (click)="deleteLink(link.id)">
Delete
</button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="9">
<mfBootstrapPaginator [rowsOnPageSet]="[5, 10, 15]"></mfBootstrapPaginator>
</td>
</tr>
</tfoot>
</table>
I have service for manage.component with links$ Observable as well:
export class ManageService {
private _links$ = new BehaviorSubject<model.Link[]>([]);
private _query$: BehaviorSubject<string> = new BehaviorSubject('');
links$: Observable<model.Link[]>;
query$: Observable<string>;
constructor(private httpClient: HttpClient) {
this.refreshLinks();
this.links$ = combineLatest([this._links$, this._query$]).pipe(
map(([links, query]) => links.filter(link => link.linkName.toLowerCase().includes(query)))
);
}
getLinks(): Observable<model.Link[]> {
return this.httpClient.get<model.Link[]>('/api/links');
}
findLink(query: string) {
this._query$.next(query);
}
private refreshLinks() {
this.getLinks().subscribe(links => {
this._links$.next(links);
});
}
}
For know everything is working correctly. I see table with data and buttons in every row.
I've prepared new edit.component for edit button:
form in HTML with data prefilled
scss styles
submit button which who is calling createLink method as below (method from edit.service):
public createLink() {
this.link.id = parseInt(this.linkId);
this.service.createLink(this.link).subscribe(res => {
console.log(res);
this.router.navigate(['/links/manage']);
});
this.publishModal.hide();
}
edit.service.ts (portion of code):
createLink(link: model.Link): Observable<model.Link> {
return this.httpClient.post<model.Link>('/api/createLink', link, this.httpOptions);
}
My changed data are sending to database correctly and after submit I have newer data in DB and I'm navigate back to table with whole links.
I see table of links$ but I have to refresh the browser to see updated rows as well.
In console.log(res); I see updated object, in DB I see it also but I don't see them on the screen.
What's wrong?

Why isn't my form posting any data even though I've set the name property?

So I'm trying to post a form with some data, but for some reason when clicking the button that POSTS the data, I'm not getting anything serversided, all the values are NULL.
As you can see it's a simple form that uses razor and I've set the name and the id properties of the elements I want to send
<table class="table table-hover mb-0">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Version</th>
</tr>
</thead>
<tbody>
#foreach (var _model in Model)
{
<tr>
#using (Html.BeginForm("Start", "Dashboard", FormMethod.Post))
{
<div class="form-group">
<td id="serverid" name="serverid">#Html.DisplayTextFor(x => _model.ServerID) #Html.HiddenFor(x => _model.ServerID)</td>
<td>
#Html.DisplayTextFor(x => _model.ServerName) #Html.HiddenFor(x => _model.ServerName)
</td>
<td>
#Html.DisplayTextFor(x => _model.Version) #Html.HiddenFor(x => _model.Version)
</td>
<td>
<button type="submit" asp-action="Start" asp-controller="Dashboard" class="btn btn-icon waves-effect waves-light btn-success" onclick="Start('#_model.ServerName')"> <i class="fa fa-power-off"></i> </button>
</td>
</div>
}
</tr>
}
</tbody>
</table>
And here is what the model looks like
public class ServerModel
{
public string ServerID { get; set; }
public string ServerName { get; set; }
}
And here is the action that's inside the controller
//The "model" parameter here exists, but all the properties are null when inspecting it
public async Task<IActionResult> Start(ServerModel model)
{
//Doing stuff here
}
Why are all the properties inside the model null when inspecting them after posting? I'm putting a breakpoint to inspect it.
Change your submit button to -
<button type="submit" class="btn btn-icon waves-effect waves-light btn-success"> <i class="fa fa-power-off"></i> </button>
As you are within the form element (generated by beginform) you dont need the additional attributes. Also not entirely sure what your intentions are with -
onclick="Start('#_model.ServerName')
Firstly you can remove the onclick="Start('#_model.ServerName')" of submit button since you are passing the data via Form Post , then modify below codes :
#Html.HiddenFor(x => _model.ServerID)
#Html.HiddenFor(x => _model.ServerName)
To :
<input name="ServerID" type="hidden" value="#_model.ServerID">
<input name="ServerName" type="hidden" value="#_model.ServerName">
So it will match name with model's property name during model binding .

applying pagination to a table in angular js where map is what i'm iterating through in ng-repeat

i'm iterating through map where key and value pair is there and i'm not getting how to apply pagination of those data where that map data is generated from the database directly.i'm iterating through map where key and value pair is there and i'm not getting how to apply pagination of those data where that map data is generated from the database directly.
<table>
<thead></thead>
<tbody>
<tr>
<td><marquee>
<h3>
A tag is a keyword or label that categorizes your question with
other, similar questions. Using the right tags makes it easier
for others to find and answer your question.
</h4>
</marquee>
<hr></td>
</tr>
<tr ng-repeat="(key,value) in tagForm.data track by $index">
<td align="left">
<div ng-init='tagForm.getTag(key)'
class=" w3-container w3-card w3-white w3-margin w3-round "
style="padding-left: 40px; padding-right: 40px;">
<br>
<p ng-repeat="data2 in tagForm.message track by $index">
<a ng-click="tagForm.getAnswer(key)">Q. {{value}}</a> <span
class="badge">{{data2}}</span> <br>
</p>
<ul ng-repeat="(key2,value2) in tagForm.ans track by $index">
<li><b ng-if="key==key2">Ans. {{value2}}</b></li>
</ul>
<div ng-init='tagForm.getUser(key)'>
<b>Posted
by:{{tagForm.user2[$index]}}</b>
</div>
<button class="btn btn-default" id="{{$index}}"
ng-click="count1=count1+1" ng-init="count1=5+($index*3)-5">
Upvote <span class="badge">{{count1}}</span>
</button>
<button class="btn btn-default" id="{{$index}}"
ng-click="count=count+1" ng-init="count=1+($index*2)">
Downvote<span class="badge">{{count}}</span>
</button>
<button class="btn btn-default" ng-click="gotoanswer()">Answer
this question</button>
<br> <br />
</div> <br>
</td>
<td height=100px><br /></td>
</tr>
</tbody>
</table>
use the following pagination in your html file
<uib-pagination ng-show="isPaginate == false "
total-items="totalItems" ng-model="currentPage"
boundary-links="true" items-per-page="numPerPage"
class="pagination-sm" ng-change="pageChanged()" max-size="5">
</uib-pagination>
and then initialize the variable as per requirement in angular controller
$scope.totalItems = 80;//length of records
$scope.currentPage = 1;
$scope.numPerPage = 10;
var startpos = 0;
for dynamically loading records (loading records batch wise instead of loading all at time) refer following function
$scope.pageChanged = function() {
// if($scope.currentPage!=1)
$scope.isCollapsed = false;
$scope.isRCollapsed = false;
$scope.page = ($scope.currentPage - 1) * $scope.numPerPage;
callApi($scope.page);//get next set of 10 records
console.log('Page changed to: ' + $scope.currentPage);
};

Angular 2 save all changes from grid

I need to save all changes from editable fields in table.
<tbody>
<tr *ngFor="let a of arr; let i = index">
<td contenteditable>{{a.text}}</td>
<td contenteditable type="number" pattern="[0-9]">{{a.number}}</td>
</tr>
</tbody>
<button class="btn btn-success" (click) = "saveAllChanges()">Save all </button>
--
saveAllChanges(){
for(let a of this.arr){
this.myservice.updateRepeat(r)
}
}
Objects in array arent saved after edit on grid so it doesnt work.
Any idea how can i save all changes?

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.