如何在淘汰赛 foreach 绑定中使用表单

Posted

技术标签:

【中文标题】如何在淘汰赛 foreach 绑定中使用表单【英文标题】:How to use form inside knockout foreach binding 【发布时间】:2019-07-25 05:45:57 【问题描述】:

我有一个显示作者列表的页面,我希望能够为每个作者上传他们的图片。 我在 foreach 中有一个表单(对于每个作者或咨询编辑),它调用自定义绑定函数“fileupload” 这是用于上传图像和调用 API 端点的处理程序。

问题在于 API URL

this.EditorImageUploadUrl(Api.baseApi + "/topics/" + this.Id + "/uploadEditorImage/" + e.AuthorRef);

它总是返回一个作者引用,它是数组中的最后一个,而不是我选择的实际作者...... 我会以错误的方式解决这个问题吗?如何返回正确的 URL?有没有更好的写法?

谢谢

html

  <div class="consulting-editors" data-bind="foreach: ConsultingEditors">

        <i class="fa fa-times" data-bind="click: $parent.removeConsultingEditor"></i>
        <span class="editor-name" data-bind="text: AuthorName"></span>

        <form method="post" enctype="multipart/form-data" data-bind="fileupload:  url: $parent.EditorImageUploadUrl " class="form-horizontal">
            <div class="upload-image">
                <div class="button">
                    <span class="btn btn-success fileinput-button">
                        <input type="file" id="file" class="hidden" />
                        <label for="file">Add editor photo</label>
                    </span>
                </div>
                <div class="progress" style="width: 30%; float: left; margin: 10px 0 0; display:none;">
                    <div class="bar" style="width: 0%;"></div>
                </div>
                <div class="info" style="width:30%; float:left; margin: 10px 0 0; display:none;"></div>
            </div>
        </form>

        <div class="editor-image">
            <img data-bind="attr:  src: EditorImageUrl " />
        </div>

    </div>

获取图片上传URL和源路径

export class Topic 

    ConsultingEditors: KnockoutObservableArray<NavigatorAuthorApi> = ko.observableArray();
    EditorImageUploadUrl: KnockoutObservable<string> = ko.observable();
    EditorImageSource: KnockoutObservable<string[]> = ko.observable();

    removeConsultingEditor = (editor: NavigatorAuthorApi) => 
        this.ConsultingEditors.remove(editor);
    


    constructor(data: NavigatorTopicApi) 
        this.Id = data.Id;
        this.ConsultingEditors(data.ConsultingEditors);

        data.ConsultingEditors.forEach((e) => 
            e.EditorImageUrl = 'data:image/jpeg;base64,' + e.EditorImage;
            this.EditorImageUploadUrl(Api.baseApi + "/topics/" + this.Id + "/uploadEditorImage/" + e.AuthorRef);
        );

        ...
    



export type NavigatorAuthorApi =
    
        SortOrder: number,
        FirmRef: number,
        FirmName: string,
        AuthorRef: number,
        AuthorName: string,
        DisplayString: string,
        EditorImage: ByteString[],
        EditorImageUrl: string
    

文件上传脚本

import  ConfirmDialog  from '../../Components/Typescript/confirmdialog';

declare global 
    interface JQuery 
        fileupload(option: any, url?: any, value?: any): any;
        confirmDialog: ConfirmDialog;
    


