Trying to get a dom-if to work - dart

I am trying to remove and add a template content using the a paper-toggle-button. However, I am not able to see the contents of the template even though the toggle is occurring.
The code used is shown below:
.dart
PolymerRegister( 'test-if-form' )
class TestIfForm extends PolymerElement {
TestIfForm.created( ) : super.created( );
#Property( observer: 'togglerChanged' )
//#property
bool toggler = false;
#reflectable
void toggleNormalChangedEvent( event, [_] ) {
toggler = !toggler;
print( 'toggler $toggler' );
}
#reflectable
void togglerChanged( newValue, oldValue ) {
print( 'new value $newValue' );
if ( newValue ) {
toggler = !toggler;
}
}
}
.html
<dom-module id = "test-if-form">
<template>
<div
class = "layout horizontal center-justified"
id = "normal-changed-toggler-container">
<span class = "margin-rt-5px">Normal</span>
<paper-toggle-button
required
on-change = "toggleNormalChangedEvent"
id = "togglerBtn">Changed
</paper-toggle-button>
<div>
<hr>
</div>
</div>
<br>
<template is = "dom-if"
if = "{{toggler}}"
restamp>
<div>I am now visible</div>
</template>
</template>
</dom-module>
The contents of the nested div element is never visible. It does not seem that the if is being called?
Thanks for your help.

toggler = !toggler;
should be
set('toggler', !toggler);

Related

Read span value in angular reactive forms

<span class="headmain" style="cursor:pointer" (click)="modalshow(ratingImage,el.name+i,el)" >
hello</span>
How to get the value from span element in similar to formControlName?
Span element is in a form tag.
You can use #ViewChild / ElementRef.
HTML:
<span #mySpan (click)="handleClick()">
Hallo from span.
</span>
TS:
export class AppComponent {
#ViewChild("mySpan", { static: false }) mySpanRef: ElementRef;
public handleClick(): void {
const spanValue = this.mySpanRef.nativeElement.innerHTML;
console.log("Span-Value: ", spanValue);
}
}
Working stackblitz

Cannot read property id of undefined, error found in the map function (react-rails)

I'm trying to make my submit button add on to the list of items, my seed data in rails gets rendered fine but when I try to add a new item when clicking submit, I get this error Cannot read property id of undefined which is found at the map function. The post request works fine, and only after I refresh the page, the item I add gets rendered on the page. Any help would be appreciated!
class TodoList extends React.Component {
render() {
const {todos} = this.props
var todoItems = todos.map(title => <TodoItem key={title.id} title={title}/>) //here
return (
<ListGroup className="my-2">
<h2 className="text-center">Items</h2>
{todoItems}
</ListGroup>
)
}
}
export default TodoList
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
todos: [],
stuff: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.addNewTodo = this.addNewTodo.bind(this)
}
handleChange = event => {
this.setState({
stuff:event.target.value
})
}
handleSubmit = event => {
event.preventDefault()
let body = JSON.stringify({todo: {item: this.state.stuff} })
fetch('http://localhost:3000/api/v1/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: body,
})
.then(response => {response.json()})
.then(todo => {this.addNewTodo(todo)})
}
addNewTodo(todo){
this.setState({
todos: this.state.todos.concat(todo)
})
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-10 mx-auto mt-4">
<h1 className="text-center">Todo List</h1>
<TodoInput
stuff={this.state.stuff}
handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
/>
<TodoList
todos={this.state.todos}
/>
</div>
</div>
</div>
)
}
}
when your component is renders first "todos" won't have any value, it will be undefined. try below code
class TodoList extends React.Component {
render() {
const { todos } = this.props;
if (todos) {
return (
<ListGroup className="my-2">
<h2 className="text-center">Items</h2>
{todos.map(title => (
<TodoItem key={title.id} title={title} />
))}
</ListGroup>
);
}
return ""; } }
export default TodoList;

How to customize mat-paginator in angular material

