【问题标题】:breezejs: Nonscalar navigation properties are readonly微风js:非标量导航属性是只读的
【发布时间】:2016-12-14 17:23:23
【问题描述】:

我有以下元数据:

var entityTypeParent = {
    shortName: 'ParentItemType',
    namespace: 'MyNamespace',
    autoGeneratedKeyType: Identity,
    defaultResourceName: 'ParentItemTypes',

    dataProperties: {
        id: { dataType: DT.Int32, isPartOfKey: true }
    },

    navigationProperties: {
        users: {
            entityTypeName: 'User',
            isScalar: false,
            associationName: 'ParentItem_User'
        }
    }
};

var entityTypeUser = {
    shortName: 'User',
    namespace: 'MyNamespace',
    autoGeneratedKeyType: Identity,
    defaultResourceName: 'Users',

    dataProperties: {
        loginName: { dataType: DT.String, isPartOfKey: true },
        displayText: {},
        parentItemId: {
            dataType: DT.Int32
        }
    },

    navigationProperties: {
        agendaTask: {
            entityTypeName: 'ParentItemType',
            associationName: 'ParentItem_User',
            foreignKeyNames: ['parentItemId']
        }
    }
};

在 UI 中,我有一个控件(Kendo Multi-Select),它绑定到 ParentItemType.users 属性(AngularJS 绑定),它允许从列表中选择一个用户(使用微风剑道桥和 'webApiOData 检索' 适配器)。

选择用户会导致“错误:非标量导航属性是只读的 - 可以添加或删除实体,但不能更改集合。” setNpValue (https://github.com/Breeze/breeze.js/blob/397b2a02aa2173175c304eb1b37332f1656db6f5/src/a35_defaultPropertyInterceptor.js#L287) 中的异常。

如果我将定义更改为 isScalar eq true。我在https://github.com/Breeze/breeze.js/blob/397b2a02aa2173175c304eb1b37332f1656db6f5/src/a35_defaultPropertyInterceptor.js#L298 得到一个例外@

context.newValue 是一个用户实体数组。

这是我的元数据定义中的某个错误吗?实际上我只想在我的 ParentItem 中有多个用户。

微风版本:1.5.4

【问题讨论】:

    标签: angularjs kendo-ui breeze


    【解决方案1】:

    不,这不是您的元数据定义中的错误。这是设计使然。非标量导航属性由 Breeze 管理。多选控件尝试获取值数组的所有权并将其替换为新数组。 Breeze 不允许这样做,因为这将是灾难性的并破坏您的实体。

    多选控件有点麻烦。您需要在用户进行选择时利用控件触发的事件来更新导航属性,并从所选值手动创建关联实体,并在用户取消选择它们时将其删除。我没有 KendoUI 的示例,但是 FWIW,这里是 DevExpress TagBox 和以下实体模型的 Angular2 示例。

    Model(Subject) 1n SubjectVehicleAssociation n1 LookupItem(Vehicle)

    这涉及到一些工作。开箱即用的多选控件仅适用于基本数组。

    <dx-tag-box [dataSource]="adapter.dataSource"
            displayExpr="description"
            [value]="adapter.value"
            [searchEnabled]="true"
            (onSelectionChanged)="adapter.selectionChanged($event)">
    
    </dx-tag-box>
    

    在组件中创建适配器实例:

    let valueConverter: ITagBoxValueConverter<SubjectVehicleAssociation, { code: string, longValue: string }> = {
        match: (item: SubjectVehicleAssociation, lookupItem: { code: string, longValue: string }) => item.abbreviation === lookupItem.code,
        seed: (item: SubjectVehicleAssociation, lookupItem: { code: string, longValue: string }) => {
            item.description = lookupItem.longValue;
            item.abbreviation = lookupItem.code;
            return item;
        }
    };
    this.adapter = new TagBoxAdapter(SubjectVehicleAssociation,
        () => Promise.resolve(this.vehicleAssociationsLookup),
        this.modelSubjectVehicleRelation,
        'associations',
        valueConverter);
    

    以及适配器实现:

    import DataSource from 'devextreme/data/data_source';
    import * as _ from 'lodash';
    
    import { Entity, EntityState } from 'breeze-client';
    
    export interface ITagBoxValueConverter<T extends Entity, W> {
    
        match(value1: T, value2: W): boolean;
    
        seed(target: T, source: W): T;
    }
    
    export class TagBoxAdapter<T extends Entity, W> {
    
        dataSource: DataSource;
    
        constructor(private itemType: { new (): T },
            private data: (searchValue?: string, maxSearchResults?: number) => Promise<W[]>,
            private model: any,
            private valueExpr: string,
            private valueConverter: ITagBoxValueConverter<T, W>,
            private maxSearchResults: number = 20) {
    
            this.initializeDataSource();
        }
    
        get value(): T[] {
            return _.get<T[]>(this.model, this.valueExpr, []);
        }
    
        selectionChanged(e: { addedItems: T[], removedItems: T[] }) {
    
            e.addedItems.forEach(item => {
                if (item.entityAspect && item.entityAspect.entityState.isDeleted()) {
                    // Real entity, needs to be resurrected
                    item.entityAspect.rejectChanges();
                } else {
                    // Placeholder entity, needs to be attached first
                    this.model.entityAspect.entityManager.attachEntity(item, EntityState.Added);
                    this.value.push(item);
                }
            })
    
            e.removedItems.forEach(item => {
                item.entityAspect.setDeleted();
            })
    
        };
    
        private initializeDataSource() {
            let sourceData = (searchValue: string, maxSearchResults: number) => {
                return this.data(searchValue, maxSearchResults).then((results) => {
                    return results.map(dataItem => {
                        // Find existing association entity if exists
                        let item = _.find(this.value, item => this.valueConverter.match(item, dataItem));
                        if (item) return item;
    
                        // Not associated yet, return placeholder association entity
                        return this.valueConverter.seed(new this.itemType(), dataItem);
                    })
                });
            };
    
            this.dataSource = new DataSource({
                // The LoadOptions interface is defined wrong it's lacking the search properties
                load: (loadOptions: any) => {
                    let searchValue = loadOptions.searchValue ? loadOptions.searchValue : "";
                    return sourceData(searchValue, this.maxSearchResults).then(data => data.filter(item => !_.intersection(this.value, [item]).length));
                },
                byKey: (key) => {
                    return sourceData(key, 1).then((data) => _.find(data, key));
                }
            });
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-19
      • 2013-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多