export default function init(ko: KnockoutStatic) 
    ko.bindingHandlers.fileupload = 
        init: function (element, valueAccessor, allBindings) 
            var config = ko.unwrap(valueAccessor());
            var url = ko.unwrap(config.url);
            var confirmDialog =  new ConfirmDialog("#confirmDialog");


            $(element).fileupload(
                add: function (e, data) 
                    var acceptFileTypes = config.acceptFileTypes || /(\.|\/)(jpg|gif|jpeg|png)$/i; // default to just images
                    if (data.originalFiles[0]['type'].length && !acceptFileTypes.test(data.originalFiles[0]['name'])) 
                        confirmDialog.showFailure( title: "Error", subHeader: "Files of this type cannot be uploaded");
                    
                    else 
                        data.submit();
                    
                ,
                url: url,
                type: 'POST',
                progressall: function (e, data) 
                    var calc = data.loaded / data.total * 100;
                    var progress = parseInt(calc.toString(), 10);
                    $(element).find('.progress .bar').css(
                        'width',
                        progress + '%'
                    );
                    var filesize, loaded;
                    if (data.total >= 1000000000) 
                        filesize = (data.total / 1000000000).toFixed(2) + ' Gbit/s';
                    
                    else if (data.total >= 1000000) 
                        filesize = (data.total / 1000000).toFixed(2) + ' Mbit/s';
                    
                    else if (data.total >= 1000) 
                        filesize = (data.total / 1000).toFixed(2) + ' kbit/s';
                    
                    else 
                        filesize = data.total.toFixed(2) + ' bit/s';
                    

                    if (data.loaded >= 1000000000) 
                        loaded = (data.loaded / 1000000000).toFixed(2) + ' Gbit/s';
                    
                    else if (data.loaded >= 1000000) 
                        loaded = (data.loaded / 1000000).toFixed(2) + ' Mbit/s';
                    
                    else if (data.loaded >= 1000) 
                        loaded = (data.loaded / 1000).toFixed(2) + ' kbit/s';
                    
                    else 
                        loaded = data.loaded.toFixed(2) + ' bit/s';
                    

                    $(element).find('.info').html(loaded + '/' + filesize);
                ,
                start: function (e, data) 
                    $(element).find('.progress, .info').show();
                ,
                done: function (e, data) 
                    setTimeout(function () 
                        $(element).find('.progress, .info').fadeOut(400, function () 
                            $(element).find('.progress .bar').css('width', '0%');
                        );
                    , 500);

                    confirmDialog.showSuccess( title: "Success", subHeader: "Image uploaded successfully" );

                ,
                fail: function (e, data) 
                    setTimeout(function () 
                        $(element).find('.progress, .info').fadeOut(400, function () 
                            $(element).find('.progress .bar').css('width', '0%');
                        );
                    , 500);

                    var xhrObject = data.xhr();
                    if (xhrObject && xhrObject.status == '412') 
                        confirmDialog.showFailure( title: "Error", subHeader: "Failed to upload image " + xhrObject );
                     else 
                        confirmDialog.showFailure( title: "Error", subHeader: "Failed to upload image " + data );
                    
                
            );
        
    ;
 

API 端点

    [Route("topics/topicId/uploadEditorImage/authorRef")]
    [HttpPost]
    public async Task<HttpResponseMessage> UpdateEditorImage(Guid topicId, int authorRef)
    
        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);

        if (topicId != null)
        
            if (Request.Content.IsMimeMultipartContent())
            
                var provider = new MultipartMemoryStreamProvider();
                await Request.Content.ReadAsMultipartAsync(provider);

                var file = provider.Contents.First();
                var data = await file.ReadAsByteArrayAsync();

                await topicService.UploadEditorImage(topicId, authorRef, data);
                return Request.CreateResponse(HttpStatusCode.OK);
            
        
        else
        
            response = Request.CreateResponse(HttpStatusCode.BadRequest, "The topic does not exist");
        
        return response;

    

【问题讨论】:

应该EditorImageUploadUrl 是每个ConsultingEditors 的属性吗? 【参考方案1】:

@adiga 是对的,你只是在每次迭代 data.ConsultingEditors.forEach 时静态地重新分配 Topic.EditorImageUploadUrl,显然最后一个最终是 authorRef,也许你可以只创建 url onClick 而不是 in进步? 例如

html 绑定:fileupload: url: $parent.EditorImageUploadUrl, authorRef: authorRef

在bindinghandler中:var authorRef = config.authorRef这样就可以在fileupload bindinghandler中构造url

**评论后更新

那么你为什么不直接在NavigatorAuthorApi.EditorImageUploadUrl-property 上的NavigatorAuthorApi 类中输入 url,这样你的数据列表中的每个对象都会有一个正确的 url,我什至认为那是你的意图,但你有一个 this 而不是 e

data.ConsultingEditors.forEach((e) => 
            e.EditorImageUrl = 'data:image/jpeg;base64,' + e.EditorImage;
            e.EditorImageUploadUrl(Api.baseApi + "/topics/" + this.Id + "/uploadEditorImage/" + e.AuthorRef);
        );

【讨论】:

我需要这个在 url 因为文件上传自定义绑定在别处使用

以上是关于如何在淘汰赛 foreach 绑定中使用表单的主要内容,如果未能解决你的问题,请参考以下文章

淘汰赛无法处理“foreach”的绑定

$index+1 在 Knockout foreach 绑定中

Knockout js foreach 按字母顺序绑定

如何在淘汰赛中使用单向绑定

淘汰赛无法处理绑定“foreach”

淘汰赛foreach绑定不起作用