Refresh sortable when a new item is added to the array - ng2-bootstrap

Sortable component only shows initial array elements. When a new value is pushed into array, the sortable does not display it.
Component:
import { Component } from '#angular/core';
#Component({
selector: 'custom-item-template-demo',
templateUrl: './custom-item-template.html'
})
export class CustomItemTemplateDemoComponent {
public itemStringsLeft: any[] = [
'Windstorm',
'Bombasto',
'Magneta',
'Tornado'
];
public addItem() {
this.itemStringsLeft.push("new item");
}
}
Template:
<button type="button" (click)="addItem()">Add</button>
<template #itemTemplate let-item="item" let-index="index"><span>{{index}}: {{item.value}}</span></template>
{{itemStringsLeft.length}}
<pre>{{ itemStringsLeft | json }}</pre>
<bs-sortable
[(ngModel)]="itemStringsLeft"
[itemTemplate]="itemTemplate"
itemClass="sortable-item"
itemActiveClass="sortable-item-active"
placeholderItem="Drag here"
placeholderClass="placeholderStyle"
wrapperClass="sortable-wrapper"
></bs-sortable>

Workaround: Call manually to writeValue of the SortableComponent
import { Component, ViewChild } from '#angular/core';
#Component({
selector: 'custom-item-template-demo',
templateUrl: './custom-item-template.html'
})
export class CustomItemTemplateDemoComponent {
public itemStringsLeft: any[] = [
'Windstorm',
'Bombasto',
'Magneta',
'Tornado'
];
#ViewChild(SortableComponent, {static: false}) sortableComponent: SortableComponent;
public addItem() {
this.itemStringsLeft.push("new item");
this.sortableComponent.writeValue(this.itemStringsLeft);
}
}

Another workaround with re asigning list values:
public addItem() {
this.itemStringsLeft.push("new item");
this.itemStringsLeft = this.itemStringsLeft.slice();
}

should be add import { SortableComponent } from 'ngx-bootstrap';
It will working.

The spread operator worked well for me with this issue:
addItem(){
this.itemStringsLeft = [...this.itemStringsLeft, {name: 'Windstorm', range: 5}];
}

Related

Inconsistent behaviour of inputs values linked to an array in Angular

I am using an array of numbers in an Angular Component, linked to a series of inputs in the html template, using *ngFor. When a change is detected on a given input, the value of the corresponding cell in the array is updated. I checked it and the values of the array in the components are correctly updated.
However, a new value on a given input sometimes impact the value shown in another input (i.e. the new value is also written in that input). This usually happens with different inputs whose values are initially identical. It also seems to always be an input further in the loop that is impacted.
What is even more strange is that if I display the values of the array as plain text, the values shown are always correct.
I also manually set unique names and ids to the inputs in order to avoid any possible confusion, with no result.
I have made a small jsfiddle code showing the issue. The issue is immediately visible if you change the first input value to '2': the second input will also see its values changed to '2'.
The jsfiddle is using Angular 4, I have tested with Angular 6 and it doesn't work either.
Any idea what I might be doing wrong or where the issue is ?
let { Component, NgModule, OnInit } = ng.core;
#Component({
selector: 'my-app',
template: `
<div *ngFor="let v of values; let i = index;" style="float: left;"><input type="text" name="input-{{i}}" [value]="v" (change)="onChange(i, $event)"/><p>{{ v }}</p></div>
`,
})
class HomeComponent implements OnInit {
public values: number[] = [];
n = 5;
ngOnInit() {
for(let i = 0; i < this.n; i++)
this.values.push(1);
}
onChange(i: number, event)
{
this.values[i] = parseInt(event.target.value);
}
}
const { BrowserModule } = ng.platformBrowser;
#NgModule({
imports: [ BrowserModule ],
declarations: [ HomeComponent ],
bootstrap: [ HomeComponent ]
})
class AppModule { }
const { platformBrowserDynamic } = ng.platformBrowserDynamic;
platformBrowserDynamic().bootstrapModule(AppModule);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div *ngFor="let v of values; let i = index;" style="float: left;">
<input type="text" name="input-{{i}}" [value]="v" (change)="onChange(i, $event)"/>
<p>{{ v }}</p>
</div>
using (input) instead of (change) can working
let { Component, NgModule, OnInit } = ng.core;
#Component({
selector: 'my-app',
template: `
<div *ngFor="let v of values; let i = index;" style="float: left;"><input type="text" name="input-{{i}}" id="input-{{i}}" value="{{v}}" (input)="onChange($event,i)"/><p>{{ v }}</p></div>
`,
})
class HomeComponent implements OnInit {
public values: number[] = [];
n = 5;
ngOnInit() {
for(let i = 0; i < this.n; i++)
this.values.push(1);
}
// event first
onChange(event,i: number)
{
this.values[i] = parseInt(event.target.value);
console.log(event)
}
}
const { BrowserModule } = ng.platformBrowser;
#NgModule({
imports: [ BrowserModule ],
declarations: [ HomeComponent ],
bootstrap: [ HomeComponent ]
})
class AppModule { }
const { platformBrowserDynamic } = ng.platformBrowserDynamic;
platformBrowserDynamic().bootstrapModule(AppModule);
suggest using [(ngModel)] instead of value ,(ngModelChange) instead of (change),do not mixed