I want to customize mat-paginator .By default i am getting paginator like this which have given in below link
https://material.angular.io/components/paginator/overview.
But i want pagination like below image .
How can i do this using mat-paginator
Can anyone please help me with this.
UPDATE 08/20/2020
Using #uhamid answer as inspiration, along with several comments below, indicating this is in fact possible, I revised my first attempt to provide a complete solution.
Although Umair Hamid example below is functional, it did not include the styling required. It also leveraged ngDoCheck which introduces recursive type behavior and is likely to introduce performance issues.
I also refactored most of the logic to make for a more complete solution.
Use it like:
<mat-paginator style-paginator showFirstLastButtons [showTotalPages]="3" [length]="7130" [pageSize]="10" [pageSizeOptions]="[5, 10, 25, 100]"> </mat-paginator>
Provide page buttons to display as input [showTotalPages], if not provided it will default to 2
STACKBLITZ
https://stackblitz.com/edit/angular-wyx2ue-bw95ug?embed=1&file=app/style-paginator.directive.ts
REVISED STACKBLITZ 8/20/2020
https://stackblitz.com/edit/angular-8holwx?file=src/app/style-paginator.directive.ts
With the help of #Marshal, I have created a directive for pagination with dot gap.
Copy this code in your directive
import {
AfterViewInit,
Directive,
DoCheck,
Host,
Optional,
Renderer2,
Self,
ViewContainerRef,
} from '#angular/core';
import { MatPaginator } from '#angular/material';
#Directive({
selector: '[appStylePaginator]',
})
export class StylePaginatorDirective implements AfterViewInit, DoCheck {
public currentPage = 1;
public directiveLoaded = false;
public pageGapTxt = '...';
constructor(
#Host() #Self() #Optional() private readonly matPag: MatPaginator,
private readonly vr: ViewContainerRef,
private readonly ren: Renderer2,
) {}
private buildPageNumbers(pageCount, pageRange) {
let dots = false;
const paglast = pageCount;
const pagcurrent = this.matPag.pageIndex;
const showTotalPages = 8;
for (let i = 0; i < paglast; i = i + 1) {
if (
i === pagcurrent ||
(pagcurrent < showTotalPages && i < showTotalPages) ||
(i > pagcurrent - (showTotalPages - 1) && i < pagcurrent) ||
i > paglast - 1 ||
(i > pagcurrent && i < pagcurrent + showTotalPages)
) {
this.ren.insertBefore(pageRange, this.createPage(i, this.matPag.pageIndex), null);
} else {
if (i > pagcurrent && !dots) {
this.ren.insertBefore(pageRange, this.createPage(this.pageGapTxt, this.matPag.pageIndex), null);
dots = true;
}
}
}
}
private createPage(i: any, pageIndex: number): any {
const linkBtn = this.ren.createElement('mat-button');
this.ren.addClass(linkBtn, 'mat-icon-button');
const pagingTxt = isNaN(i) ? this.pageGapTxt : +(i + 1);
const text = this.ren.createText(pagingTxt + '');
this.ren.addClass(linkBtn, 'mat-custom-page');
switch (i) {
case pageIndex:
this.ren.setAttribute(linkBtn, 'disabled', 'disabled');
break;
case this.pageGapTxt:
this.ren.setAttribute(linkBtn, 'disabled', 'disabled');
break;
default:
this.ren.listen(linkBtn, 'click', () => {
this.currentPage = i;
this.switchPage(i);
});
break;
}
this.ren.appendChild(linkBtn, text);
return linkBtn;
}
private initPageRange(): void {
const pagingContainerMain = this.vr.element.nativeElement.querySelector('.mat-paginator-range-actions');
if (
this.vr.element.nativeElement.querySelector('div.mat-paginator-range-actions div.btn_custom-paging-container')
) {
this.ren.removeChild(
pagingContainerMain,
this.vr.element.nativeElement.querySelector('div.mat-paginator-range-actions div.btn_custom-paging-container'),
);
}
const pagingContainerBtns = this.ren.createElement('div');
const refNode = this.vr.element.nativeElement.childNodes[0].childNodes[0].childNodes[2].childNodes[5];
this.ren.addClass(pagingContainerBtns, 'btn_custom-paging-container');
this.ren.insertBefore(pagingContainerMain, pagingContainerBtns, refNode);
const pageRange = this.vr.element.nativeElement.querySelector(
'div.mat-paginator-range-actions div.btn_custom-paging-container',
);
pageRange.innerHtml = '';
const pageCount = this.pageCount(this.matPag.length, this.matPag.pageSize);
this.buildPageNumbers(pageCount, pageRange);
}
private pageCount(length: number, pageSize: number): number {
return Math.floor(length / pageSize) + 1;
}
private switchPage(i: number): void {
this.matPag.pageIndex = i;
this.matPag._changePageSize(this.matPag.pageSize);
}
public ngAfterViewInit() {
setTimeout(() => {
this.directiveLoaded = true;
}, 500);
}
public ngDoCheck() {
if (this.directiveLoaded) {
this.initPageRange();
}
}
}
After that you just need to add this directive in our module's entryComponents.
Use it like:
<mat-paginator
appStylePaginator //<<== Use of directive
(page)="pageChangeEvent($event)"
[length]="pageLength"
[pageSize]="pageSize"
showFirstLastButtons
>
</mat-paginator>
The output is now:
I wanted a custom paginator with both forward and backward dots with event emitter. So, I improved the #marshal's directive by clearing many bugs and modified to make it work exactly like the ngx-pagination.
Just wanted to share in case if anyone needs this, the code is here Repo link
Insert the buttons from mat-paginator I think it is not possible but you can create the custom pager:
paginator-configurable-example.html
<button mat-button (click)="page.previousPage()"><</button>
<button mat-button (click)="updateManualPage(1)" >1</button>
<button mat-button (click)="updateManualPage(2)">2</button>
<button mat-button (click)="updateManualPage(3)">3</button>
<button mat-button (click)="page.nextPage()">></button>
<mat-paginator style="visibility:hidden" [pageIndex]="pageIndex" #page [length]="100" [pageSize]="10" [pageSizeOptions]="[5, 10, 25, 100]" (page)="pageEvent = $event" ></mat-paginator>
<div *ngIf="pageEvent">
<h5>Page Change Event Properties</h5>
<div>List length: {{pageEvent.length}}</div>
<div>Page size: {{pageEvent.pageSize}}</div>
<div>Page index: {{pageEvent.pageIndex}}</div>
</div>
paginator-configurable-example.ts
import {Component} from '#angular/core';
import {PageEvent} from '#angular/material/paginator';
/**
* #title Configurable paginator
*/
#Component({
selector: 'paginator-configurable-example',
templateUrl: 'paginator-configurable-example.html',
styleUrls: ['paginator-configurable-example.css'],
})
export class PaginatorConfigurableExample {
// MatPaginator Inputs
length = 100;
pageSize = 10;
pageSizeOptions: number[] = [5, 10, 25, 100];
manualPage = null;
// MatPaginator Output
pageEvent: PageEvent;
setPageSizeOptions(setPageSizeOptionsInput: string) {
this.pageSizeOptions = setPageSizeOptionsInput.split(',').map(str => +str);
}
public updateManualPage(index: number): void {
this.manualPage = index;
this.pageEvent.pageIndex = index;
}
public clearManualPage(): void {
this.manualPage = 0;
}
}
Marshal is incorrect. You can set the pageIndex property of the material paginator
https://material.angular.io/components/paginator/api#MatPaginator
I mimicked exactly what you were trying to do. Hard coded so you'll have to figure it out how to add buttons based on the number of pages but here you go.
<button mat-fab (click)="page.previousPage()"><</button>
<button mat-fab (click)="page.pageIndex = 0">1</button>
<button mat-fab (click)="page.pageIndex = 1">2</button>
<button mat-fab (click)="page.pageIndex = 2">3</button>
<button mat-fab (click)="page.nextPage()">></button>
<mat-paginator style="visibility:hidden" #page [length]="100" [pageSize]="10" [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>
You can find here my custom directive (StylePaginatorDirective) inspired by the answer provided by #Marshal but completely rewriten from scratch in order to shows the pagination inside the mat-paginator-range-label
Preview
Stackblitz
https://stackblitz.com/edit/angular-wyx2ue-ayitwa?file=app%2Fstyle-paginator.directive.ts
https://angular-wyx2ue-ayitwa.stackblitz.io
Positioning
Feel free to customize the ordering of your component with custom css class: https://stackoverflow.com/a/55969038/2835268

