<!DOCTYPE html>
<html>
<head>
<base href="." />
<title>devextreme-angular playground</title>
<link rel="stylesheet" href="https://unpkg.com/devextreme@17.1/dist/css/dx.common.css" />
<link rel="stylesheet" href="https://unpkg.com/devextreme@17.1/dist/css/dx.light.css" />
<link rel="stylesheet" href="style.css" />
<script src="https://unpkg.com/zone.js@0.6.25/dist/zone.js"></script>
<script src="https://unpkg.com/zone.js@0.6.25/dist/long-stack-trace-zone.js"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
<script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
<script src="https://unpkg.com/typescript@2.0.10/lib/typescript.js"></script>
<script src="config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
</head>
<body>
<my-app>
loading...
</my-app>
</body>
</html>
/* Styles go here */
### DevExtreme Angular simple plunker
https://github.com/DevExpress/devextreme-angular
System.config({
//use typescript for compilation
transpiler: 'typescript',
//typescript compiler options
typescriptOptions: {
emitDecoratorMetadata: true
},
paths: {
'npm:': 'https://unpkg.com/'
},
map: {
app: "./src",
'@angular/core': 'npm:@angular/core@2.4.3/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common@2.4.3/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler@2.4.3/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser@2.4.3/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@2.4.3/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http@2.4.3/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router@3.4.3/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms@2.4.3/bundles/forms.umd.js',
'rxjs': 'npm:rxjs@5.0.3',
'devextreme': 'npm:devextreme@17.1',
'jquery': 'npm:jquery@3.1.1/dist/jquery.min.js',
'jszip': 'npm:jszip@3.1.3/dist/jszip.min.js',
'devextreme-angular': 'npm:devextreme-angular@17.1'
},
//packages defines our app package
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
rxjs: {
defaultExtension: 'js'
},
'devextreme': {
defaultExtension: 'js'
},
'devextreme-angular': {
main: 'index.js',
defaultExtension: 'js'
}
}
});
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {App} from './app';
import {DevExtremeModule} from 'devextreme-angular';
import {MultilangComponent} from './multilang.component';
@NgModule({
imports: [
BrowserModule,
DevExtremeModule
],
declarations: [
App,
MultilangComponent
],
bootstrap: [ App ]
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule)
import {Component} from '@angular/core';
import {QUESTIONS} from './question.data';
@Component({
selector: 'my-app',
templateUrl: './src/app.html'
})
export class App {
questions: Question[] = [];
nextQuestions: Question[] = [];
choices: Choice[] = [];
selectedIndex: number = -1;
columns: object[] = [];
editing: object = {};
switchQuestion = false;
timer = null;
messages: object = {};
constructor() { }
ngOnInit() {
this.loadQuestion();
this.messages = {
removeQuestion: 'removeQuestion',
question: 'question',
newTitle: 'newTitle',
newQuestion: 'newQuestion'
};
}
public onRowSelected(e) {
this.switchQuestion = true;
this.selectedIndex = e.itemIndex;
this.setNextQuestion();
this.loadChoice();
}
public onFormUpdated(item, e) {
if (!this.switchQuestion) {
if (this.timer) {
clearTimeout(this.timer);
}
if (item !== 'form') {
this.questions[this.selectedIndex][item] = e.value;
}
this.timer = setTimeout(() => {
console.log('update by service');
}, 1000);
}
}
public onRemoveQuestion() {
if (this.selectedIndex > -1) {
confirm(this.messages['removeQuestion'], this.messages['question']).done((result) => {
if (result) {
console.log('remove by service');
}
});
}
}
public onInitNewChoice(e) {
e.data.isActive = true;
}
public onChoiceInserted(e) {
this.choiceService.create(e.data).subscribe(info => {
if (info.success) {
this.loadChoice();
}
});
}
public onChoiceUpdated(e) {
e.data.idSurveyChoice = e.key.idSurveyChoice;
this.choiceService.update(e.data).subscribe();
}
public onChoiceRemoved(e) {
this.choiceService.remove(e.key.idSurveyChoice).subscribe();
}
private setNextQuestion() {
this.nextQuestions = [];
this.questions.forEach((question) => {
if (question.idSurveyQuestion !== this.questions[this.selectedIndex].idSurveyQuestion) {
this.nextQuestions.push(question);
}
});
}
private loadChoice() {
this.choices = [];
this.switchQuestion = false;
}
private loadQuestion() {
this.questions = QUESTIONS;
}
}
import {Injectable} from '@angular/core';
export const QUESTIONS = [
{"description": {"idLanguageContent": "8c8517bd-8bcd-4ea7-859d-efe25efa371f", "en": "Question 1", "fr": "Question 1"}, "id_survey_question_next": null, "sequence": 0, "createdOn": "2017/04/06 16:18:14", "idLanguageContentDescription": "8c8517bd-8bcd-4ea7-859d-efe25efa371f", "title": {"idLanguageContent": "333dcbf5-2c77-4b65-aff6-6b1990a67f6c", "en": "Section 1", "fr": "Section 1"}, "idSurveyQuestion": "64d876ae-1435-4b4c-9676-1bbefd422619", "questionType": "text", "idLanguageContentTitle": "333dcbf5-2c77-4b65-aff6-6b1990a67f6c", "idSurvey": "dc2aebde-82f9-40ac-b389-6c6116acc7c5", "isActive": true},
{"description": {"idLanguageContent": "dc4ab409-ed9f-416c-bf29-19590cfaf215", "en": "Question 2", "fr": "Question 2"}, "id_survey_question_next": null, "sequence": 1, "createdOn": "2017/04/06 16:18:53", "idLanguageContentDescription": "dc4ab409-ed9f-416c-bf29-19590cfaf215", "title": {"idLanguageContent": "9a2a8426-fd10-47dd-9635-d28cd6c851f9", "en": "Section 1", "fr": "Section 1"}, "idSurveyQuestion": "903b1b44-eb03-4335-901d-04b7c3c576df", "questionType": "date", "idLanguageContentTitle": "9a2a8426-fd10-47dd-9635-d28cd6c851f9", "idSurvey": "dc2aebde-82f9-40ac-b389-6c6116acc7c5", "isActive": true},
{"description": {"idLanguageContent": "4131ed7b-f756-4f52-8980-c50a5d1b2c9c", "en": "Question 3", "fr": "Question 3"}, "id_survey_question_next": null, "sequence": 2, "createdOn": "2017/04/06 16:19:10", "idLanguageContentDescription": "4131ed7b-f756-4f52-8980-c50a5d1b2c9c", "title": {"idLanguageContent": "5eb513bb-4ee4-4472-825b-dab7c2dc9f14", "en": "Section 2", "fr": "Section 2"}, "idSurveyQuestion": "f0ab84f0-e216-49c4-a714-c31420c68abf", "questionType": "choice", "idLanguageContentTitle": "5eb513bb-4ee4-4472-825b-dab7c2dc9f14", "idSurvey": "dc2aebde-82f9-40ac-b389-6c6116acc7c5", "isActive": true},
{"description": {"idLanguageContent": "36a27daa-8b9e-485e-bd05-fe853ca67dc9", "en": "Question 4", "fr": "Question 4"}, "id_survey_question_next": null, "sequence": 3, "createdOn": "2017/06/13 10:17:07", "idLanguageContentDescription": "36a27daa-8b9e-485e-bd05-fe853ca67dc9", "title": {"idLanguageContent": "f5d9bea3-8ac8-47bf-b269-2e01cffb567e", "en": "Section 2", "fr": "Section 2"}, "idSurveyQuestion": "0526d79d-fcc7-4d1e-98e3-041eb00adf6d", "questionType": "text", "idLanguageContentTitle": "f5d9bea3-8ac8-47bf-b269-2e01cffb567e", "idSurvey": "dc2aebde-82f9-40ac-b389-6c6116acc7c5", "isActive": true}]}
];
<div fxLayout="row">
<div fxFlex="35%">
<div class="content">
<dx-tree-view
[dataSource]="questions"
displayExpr="description.fr"
keyExpr="idSurveyQuestion"
selectionMode="single"
[selectByClick]="true"
(onItemSelectionChanged)="onRowSelected($event)">
</dx-tree-view>
</div>
</div>
<div fxFlex="65%">
<div *ngIf="selectedIndex > -1">
<dx-form [formData]="questions[selectedIndex]" (onFieldDataChanged)="onFormUpdated('form',$event)">
<dxi-item [label]="{text: 'section'}" dataField="title">
<app-multilang [value]="questions[selectedIndex].title" (onValueChanged)="onFormUpdated('title',$event);"></app-multilang>
</dxi-item>
<dxi-item [label]="{text: 'description'}" dataField="description">
<app-multilang [value]="questions[selectedIndex].description" (onValueChanged)="onFormUpdated('description',$event);"></app-multilang></dxi-item>
<dxi-item
[label]="{text: 'questionType'}"
dataField="questionType"
editorType="dxSelectBox"
[editorOptions]="{dataSource: [
{value: 'choice', text: 'choiceAnswer'},
{value: 'text', text: 'textAnswer'},
{value: 'date', text: 'dateAnswer'}
], displayExpr: 'text', valueExpr: 'value'}">
</dxi-item>
<dxi-item
[label]="{text: 'nextQuestion'}"
dataField="idSurveyQuestionNext"
editorType="dxSelectBox"
[editorOptions]="{dataSource: nextQuestions, displayExpr: 'description.fr', valueExpr: 'idSurveyQuestion'}">
</dxi-item>
</dx-form>
<label>{{'options'}}</label>
<dx-data-grid
[dataSource]="choices"
[hoverStateEnabled]="true"
(onInitNewRow)="onInitNewChoice($event)"
(onRowInserted)="onChoiceInserted($event)"
(onRowUpdated)="onChoiceUpdated($event)"
(onRowRemoved)="onChoiceRemoved($event)">
<dxi-column
dataField="sequence"
caption="{{'sequence'}}"
width="15%"
sortOrder="asc"
sortIndex="0">
</dxi-column>
<dxi-column
dataField="name"
caption="{{'name'}}"
[calculateCellValue]="onCalculateCellValue"
editCellTemplate="editname">
</dxi-column>
<dxi-column
dataField="idSurveyQuestionNext"
caption="{{'surveyQuestionNext'}}">
<dxo-lookup [dataSource]="nextQuestions" displayExpr="description.fr" valueExpr="idSurveyQuestion">
</dxo-lookup>
</dxi-column>
<dxi-column dataField="isActive" caption="{{'isActive'}}" width="10%"></dxi-column>
<dxo-editing mode="row" [allowUpdating]="true" [allowAdding]="true" [allowDeleting]="true">
</dxo-editing>
<div *dxTemplate="let rowname of 'editname'">
<app-multilang [value]="rowname.row.data.name" (onValueChanged)="rowname.setValue($event.value);"></app-multilang>
</div>
</dx-data-grid>
</div>
</div>
</div>
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
selector: 'app-multilang',
templateUrl: './src/multilang.component.html'
})
export class MultilangComponent implements OnInit {
@Input()
get value(): object { return this._value; }
set value(val: object) {
['fr', 'en'].forEach((lang) => {
this._value[lang] = (val && val[lang] ? val[lang] : '');
});
this._value['idLanguageContent'] = (val && val['idLanguageContent'] ? val['idLanguageContent'] : '');
this.selectedTabValue = this._value[this.selectedTab];
}
private _value: object = {
'idLanguageContent': ''
};
public languages: string[] = [];
public selectedTab: string;
public selectedTabValue: string;
@Output() onValueChanged = new EventEmitter();
constructor() {
const labels = {'fr': 'Francais', 'en': 'Anglais'};
['fr', 'en'].forEach((lang) => {
this._value[lang] = '';
});
['fr', 'en'].forEach((lang) => {
this.languages.push(labels[lang]);
});
}
ngOnInit() {
this.selectedTab = ['fr', 'en'][0];
this.selectedTabValue = this._value[this.selectedTab];
}
public onTabValueChanged(e) {
const oldValue = Object.assign({}, this.value);
this._value[this.selectedTab] = e.element.find('input').val();
this.onValueChanged.emit({
value: this.value,
oldValue: oldValue
});
}
public onTabChanged(e) {
this.selectedTab = environment.languages[e.component.option('selectedIndex')];
this.selectedTabValue = this._value[this.selectedTab];
}
}
<dx-tab-panel
[items]="tabs"
[selectedIndex]="0"
(onSelectionChanged)="onTabChanged($event)">
<ng-container *ngFor="let lang of languages; let i = index">
<dxi-item [title]="lang">
<dx-text-box (onKeyUp)="onTabValueChanged($event)" [value]="selectedTabValue" [placeholder]="lang"></dx-text-box>
</dxi-item>
</ng-container>
</dx-tab-panel>