[ADF-4338] Add locale to CardViewDateItemModel and improve logic of LocalizedDat… (#4845)

* Add locale to CardViewDateItemModel and improve logic of LocalizedDatePipe

* Fix e2e tests

* Fix e2e tests

* Fix C305010 test
This commit is contained in:
davidcanonieto
2019-06-14 18:26:08 +02:00
committed by Eugenio Romano
parent e03290d26c
commit 334ebd1256
25 changed files with 110 additions and 58 deletions

View File

@@ -439,8 +439,8 @@
]
},
"dateValues":{
"defaultDateFormat": "medium",
"defaultDateTimeFormat": "DD/MM/YYYY HH:mm",
"defaultDateFormat": "mediumDate",
"defaultDateTimeFormat": "MMM d, y, h:mm",
"defaultLocale": "en-US"
},
"files": {

View File

@@ -72,7 +72,7 @@ export class CardViewComponent implements OnInit {
value: new Date(1983, 11, 24, 10, 0, 30),
key: 'date',
default: new Date(1983, 11, 24, 10, 0, 30),
format: 'DD.MM.YYYY',
format: 'shortDate',
editable: this.isEditable
}),
new CardViewDatetimeItemModel({
@@ -80,7 +80,7 @@ export class CardViewComponent implements OnInit {
value: new Date(1983, 11, 24, 10, 0, 0),
key: 'datetime',
default: new Date(1983, 11, 24, 10, 0, 0),
format: 'DD.MM.YYYY',
format: 'short',
editable: this.isEditable
}),
new CardViewBoolItemModel({

View File

@@ -34,7 +34,7 @@ import { NavigationBarPage } from '../../pages/adf/navigationBarPage';
describe('Metadata component', () => {
const METADATA = {
DATA_FORMAT: 'mmm dd yyyy',
DATA_FORMAT: 'mmm dd, yyyy',
TITLE: 'Details',
COMMENTS_TAB: 'COMMENTS',
PROPERTY_TAB: 'PROPERTIES',

View File

@@ -267,8 +267,8 @@ describe('CardView Component', () => {
});
it('[C279962] Should be present a default value', () => {
expect(metadataViewPage.getPropertyText('date', 'date')).toEqual('24.12.1983');
expect(metadataViewPage.getPropertyText('datetime', 'datetime')).toEqual('Dec 24 1983 10:00');
expect(metadataViewPage.getPropertyText('date', 'date')).toEqual('12/24/83');
expect(metadataViewPage.getPropertyText('datetime', 'datetime')).toEqual('Dec 24, 1983, 10:00');
});
});

View File

@@ -44,7 +44,7 @@ describe('Process Header cloud component', () => {
const simpleApp = resources.ACTIVITI7_APPS.SIMPLE_APP.name;
const subProcessApp = resources.ACTIVITI7_APPS.SUB_PROCESS_APP.name;
const formatDate = 'MMM D YYYY';
const formatDate = 'MMM D, YYYY';
const processHeaderCloudPage = new ProcessHeaderCloudPage();

View File

@@ -39,7 +39,7 @@ describe('Task Header cloud component', () => {
const simpleApp = resources.ACTIVITI7_APPS.SIMPLE_APP.name;
const priority = 30;
const description = 'descriptionTask';
const formatDate = 'MMM D YYYY';
const formatDate = 'MMM D, YYYY';
const taskHeaderCloudPage = new TaskHeaderCloudPage();

View File

@@ -40,7 +40,7 @@ describe('Process Instance Details', () => {
let appModel, process, user;
const app = resources.Files.SIMPLE_APP_WITH_USER_FORM;
const PROCESS_DATE_FORMAT = 'mmm dd yyyy';
const PROCESS_DATE_FORMAT = 'mmm dd, yyyy';
beforeAll(async (done) => {
const apps = new AppsActions();

View File

@@ -38,7 +38,7 @@ describe('Task Details component', () => {
let processUserModel, appModel;
const app = resources.Files.SIMPLE_APP_WITH_USER_FORM;
const tasks = ['Modifying task', 'Information box', 'No form', 'Not Created', 'Refreshing form', 'Assignee task', 'Attach File'];
const TASK_DATA_FORMAT = 'mmm dd yyyy';
const TASK_DATE_FORMAT = 'mmm dd, yyyy';
let formModel;
let apps;
@@ -90,7 +90,7 @@ describe('Task Details component', () => {
const taskModel = new TaskModel(allTasks.data[0]);
taskPage.tasksListPage().checkContentIsDisplayed(taskModel.getName());
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATA_FORMAT));
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATE_FORMAT));
expect(taskPage.taskDetails().getId()).toEqual(taskModel.getId());
expect(taskPage.taskDetails().getDescription()).toEqual(taskModel.getDescription());
expect(taskPage.taskDetails().getAssignee()).toEqual(taskModel.getAssignee().getEntireName());
@@ -128,7 +128,7 @@ describe('Task Details component', () => {
const taskModel = new TaskModel(allTasks.data[0]);
taskPage.tasksListPage().checkContentIsDisplayed(taskModel.getName());
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATA_FORMAT));
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATE_FORMAT));
expect(taskPage.taskDetails().getId()).toEqual(taskModel.getId());
expect(taskPage.taskDetails().getDescription()).toEqual(taskModel.getDescription());
expect(taskPage.taskDetails().getAssignee()).toEqual(taskModel.getAssignee().getEntireName());
@@ -166,7 +166,7 @@ describe('Task Details component', () => {
const taskModel = new TaskModel(allTasks.data[0]);
taskPage.tasksListPage().checkContentIsDisplayed(taskModel.getName());
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATA_FORMAT));
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATE_FORMAT));
expect(taskPage.taskDetails().getId()).toEqual(taskModel.getId());
expect(taskPage.taskDetails().getDescription()).toEqual(CONSTANTS.TASK_DETAILS.NO_DESCRIPTION);
expect(taskPage.taskDetails().getAssignee()).toEqual(taskModel.getAssignee().getEntireName());
@@ -205,7 +205,7 @@ describe('Task Details component', () => {
const taskModel = new TaskModel(allTasks.data[0]);
taskPage.tasksListPage().checkContentIsDisplayed(taskModel.getName());
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATA_FORMAT));
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATE_FORMAT));
expect(taskPage.taskDetails().getId()).toEqual(taskModel.getId());
expect(taskPage.taskDetails().getDescription()).toEqual(CONSTANTS.TASK_DETAILS.NO_DESCRIPTION);
expect(taskPage.taskDetails().getAssignee()).toEqual(taskModel.getAssignee().getEntireName());
@@ -251,7 +251,7 @@ describe('Task Details component', () => {
const taskModel = new TaskModel(allTasks.data[0]);
taskPage.tasksListPage().checkContentIsDisplayed(taskModel.getName());
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATA_FORMAT));
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATE_FORMAT));
expect(taskPage.taskDetails().getId()).toEqual(taskModel.getId());
expect(taskPage.taskDetails().getDescription()).toEqual(CONSTANTS.TASK_DETAILS.NO_DESCRIPTION);
expect(taskPage.taskDetails().getAssignee()).toEqual(taskModel.getAssignee().getEntireName());
@@ -288,7 +288,7 @@ describe('Task Details component', () => {
const taskModel = new TaskModel(allTasks.data[0]);
taskPage.tasksListPage().checkContentIsDisplayed(taskModel.getName());
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATA_FORMAT));
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATE_FORMAT));
expect(taskPage.taskDetails().getId()).toEqual(taskModel.getId());
expect(taskPage.taskDetails().getDescription()).toEqual(CONSTANTS.TASK_DETAILS.NO_DESCRIPTION);
expect(taskPage.taskDetails().getAssignee()).toEqual(taskModel.getAssignee().getEntireName());
@@ -321,7 +321,7 @@ describe('Task Details component', () => {
const taskModel = new TaskModel(getTaskResponse);
taskPage.tasksListPage().checkContentIsDisplayed(taskModel.getName());
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATA_FORMAT));
expect(taskPage.taskDetails().getCreated()).toEqual(dateFormat(taskModel.getCreated(), TASK_DATE_FORMAT));
expect(taskPage.taskDetails().getId()).toEqual(taskModel.getId());
expect(taskPage.taskDetails().getDescription()).toEqual(CONSTANTS.TASK_DETAILS.NO_DESCRIPTION);
expect(taskPage.taskDetails().getAssignee()).toEqual(taskModel.getAssignee().getEntireName());

View File

@@ -118,7 +118,7 @@ exports.PROCESS_BUSINESS_KEY = "None";
exports.PROCESS_DESCRIPTION = "No description";
exports.PROCESS_DATE_FORMAT = "mmm dd yyyy";
exports.PROCESS_DATE_FORMAT = "mmm dd, yyyy";
exports.PROCESS_DETAILS = {
NO_PARENT: "None",

View File

@@ -58,7 +58,8 @@ export class BasicPropertiesService {
label: 'CORE.METADATA.BASIC.CREATED_DATE',
value: node.createdAt,
key: 'createdAt',
editable: false
editable: false,
format: 'mediumDate'
}),
new CardViewTextItemModel({
label: 'CORE.METADATA.BASIC.SIZE',
@@ -77,7 +78,8 @@ export class BasicPropertiesService {
label: 'CORE.METADATA.BASIC.MODIFIED_DATE',
value: node.modifiedAt,
key: 'modifiedAt',
editable: false
editable: false,
format: 'mediumDate'
}),
new CardViewTextItemModel({
label: 'CORE.METADATA.BASIC.MIMETYPE',

View File

@@ -59,7 +59,7 @@ describe('CardViewDateItemComponent', () => {
const value = fixture.debugElement.query(By.css('.adf-property-value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText.trim()).toBe('Jul 10 2017');
expect(value.nativeElement.innerText.trim()).toBe('Jul 10, 2017');
});
it('should NOT render the default as value if the value is empty, editable:false and displayEmpty is false', () => {
@@ -122,7 +122,7 @@ describe('CardViewDateItemComponent', () => {
const value = fixture.debugElement.query(By.css('.adf-property-value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText.trim()).toBe('Jul 10 2017');
expect(value.nativeElement.innerText.trim()).toBe('Jul 10, 2017');
});
it('should render the picker and toggle in case of editable:true', () => {

View File

@@ -74,8 +74,10 @@ describe('CardViewComponent', () => {
it('should render the date in the correct format', async(() => {
component.properties = [new CardViewDateItemModel({
label: 'My date label', value: '2017-06-14', key: 'some key',
format: 'MMM DD YYYY'
label: 'My date label',
value: '2017-06-14',
key: 'some key',
format: 'short'
})];
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -87,7 +89,7 @@ describe('CardViewComponent', () => {
const value = fixture.debugElement.query(By.css('.adf-property-value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText).toBe('Jun 14 2017');
expect(value.nativeElement.innerText).toBe('6/14/17, 12:00 AM');
});
}));

View File

@@ -19,4 +19,5 @@ import { CardViewItemProperties } from './card-view-item-properties.interface';
export interface CardViewDateItemProperties extends CardViewItemProperties {
format?: string;
locale?: string;
}

View File

@@ -15,15 +15,18 @@
* limitations under the License.
*/
import moment from 'moment-es6';
import { CardViewItem } from '../interfaces/card-view-item.interface';
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
import { CardViewBaseItemModel } from './card-view-baseitem.model';
import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces';
import { LocalizedDatePipe } from '../../pipes/localized-date.pipe';
export class CardViewDateItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'date';
format: string = 'MMM DD YYYY';
format: string;
locale: string;
localizedDatePipe: LocalizedDatePipe;
constructor(cardViewDateItemProperties: CardViewDateItemProperties) {
super(cardViewDateItemProperties);
@@ -32,13 +35,18 @@ export class CardViewDateItemModel extends CardViewBaseItemModel implements Card
this.format = cardViewDateItemProperties.format;
}
if (cardViewDateItemProperties.locale) {
this.format = cardViewDateItemProperties.locale;
}
}
get displayValue() {
if (!this.value) {
return this.default;
} else {
return moment(this.value).format(this.format);
this.localizedDatePipe = new LocalizedDatePipe();
return this.localizedDatePipe.transform(this.value, this.format, this.locale);
}
}
}

View File

@@ -21,5 +21,5 @@ import { CardViewDateItemModel } from './card-view-dateitem.model';
export class CardViewDatetimeItemModel extends CardViewDateItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'datetime';
format: string = 'MMM DD YYYY HH:mm';
format: string = 'MMM d, y, h:mm';
}

View File

@@ -39,8 +39,8 @@ describe('LocalizedDatePipe', () => {
}));
it('should return time with locale en-US', () => {
const date = new Date('1990-11-03');
expect(pipe.transform(date)).toBe('Nov 3, 1990, 12:00:00 AM');
const date = new Date('1990-11-03 00:00');
expect(pipe.transform(date)).toBe('Nov 3, 1990');
});
it('should return correct date when formating and locating it', () => {

View File

@@ -27,17 +27,25 @@ import { UserPreferencesService, UserPreferenceValues } from '../services/user-p
export class LocalizedDatePipe implements PipeTransform {
static DEFAULT_LOCALE = 'en-US';
static DEFAULT_DATE_TIME_FORMAT = 'medium';
static DEFAULT_DATE_FORMAT = 'mediumDate';
defaultLocale: string;
defaultFormat: string;
defaultLocale: string = LocalizedDatePipe.DEFAULT_LOCALE;
defaultFormat: string = LocalizedDatePipe.DEFAULT_DATE_FORMAT;
constructor(public userPreferenceService: UserPreferencesService,
public appConfig: AppConfigService) {
constructor(public userPreferenceService?: UserPreferencesService,
public appConfig?: AppConfigService) {
if (this.userPreferenceService) {
this.userPreferenceService.select(UserPreferenceValues.Locale).subscribe((locale) => {
this.defaultLocale = locale || LocalizedDatePipe.DEFAULT_LOCALE;
if (locale) {
this.defaultLocale = locale;
}
});
this.defaultFormat = this.appConfig.get<string>('dateValues.defaultFormat', LocalizedDatePipe.DEFAULT_DATE_TIME_FORMAT);
}
if (this.appConfig) {
this.defaultFormat = this.appConfig.get<string>('dateValues.defaultDateFormat', LocalizedDatePipe.DEFAULT_DATE_FORMAT);
}
}
transform(value: any, format?: string, locale?: string): any {

View File

@@ -128,7 +128,7 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-startDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('Mar 09 2019');
expect(valueEl.nativeElement.innerText.trim()).toBe('Mar 9, 2019');
});
}));
@@ -138,7 +138,7 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-lastModified"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('Mar 09 2019');
expect(valueEl.nativeElement.innerText.trim()).toBe('Mar 9, 2019');
});
}));

View File

@@ -37,13 +37,16 @@ export class ProcessHeaderCloudComponent implements OnChanges {
processInstanceId: string;
processInstanceDetails: ProcessInstanceCloud = new ProcessInstanceCloud();
properties: CardViewItem[];
dateFormat: string;
dateLocale: string;
constructor(
private processHeaderCloudService: ProcessHeaderCloudService,
private translationService: TranslationService,
private appConfig: AppConfigService) {
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale');
}
ngOnChanges() {
@@ -102,13 +105,17 @@ export class ProcessHeaderCloudComponent implements OnChanges {
{
label: 'ADF_CLOUD_PROCESS_HEADER.PROPERTIES.START_DATE',
value: this.processInstanceDetails.startDate,
key: 'startDate'
key: 'startDate',
format: this.dateFormat,
locale: this.dateLocale
}),
new CardViewDateItemModel(
{
label: 'ADF_CLOUD_PROCESS_HEADER.PROPERTIES.LAST_MODIFIED',
value: this.processInstanceDetails.lastModified,
key: 'lastModified'
key: 'lastModified',
format: this.dateFormat,
locale: this.dateLocale
}),
new CardViewTextItemModel(
{

View File

@@ -118,7 +118,7 @@ describe('TaskHeaderCloudComponent', () => {
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('Dec 18 2018');
expect(valueEl.nativeElement.innerText.trim()).toBe('Dec 18, 2018');
});
}));

View File

@@ -60,6 +60,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
inEdit: boolean = false;
parentTaskName: string;
dateFormat: string;
dateLocale: string;
private subscriptions: Subscription[] = [];
@@ -71,6 +72,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
private cardViewUpdateService: CardViewUpdateService
) {
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale');
}
ngOnInit() {
@@ -126,7 +128,9 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
value: this.taskDetails.dueDate,
key: 'dueDate',
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.DUE_DATE_DEFAULT'),
editable: true
editable: true,
format: this.dateFormat,
locale: this.dateLocale
}
),
new CardViewTextItemModel(
@@ -141,7 +145,9 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
{
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.CREATED',
value: this.taskDetails.createdDate,
key: 'created'
key: 'created',
format: this.dateFormat,
locale: this.dateLocale
}
),
new CardViewTextItemModel(
@@ -163,7 +169,9 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
{
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.END_DATE',
value: this.taskDetails.completedDate,
key: 'endDate'
key: 'endDate',
format: this.dateFormat,
locale: this.dateLocale
}
),
new CardViewTextItemModel(

View File

@@ -71,7 +71,7 @@ describe('ProcessInstanceHeaderComponent', () => {
component.ngOnChanges({});
fixture.detectChanges();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-dateitem-ended"]');
expect(valueEl.innerText).toBe('Nov 03 2016');
expect(valueEl.innerText).toBe('Nov 3, 2016');
});
it('should display placeholder if no due date', () => {
@@ -103,7 +103,7 @@ describe('ProcessInstanceHeaderComponent', () => {
component.ngOnChanges({});
fixture.detectChanges();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-dateitem-created"]');
expect(valueEl.innerText).toBe('Nov 03 2016');
expect(valueEl.innerText).toBe('Nov 3, 2016');
});
it('should display started by', () => {

View File

@@ -31,9 +31,13 @@ export class ProcessInstanceHeaderComponent implements OnChanges {
processInstance: ProcessInstance;
properties: CardViewItem [];
dateFormat: string;
dateLocale: string;
constructor(private translationService: TranslationService,
private appConfig: AppConfigService) {
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale');
}
ngOnChanges(changes: SimpleChanges) {
@@ -60,7 +64,8 @@ export class ProcessInstanceHeaderComponent implements OnChanges {
{
label: 'ADF_PROCESS_LIST.PROPERTIES.END_DATE',
value: this.processInstance.ended,
format: 'MMM DD YYYY',
format: this.dateFormat,
locale: this.dateLocale,
key: 'ended',
default: this.translationService.instant('ADF_PROCESS_LIST.PROPERTIES.END_DATE_DEFAULT')
}),
@@ -89,7 +94,8 @@ export class ProcessInstanceHeaderComponent implements OnChanges {
{
label: 'ADF_PROCESS_LIST.PROPERTIES.CREATED',
value: this.processInstance.started,
format: 'MMM DD YYYY',
format: this.dateFormat,
locale: this.dateLocale,
key: 'created'
}),
new CardViewTextItemModel(

View File

@@ -266,7 +266,7 @@ describe('TaskHeaderComponent', () => {
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('Nov 03 2016');
expect(valueEl.nativeElement.innerText.trim()).toBe('Nov 3, 2016');
});
}));

View File

@@ -58,12 +58,16 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
properties: CardViewItem [];
inEdit: boolean = false;
dateFormat: string;
dateLocale: string;
constructor(private activitiTaskService: TaskListService,
private bpmUserService: BpmUserService,
private translationService: TranslationService,
private logService: LogService,
private appConfig: AppConfigService) {
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale');
}
ngOnInit() {
@@ -107,7 +111,9 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
value: this.taskDetails.dueDate,
key: 'dueDate',
default: this.translationService.instant('ADF_TASK_LIST.PROPERTIES.DUE_DATE_DEFAULT'),
editable: true
editable: true,
format: this.dateFormat,
locale: this.dateLocale
}
),
new CardViewTextItemModel(
@@ -131,7 +137,9 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
{
label: 'ADF_TASK_LIST.PROPERTIES.CREATED',
value: this.taskDetails.created,
key: 'created'
key: 'created',
format: this.dateFormat,
locale: this.dateLocale
}
),
new CardViewTextItemModel(
@@ -152,7 +160,9 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
{
label: 'ADF_TASK_LIST.PROPERTIES.END_DATE',
value: this.taskDetails.endDate,
key: 'endDate'
key: 'endDate',
format: this.dateFormat,
locale: this.dateLocale
}
),
new CardViewTextItemModel(