Error | Removing disallowed attribute on SelectElement

I am trying to use a button to add a new row in an html table when the button is clicked, and another button to remove the row/s with its checkbox checked. I am experience two problems:
Whenever the add-button is clicked I get Removing disallowed attribute SELECT on-change="{{ onChangeTypeFired }}"
If I select two checkboxes and clicked the delete row button it throws an indexIndexSizeError: Index or size was negative, or greater than the allowed value. The index provided (2) is greater than the number of rows in the table (2).
The files are shown below
.html
<!DOCTYPE html>
<link rel='import' href='../../../../packages/polymer/polymer.html' >
<link rel='import' href='../../../../packages/paper_elements/paper_checkbox.html' >
<link rel='import' href='../../../../packages/paper_elements/paper_button.html' >
<link rel='import' href='../../../../packages/core_elements/core_collapse.html' >
<link rel='import' href='../../../../packages/core_elements/communication_icons.html' >
<polymer-element name='phone-form'>
<template>
<div
class='parent-container'>
<paper-button id='add-btn' on-click='{{toggle}}'>
<core-icon id='toggle-btn-icon' icon=''></core-icon>
Phone
<core-icon id='validation-icon' class='margin-l5px' icon=''></core-icon>
</paper-button>
<table id='table' border='1' width='350px'>
<tbody><tr>
<td><input name='chk' type='checkbox'></td>
<td>
<select id='phoneType' class='width95percent'
selectedIndex='{{typeSelected}}'
on-change='{{onChangeTypeFired}}'>
<option template repeat='{{key in types.keys}}'
value='{{types[key]}}'>{{types[key]}}
</option>
</select>
</td>
</tr>
</tbody></table>
</div>
<div>
<paper-button raised id='add-row-btn' class='margin-8px'
on-click='{{addRow}}'>
Add Row
<core-icon id='add-row-btn-icon' icon='check-all'></core-icon>
</paper-button>
<paper-button raised id='delete-row-btn' class='margin-8px'
on-click='{{deleteRow}}'>
Delete Selected Row(s)
<core-icon id='delete-row-btn-icon' icon='check'></core-icon>
</paper-button>
</div>
</div>
</template>
<script type='application/dart' src='phone_form.dart'></script>
</polymer-element>
.dart
import 'package:polymer/polymer.dart';
import 'dart:html' as dom;
import 'package:paper_elements/paper_button.dart' show PaperButton;
import 'package:core_elements/core_collapse.dart';
//import 'package:epimss_shared/epimss_shared.dart';
//import 'package:epimss_shared/epimss_shared_client.dart' hide DataEvent;
#CustomTag('phone-form')
class PhoneForm extends PolymerElement {
#observable String errorMsg;
String topic;
PaperButton addBtn;
int typeSelected = 0;
CoreCollapse coreCollapse;
#observable
Map<String, String> types = <String, String> {
'': '',
'Car': 'Car',
'Direct': 'Direct',
'Fax': 'Fax',
'Home': 'Home',
'Mobile': 'Mobile',
'Video': 'Video',
'Work': 'Work'
};
PhoneForm.created() : super.created();
void toggleCoreCollapse() {
coreCollapse.toggle();
}
void onSelectTypeFired()
{
}
void onChangeTypeFired( dom.Event e, var detail, dom.SelectElement target)
{
print(target.value);
}
void addRow()
{
var table = $['table'];
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
var colCount = table.rows[0].cells.length;
for(var i = 0; i<colCount; i++ )
{
var newcell = row.insertCell(i);
newcell.innerHtml = table.rows[0].cells[i].innerHtml;
switch(newcell.childNodes[0].runtimeType.toString())
{
case 'text':
newcell.childNodes[0].value = '';
break;
case 'checkbox':
newcell.childNodes[0].checked = false;
break;
case 'select':
newcell.childNodes[0].selectedIndex = 0;
break;
}
}
}
void deleteRow()
{
var rowsToDelete = [];
try{
var table = $['table'];
var rowCount = table.rows.length;
for(var i = 0; i < rowCount; i++)
{
var row = table.rows[i];
var chkbox = row.cells[0].childNodes[0];
if(chkbox != null && chkbox.checked)
{
if(rowCount <= 1)
{
print('Cannot delete all the rows.');
break;
}
else
{
rowsToDelete.add(i);
}
}
}
rowsToDelete.forEach( (row)
{
table.deleteRow(row);
});
}
catch(e)
{ print(e); }
}
#override
void attached() {
super.attached();
topic = this.dataset['topic'];
coreCollapse = $['core-collapse'];
addBtn = $['add-btn'];
}
}
Thanks for your help.
It is probably caused by this line
newcell.innerHtml = table.rows[0].cells[i].innerHtml;
You need to specify which elements and tags are allowed to be added to the DOM from a string.
see https://stackoverflow.com/a/27334820/217408 for more details.
That doesn't apply when you generate the elements imperatively like new DivElement().
In your example it would probably easier and better to do something like
newcell.children.clear();
newcell.children.addAll(table.rows[0].cells[i].children.map((c) =>
c.clone(true)));
Caution: I'm not sure about how the code should exactly look like but I think you get the idea. If you can't work it out add a comment and I'll have a closer look.

In Dart-polymer I have been unable to find a way to keep a text-box scrolled to the bottom

I have found multiple ways to do this in various browsers and languages but nothing I can find has worked for Dart-polymer in Chrome.
Otherwise everything is very simple and standard:
<template>
<style>
textarea {
width: 825px;
}
</style>
<div>
<textarea id="ta" rows="10" on-mouseover="{{on_mouse_over}}">
{{message}}
</textarea>
</div>
</template>
Thanks!
I guess this is what you want:
#CustomTag('my-text')
class MyText extends PolymerElement {
#observable String message = "";
MyText.created() : super.created();
void messageChanged(old) {
var ta = $['ta'];
ta.scrollTop = ta.scrollHeight;
}
void attached() {
super.attached();
new Timer.periodic(new Duration(seconds: 1), (_) => message += 'some text ${new DateTime.now()}\n');
}
}
I added id="ta" to the <textarea> to make $['ta'] work

Resources