ionic 2 get ion-input text to refresh

I have a page with an ion-input in a FormGroup.
When they go to the page, data is loaded from a server. When the data comes in I want the ion-input to be filled in with the data from the server, which the user can then edit and save.
I cannot get the ion-input to show the data. (it remains blank)
Here is the page:
import { Component,ViewChild } from '#angular/core';
import { Validators, FormBuilder, FormGroup } from "#angular/forms";
import { NavController, App, MenuController, NavParams, AlertController,Content } from 'ionic-angular';
import {DomSanitizer,SafeResourceUrl} from '#angular/platform-browser';
import { TabsPage } from '../tabs/tabs';
// Providers
import { ProjectDetailService } from '../../providers/project-detail-service';
#Component({
selector: 'page-form',
templateUrl: 'form.html',
})
export class FormPage {
#ViewChild(Content) content: Content;
newObject: FormGroup;
object: any = {};
objectKey: string = "";
pageTitle: string = "Create New";
videoURL: SafeResourceUrl;
sanitizer: DomSanitizer;
updating: boolean = false;
constructor( public formBuilder: FormBuilder,
public service: ProjectDetailService,
public navParams: NavParams,
public app: App,
public alertCtrl: AlertController,
sanitizer: DomSanitizer ) {
this.sanitizer = sanitizer;
this.newObject = this.formBuilder.group({
name: this.object.name
});
}
setData()
{
this.newObject.value.name = this.object.name;
//none of these work:
//this.content.resize();
//window.location.reload();
//this.app.getRootNav().setRoot(this.app.getRootNav().getActive().component);
}
ionViewDidLoad()
{
this.objectKey = this.navParams.get('projectKey');
console.log("objectkey="+this.objectKey)
this.service.getProject(this.objectKey).subscribe( ( data: any ) => {
this.object = data;
this.setData();
})
}
This is the html:
<ion-content padding>
<form [formGroup]="newObject" (ngSubmit)="save()">
<ion-item>
<ion-label>Project Name</ion-label>
<ion-input type="text" formControlName="name"></ion-input>
</ion-item>
</form>
</ion-content>
I think FormBuilder is not a two way bindier Instead use a simple two way binding like this
<ion-input type="text" [(ngModel)]="name" formControlName="name"></ion-input>
and access as
this.name = 'something';

Runtime Error Cannot read property 'element' of undefined in angular 2?

I have created form builder validators that do a validation perfectly but i need get a error message from validators so i have tried some method that i shown below
import { Injectable,Output,Component} from '#angular/core';
import { FormGroup, FormControl} from '#angular/forms';
import{Sharedata} from '../providers/sharedata';
export class NextPayDayValidator1 {
public error: any="Next Pay Date should be greater than todays date";
constructor(public Share:Sharedata){
}
public isValid(control: FormControl): any {
// console.log("false");
let formGroup = control.parent;
var IsValid=true;
var errormessage;
// this.Err="eerere";
if(formGroup) {
console.log("true");
var SelVal_Howpaid=formGroup.get('Frequency').value;
console.log(SelVal_Howpaid);
var today = new Date();
var today_date = today.getDate();
var today_month = today.getMonth();
var today_year = today.getFullYear();
var weekNo = 0;
for (var index = week[0]; index <= week[1]; index++) {
weekNo++;
}
var nextpaydaycontrol=formGroup.get('NextPayDate');
var date_next = new Date( formGroup.get('NextPayDate').value);
var date_nextpaydate = date_next.getDate();
var month_nextpaydate = date_next.getMonth();
var year_nextpaydate = date_next.getFullYear();
console.log("nextpaydaycontrol"+date_next +"date"+date_nextpaydate+"month"+month_nextpaydate+"year"+year_nextpaydate);
if (nextpaydaycontrol.value == "") {
IsValid = false;
errormessage = "* Please select Next Pay Date";
console.log(errormessage);
this.error=errormessage;
alert(this.error);
return{
" * Please select Next Pay Date":true
}
}
}
}
my addleads.html
<ion-item>
<ion-label>Next Pay Date:</ion-label>
<!--<ion-input formControlName="NextPayDate" type="number"></ion-input>-->
<ion-datetime displayFormat="MM/DD/YYYY" formControlName="NextPayDate" (ionChange)="npay()"></ion-datetime>
</ion-item>
<span style="color:red" *ngIf="!RegisterForm3.controls.NextPayDate.valid && (!RegisterForm3.controls.NextPayDate.dirty || submitAttempt)">ddd </span>
<span style="color:red" *ngIf="!RegisterForm3.controls.NextPayDate.valid && RegisterForm3.controls.NextPayDate.hasError('Paymustgreaterthentodaydate') && (!RegisterForm3.controls.NextPayDate.dirty || submitAttempt)"> * Next Payday Must Be Greater Than Today's Date </span>
<span style="color:red" *ngIf="!RegisterForm3.controls.NextPayDate.valid && RegisterForm3.controls.NextPayDate.hasError('Invalid') && (!RegisterForm3.controls.NextPayDate.dirty || submitAttempt)"> * Invalid Next Payday</span>
<hr/>
and my addlead ts
import { Component} from '#angular/core';
import { NavController,Platform } from 'ionic-angular';
import {ViewChild} from '#angular/core';
import {Content} from 'ionic-angular';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import{Sharedata} from '../../providers/sharedata';
import {NextPayDayValidator1} from '../../validators/NextPayDate'
import 'rxjs/add/operator/map';
/*
Generated class for the AddLeads page.
See http://ionicframework.com/docs/v2/components/#navigation for more info on
Ionic pages and navigation.
*/
#Component({
templateUrl: 'add-leads.html'
})
export class AddLeadsPage {
RegisterForm3: FormGroup;
public NextPayDateErrorMsg:any;
loading: any;
constructor(public navCtrl: NavController,platform: Platform,public np:NextPayDayValidator1 ,private datePipe: DatePipe, public formBuilder: FormBuilder, public toastCtrl: ToastController,public loginservice :Loginservice,public sharedata:Sharedata, public loadingCtrl: LoadingController,
public http: Http, public alertCtrl: AlertController) {
this.NextPayDateErrorMsg=this.np.error;
alert(this.NextPayDateErrorMsg)
this.RegisterForm3 = formBuilder.group({
LastPayDate: ['', Validators.required],
NextPayDate:['',np.isValid]//here i invoke my validator
});
}
}
the above code i have create error global variable.if any error occurs assign errormsg to error global variable;if i trying to execute validation validation will fires error messge shown in alert after closing alert me getting a error like
EXCEPTION: Error in ./AddLeadsPage class AddLeadsPage - inline
template:427:65 caused by: Cannot set property 'error' of undefined
any one help me to fix this bugs
You have set a custom validator NextPayDayValidator1 as a component instead of a directive. A component cannot be injected like a provider.
Check here for custom validators.
#Directive({
selector: '[next-pay-valid]',
providers: [{provide: NG_VALIDATORS, useExisting: NextPayDayValidator1, multi: true}]
})
export class NextPayDayValidator1 implements Validator, OnChanges {..}
Or create a provider for NextPayDayValidator1
#Injectable()
export class NextPayDayValidator1 {
public error: any="Next Pay Date should be greater than todays date";
constructor(public Share:Sharedata){
}
public isValid(control: FormControl): any {
// console.log("false");
let formGroup = control.parent;
var IsValid=true;
//....
}
}
and inject in your component constructor.

