i have bounded a api call response to kendo grid and am able to bind the data in the columns and to do edit and save for a inline edit which i have bound in my html , I see lots of samples binding through component template am not sure if that works in my approach can someone give me some links as am new to kendo. I need to get all the editing row data and then wen i save button it goes to my put api call.TIA
my application
HTML
<kendo-grid [data]="ForecastProductionQuantity"
[pageSize]="10" [pageable]="true" [resizable]="true"
[scrollable]="scrollable" [filterable]="true"
[height]="650">
<kendo-grid-column field="AmgName">
<ng-template kendoGridHeaderTemplate let-column let-columnIndex="columnIndex">
{{gridHeaders["AmgName"]}}
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="ProdQty.ProdQty0">
<ng-template kendoGridHeaderTemplate let-column let-columnIndex="columnIndex">
{{gridHeaders["ForecastType0"]}}
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="ProdQty.ProdQty1">
<ng-template kendoGridHeaderTemplate let-column let-columnIndex="columnIndex">
{{gridHeaders["ForecastType1"]}}
</ng-template>
</kendo-grid-column>
</kendo-grid>
Component.ts
loadForecastQuantityData() {
//keeping it 0 will be incorporated after Go click is integrated will be changed as per save input
this.allocationSetupID = 0;
return this.restapi.getForecastProductionQuantityData(this.allocationSetupID, this.ForecastID).subscribe((data: {}) => {
this.ForecastProductionQuantity = data;
for (var prod in this.ForecastProductionQuantity) {
this.gridHeaders = {
AmgName: "Amg Name",
ForecastType0: this.ForecastProductionQuantity[prod].Forecast_type.ForecastType0 + " " + this.ForecastProductionQuantity[prod].ProdMonth.ProdMonth0,
ForecastType1: this.ForecastProductionQuantity[prod].Forecast_type.ForecastType1 + " " + this.ForecastProductionQuantity[prod].ProdMonth.ProdMonth1,
}
}
}, (err) => {
console.log(err);
})
}
service.ts
getForecastProductionQuantityData(allocationSetupID: any, ForecastID: any): Observable<any> {
return this.http.get<any>(this.apiURL + '/GetProductionVolume/' + allocationSetupID + '/' +
ForecastID )
.pipe(
retry(1),
catchError(this.handleError)
)
}
Have you followed Kendo's example here? It is what I use in my application. In the (save)="saveHandler($event)" function in TypeScript, send the data to your API service to save. The asynchronous view will automatically update after the data posts.
Related
The data from workData fills <Card></Card> correctly.
The <Modal></Modal> only fills with the last entry of workData (e.g. Test4, Modal4, test text 4...)
my goal is to generate cards and respective modals (for each card) using the data from the json, in the same file.
why is the modal only being filled by the last properties in the json? how do i get it to populate with the entire array? if possible please explain why this does not work the way it is.
if it's not obvious im super new, i am, any responses would be super appreciated. ty
cards good
after clicking "Read1" bad, should say Test1, test text 1
in App.js: import { Works } from "./Works";
in Works.js: import { workData } from "./data";
also in Work.js:
export const Works = () => {
const [show, setShow] = React.useState(false);
const onClick = () => setShow(true);
return (
<>
<div className="work-container">
<Row xs={1} md={2} lg={4} className="g-4">
{workData.map((data, key) => {
return (
<div key={key}>
<Col>
<Card>
<Card.Img variant="top" src={data.projectImage} />
<Card.Body>
<Card.Title>{data.projectTitle}</Card.Title>
<Card.Text>with {data.projectTeam}</Card.Text>
<Button variant="link" onClick={onClick}>
{data.readMore}
</Button>
</Card.Body>
<Card.Footer>{data.tags}</Card.Footer>
</Card>
</Col>
<Modal
show={show}
onHide={() => setShow(false)}
dialogClassName="modal-95w"
>
<Modal.Header closeButton>
<Modal.Title>{data.projectTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Image src={data.projectImage}></Image>
<p>
{data.modalText}
</p>
<Image src={data.modalImage}></Image>
</Modal.Body>
</Modal>
</div>
);
})}
</Row>
</div>
</>
);
}
in data.js:
export const workData = [
{
projectTitle: "Test1",
modalTitle: "Modal1",
modalText: "test text 1",
modalImage: "image",
readMore: "Read1",
projectImage: "image",
projectTeam: "Test1",
year: "2022",
link1: "link",
link2: "link2",
tags: [
"#tag1 ",
"#tag2 "
]
},
...
The data from workData fills <Card></Card> correctly.
The <Modal></Modal> only fills with the last entry of workData (e.g. Test4, Modal4, test text 4...)
my goal is to generate cards and respective modals (for each card) using the data from the json, in the same file.
why is the modal only being filled by the last properties in the json? how do i get it to populate with the entire array? if possible please explain why this does not work the way it is.
cards good
after clicking "Read1" bad, should say Test1, test text 1
You iterate over workData for Cards and Modals, but you use only one single state for everything. What you need to do, is to also create a state for every Modal. Usually you create an array with unique id as key and boolean value. I assumed projectTitle is unique:
{
Test1: false,
Test2: false,
Test3: false
}
Because you don't know the length of your data, you just iterate over the array, as you have done for Cards und Modals:
const initialShowState = Object.fromEntries(
workData.map((data) => [data.projectTitle, false])
);
const [show, setShow] = React.useState(initialShowState);
Then you need to create a generic callback function, which takes the id of the Card and shows the appropriate Modal. I simplified the logic and created a toggle function:
const toggleShow = (id) =>
setShow((prev) => {
return { ...prev, [id]: !prev[id] };
});
Finally, when you render UI and iterate over workData, you need to apply the callback function to Button onClick and Modal onHide event handlers and set the show property of Modal:
<Button variant="link" onClick={() => toggleShow(data.projectTitle)}>
...
<Modal
show={show[data.projectTitle]}
onHide={() => toggleShow(data.projectTitle)}
dialogClassName="modal-95w"
>
That's it. Here is the working sandbox: https://codesandbox.io/s/hungry-sunset-t865t3
Some general tips:
You don't need the outer Fragment in Works as you only have one upper most element
If you use JSX syntax in your file, your extension should be .jsx and not.js (Works.jsx)
Using index as key in the list is bad practice. Find some unique id in your data
Select Box 1 is "Path".
Select Box 2 is "SkillSet".
I'm trying to duplicate group of select boxes(Path and SkillSet) based on a button click. So when I click Add button the select box(Path and SkillSet) form element will be duplicated. The catch here is the select box "Skillset" element's options are dynamic because its depends on the select box "Path".
Issue Below:
Step1: Choosing Path as BackEnd and Skill will be populated based on the Path. In the second select box selected as Java8.
Step2: Clicking Add button, so the select box Path and Skill is duplicated. Now choosing select box Path as FrontEnd.
Step3: After choosing Path as FrontEnd in the second row, the selected Skill in first row's are reseted to empty. (In the image I have added two Path's)
StackBlitz Demo for the Issue:
https://stackblitz.com/edit/angular-duplicate-dynamic-select-box?file=null
Expectation is : I have to select each Path and respective Skill. Like If I choose 3 different path, then I have to choose 3 different skills in the 3 different row of select boxes.
I have tried many solutions. Nothing is working out. Can someone help in this case.?
Sorry for my English and bad formatting. Appreciate your help !!!
You can Push the skillsets for the selected path into an array and access them in the HTML file using the index.
In .ts File
import { Component } from '#angular/core';
import { FormGroup, FormArray, FormBuilder, Validators } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
skillSetObj;
majorPathObj;
skillForm: FormGroup;
skillList: FormArray;
choosenPath;
skillsForSelectedPath:any = []; // <--- Declare a new array for the skillsets to be pushed
constructor(private fb:FormBuilder) {
}
ngOnInit() {
this.skillSetObj = {
"BackEnd": ["Java8", "Node JS", "Python", "Dotnet"],
"FrontEnd": ["Javascript ", "Angular ", "React", "Vue"],
"Database": ["Oracle", "Postgres", "Mysql"]
};
this.majorPathObj = ["BackEnd", "FrontEnd", "Database"];
this.skillForm = this.fb.group({
skillFormArray: this.fb.array([this.createSkills()])
});
this.skillList = this.skillForm.get('skillFormArray') as FormArray;
}
createSkills(): FormGroup {
return this.fb.group({
majorPath: ['', Validators.compose([Validators.required])],
skillSet: ['', Validators.compose([Validators.required])]
});
}
getSkillFormGroup(index): FormGroup {
const formGroup = this.skillList.controls[index] as FormGroup;
return formGroup;
}
get skillFormGroup() {
return this.skillForm.get('skillFormArray') as FormArray;
}
addNewSkill() {
this.skillList.push(this.createSkills());
}
removeSkill(skillRowIndex) {
this.skillList.removeAt(skillRowIndex);
}
prepareSkillSet(event, i) {
this.skillsForSelectedPath[i]=this.skillSetObj[event.value]; // <--- Push the skills for the selected majorPath into the new array
const formGroup = this.getSkillFormGroup(i);
const choosenPath = formGroup.controls['majorPath'].value;
this.choosenPath = choosenPath;
}
}
** In HTML file **
<form [formGroup]="skillForm">
<div formArrayName="skillFormArray">
<div *ngFor="let skillArray of skillFormGroup.controls; let i=index">
<div [formGroupName]="i">
<div >
<mat-form-field appearance="outline">
<mat-select formControlName="majorPath"
(selectionChange)="prepareSkillSet($event, i)">
<mat-option *ngFor="let major of majorPathObj" value={{major}}>
{{major}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-select formControlName="skillSet">
<mat-option *ngFor="let skill of skillsForSelectedPath[i]" [value]="skill"> <!-- display the skills for the selected majorPath using the index of the newly created variable -->
{{skill}}
</mat-option>
</mat-select>
</mat-form-field>
<button *ngIf="i===0" mat-fab color="accent" class="add-file-button mt-5"
(click)="addNewSkill()" aria-label="Add Skill">
<mat-icon>add</mat-icon>
</button>
<button *ngIf="i!==0" mat-fab color="warn" class="add-file-button"
(click)="removeSkill(i)" aria-label="Remove Skill">
<mat-icon>remove</mat-icon>
</button>
</div>
</div>
</div>
</div>
</form>
So everytime the majorPath is the skills object is also updated and you can select the corresponding skills for the newly selected majorPath.
The output looks like below
I am trying to get and use index of the list in NativeScript, in this case using Repeater.
Here is my code:
<ScrollView>
<Repeater items="{{ sviArtikli }}" itemTemplateSelector="$index">
<Repeater.itemsLayout>
<WrapLayout orientation="horizontal"/>
</Repeater.itemsLayout>
<Repeater.itemTemplate>
<Card:CardView margin="10" width="45%" height="250" item="{{ id }}" myIndex="{{ $index }}" tap="detaljiArtikla">
<grid-layout rows="250, *" columns="*, 50">
<Image loaded="slikaUcitana" class="lista-katalozi-slika" id="slicice" opacity="1" stretch="aspectFill" colSpan="3" row="0" src="{{ 'https://url_location/' + slika_image }}" />
</grid-layout>
</Card:CardView>
</Repeater.itemTemplate>
</Repeater>
</ScrollView>
The part with myIndex="{{ $index }}" does not work as index can not be calculated in Repeater.
Is there a way I can get index for every item in repeater? - ListView works, but I can not use listview in this case.
I have created a plyground for you here. You can pass the unique ID in your dataprovider and can access in item.
P.S. There is an open Feature-request to support index in repeater, you can also cast your vote there.
Accessing item index is not yet supported in Repeater, but if it's crucial for you, you could extend the Repeater class to support index.
indexed-repeater.ts (tested against tns-core-modules v6.0.1)
import { Repeater } from "tns-core-modules/ui/repeater";
import { CSSType } from "tns-core-modules/ui/layouts/layout-base";
import { parse } from "tns-core-modules/ui/builder";
export module knownTemplates {
export const itemTemplate = "itemTemplate";
}
#CSSType("Repeater")
export class IndexedRepeater extends Repeater {
public refresh() {
if (this.itemsLayout) {
this.itemsLayout.removeChildren();
}
if (!this.items) {
return;
}
const length = this.items.length;
for (let i = 0; i < length; i++) {
const viewToAdd = this.itemTemplate ? parse(this.itemTemplate, this) : (<any>this)._getDefaultItemContent(i);
const dataItem = (<any>this)._getDataItem(i);
viewToAdd.bindingContext = {
item: dataItem,
index: i
};
this.itemsLayout.addChild(viewToAdd);
}
(<any>this)._isDirty = false;
}
}
Sample Usage
<ScrollView xmlns:comps="components/indexed-repeater">
<comps:IndexedRepeater items="{{ items }}">
<comps:IndexedRepeater.itemTemplate>
<Label text="{{ index + ' ' + item.name }}" class="m-10 h3" />
</comps:IndexedRepeater.itemTemplate>
</comps:IndexedRepeater>
</ScrollView>
Playground Sample
Hi guys thanks and it helped. What I did, without changing any code base is pushing locally generated ID's into JSON element and used them for indexes.
Now json has { "my_index": "0" ..... } which I change in for loop when fetch is over. I am using those indexes just to scroll the view on certain element in the list.
http.getJSON("https://....")
.then(function (response){
for(var i = 0; i < response.length; i++) {
response[i].my_index = i; // change response on the fly.
// do something with response....
}
}, function(error) {
console.error(JSON.stringify(error));
})
I guess this could help to someone as well.
I have a simple form as below:
some.component.html
<form class="example-form" novalidate (ngSubmit)="onSubmit()" autocomplete="off" [formGroup]="testform">
<input type="text" formControlName="name" class="form-control" placeholder="Enter name" required/>
<app-show-errors [control]="claimform.controls.name"></app-show-errors>
<button type="submit" (click)="onSubmit()">Next</button>
</form>
some.component.ts
ngOnInit() {
this.testform= new FormGroup({
name: new FormControl('', { validators: Validators.required})
}, {updateOn: 'submit'});
}
onSubmit() {
if (this.testform.valid) {
alert('saving data');
} else {
this._validationService.validateAllFormFields(this.testform);
}
}
validationService.ts
validateAllFormFields(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
} else if (control instanceof FormGroup) {
this.validateAllFormFields(control);
}
});
}
Reference
Problem
The form will validate on submit if left blank, but even after filling the value when I check this.testform.valid it returns false. But if I remove updateOn:'submit' on form then it validates on blur of input control and when value is entered it validates form return true. Not sure if updateOn is working fine or not or whether I've implemented this in a proper way. Could someone point me in the right direction.
in your HTML you have two calls to onSubmit() function, from submit button:
<button type="submit" (click)="onSubmit()">Next</button>
and from the form:
<form class="example-form"
ovalidate
(ngSubmit)="onSubmit()"
autocomplete="off"
[formGroup]="testform">
The first call to be triggered is the button's trigger, which actually does nothing in terms of updating your reactive form, since you set FormGroup's option to {updateOn: 'submit'}. The second call to be triggered is the form's trigger, which does actual form update.
Here is FormGroup directive config:
#Directive({
selector: '[formGroup]',
providers: [formDirectiveProvider],
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
exportAs: 'ngForm'
})
as we can see in host property DOM form's submit (triggered by hitting ENTER while focused within form or clicking form's submit button) will call onSubmit() function:
onSubmit($event: Event): boolean {
(this as{submitted: boolean}).submitted = true;
syncPendingControls(this.form, this.directives);
this.ngSubmit.emit($event);
return false;
}
which then will call syncPendingControls() function:
export function syncPendingControls(form: FormGroup, directives: NgControl[]): void {
form._syncPendingControls();
directives.forEach(dir => {
const control = dir.control as FormControl;
if (control.updateOn === 'submit' && control._pendingChange) {
dir.viewToModelUpdate(control._pendingValue);
control._pendingChange = false;
}
});
}
which updates a model at last.
So, in your case, just remove (click)="onSubmit()" from the submit button:
<button type="submit">Next</button>
also you do not need required DOM element property on your input, since you set it using Reactive Forms API validators: Validators.required and since you set your form to novalidate which cancels HTML5 form validation.
Is there anyway to do a if condition in the XML file of nativescript? (without angular)
if
<Card:CardView class="cardStyle" margin="10" elevation="40" radius="5">
<Slider value="18" minValue="5" maxValue="30" />
</Card:CardView>
else
<Card:CardView class="cardStyle" margin="10" elevation="40" radius="5">
<Label text="Example" class="h3" margin="10" />
</Card:CardView>
What you could do is use a boolean property (which in its get function has the condition you want) and bind it to the visibility of the CardView.
Looks like you can show/hide from the template file using visibility:
In XML file: ie. 'sample-page.xml'
<Button text="{{ isShowing ? 'Hide Me' : 'Show Me' }}" tap="toggleShowing"/> <!-- Notice missing interpolation in Button tag -->
<Label text="Showing Hidden Element" visibility="{{ isShowing ? 'visible' : 'collapsed' }}"/>
In Page file: ie. 'sample-page.ts'
let model = new ViewModel();
// Event handler for Page 'loaded' event attached in main-page.xml
export function pageLoaded(args: observable.EventData) {
const page = <Page>args.object;
page.bindingContext = model;
}
export function toggleShowing() {
model.set('isShowing', !model.get('isShowing'));
}
In View Model: ie. 'sample-view-model.ts'
isShowing:boolean = false;
The only way I found to do to do this was to use the navigatingTo event of the page:
export function navigatingTo(args: EventData) {
let page = <Page>args.object;
var myLayout = <StackLayout>page.getViewById("myLayout");
if (condition) {
let segmentedBar = new SegmentedBar;
...
myLayout.addChild(segmentedBar);
}
else {
let button: Button = new Button;
...
myLayout.addChild(button);
}
No possibility in the template file :-/
It can be done using ng-container.For example
<ng-container *ngIf="true">Your logic here....</ng-container>