Ionic2 and AngularJS2 application using Typescript doesn't find type

I am trying to build an Ionic2 and AngularJS2 application using TypeScript, and I am getting the error: EXCEPTION: No provider for CalEvent!
// File event.ts
export class CalEvent {
name: string;
date: Date;
description: string;
isComplete: boolean;
constructor(n: string, d: Date){
this.name = n;
this.date= d;
}
toString(){
return this.name + ' at ' + this.date;
}
}
/*****************************/
// File event_card_large.ts
import {Component} from 'angular2/core'
import {CalEvent} from '../classes/event.ts';
#Component({
selector: 'event-card-large',
template: '<div style="color: red;">here</div>'
})
export class EventCardLarge{
constructor(public calEvent: CalEvent){}
}
/*****************************/
// File my_page.ts
import {Page} from 'ionic-angular';
import {CalEvent} from '../../classes/event.ts';
import {EventCardLarge} from '../../components/event_card_large.ts';
#Page({
templateUrl: 'build/pages/my_page/my_page.html',
directives:[EventCardLarge]
})
export class MyPage {
public pageName: string;
public testItems: CalEvent[];
selectedItem = 0;
constructor() {
// Test code
this.pageName = 'Test Page 2016-05-17';
this.testItems = [];
let d1 = new Date('2016-05-17');
let ce = new CalEvent('The name', d1);
ce.isComplete = true;
this.testItems.push();
}
}
/*****************************/
// File my_page.html
...
<ion-item *ngFor="#v of testItems; #i = index" >
<event-card-large [calEvent]="v">Loading...</event-card-large>
</ion-item>
...
Thanks for any help.
The problem is you are trying to inject CalEvent in EventCardLarge:
constructor(public calEvent: CalEvent){}
Just declare calEvent as a member of EventCardLarge:
export class EventCardLarge{
calEvent: CalEvent;
constructor(){}
}
It turned out that I needed to make the CalEvent class a "provider", and inject it in the EventCardLarge metadata.
#Component({
selector: 'event-card-large',
template: '<div style="color: red;">here</div>',
providers: [CalEvent]
})

Push object into new array with interfaces in Angular2

Im retrieving a list of objects from a service in Angular2.
On click I want to put the object from my ngFor onto a new array (with the same interface).
I'm able to display the list with items just fine. I'm also able to display the item I selected in the console, but I'm not able to push this object into a new array with the same interface.
My interface:
export interface Item {
id: string,
name: string,
icon_url: string,
exterior: string,
type: string,
tag: string,
addedClass: string,
selected: boolean
}
My component:
import {Component, OnInit, DoCheck} from 'angular2/core';
import {Item} from './../interfaces/interfaces.ts';
import {ItemService} from './../services/item.service.ts';
#Component({
template: `
<h2>Add item</h2>
<div class="itemlist">
<ul class="items">
<li
*ngFor="#item of items"
[class.selected]="item === selectedItem"
(click)="onSelect(item)"
>
<span class="item">
{{item.name}}
</span>
</li>
</ul>
</div>
`
})
export class AddTradeComponent implements OnInit {
public items: Item[];
public selectedItems: Item[];
constructor(private _itemService: ItemService) { }
getUserItems() {
this._itemService.getUserItems().then(userItems => this.items = userItems);
}
ngOnInit() {
this.getUserItems();
}
onSelect(item) {
console.log('items ', item);
this.selectedItems.push(item);
}
}
Errors that I'm getting:
Error during evaluation of "click"
TypeError: Cannot read property 'push' of undefined
Logging the object works fine, I just cannot push it onto this.selectedItems.
You didn't initialize your arrays. Either initialize the arrays when you declare your member variables:
public items: Item[] = [];
public selectedItems: Item[] = [];
Or initialize them in your constructor:
constructor() {
this.items = [];
this.selectedItems = [];
}

Resources