KnockoutJS - update observable from another modelview
Asked Answered
H

2

0

I have an existing ComposePopupView() modelview which contains observable

this.attachments = ko.observableArray([]);

and a method below

ComposePopupView.prototype.addDropboxAttachment = function (oDropboxFile)
{
    var
        oAttachment = null,
        iAttachmentSizeLimit = Utils.pInt(Settings.settingsGet('AttachmentLimit')),
        mSize = oDropboxFile['bytes']
    ;

    oAttachment = new ComposeAttachmentModel(
        oDropboxFile['link'], oDropboxFile['name'], mSize
    );

    oAttachment.fromMessage = false;
    oAttachment.cancel = this.cancelAttachmentHelper(oDropboxFile['link']);
    oAttachment.waiting(false).uploading(true).complete(false);

    this.attachments.push(oAttachment);

    this.attachmentsPlace(true);

    if (0 < mSize && 0 < iAttachmentSizeLimit && iAttachmentSizeLimit < mSize)
    {
        oAttachment.uploading(false).complete(true);
        oAttachment.error(Translator.i18n('UPLOAD/ERROR_FILE_IS_TOO_BIG'));
        return false;
    }

    Remote.composeUploadExternals(function (sResult, oData) {

        var bResult = false;
        oAttachment.uploading(false).complete(true);

        if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
        {
            if (oData.Result[oAttachment.id])
            {
                bResult = true;
                oAttachment.tempName(oData.Result[oAttachment.id]);
            }
        }

        if (!bResult)
        {
            oAttachment.error(Translator.getUploadErrorDescByCode(Enums.UploadErrorCode.FileNoUploaded));
        }

    }, [oDropboxFile['link']]);

    return true;
};

Then I have created my other modelview called UsersDocumentsPopupView() where I'm accessing above method as

DropBox = __webpack_require__(/*! View/Popup/Compose */ 31)
....
DropBox.prototype.addDropboxAttachment(aFiles[0]);

but it throws an error

Cannot read property 'attachments' of undefined

Then I have decided to add observable

this.attachments = ko.observableArray([]);

to my modelview and then tired to do

this.attachment.push(oAttachment);

where oAttachment is an object taken from aFiles array but still I'm getting the same error.

My question is how or even if can I update observable attachments from one modelview executing another modelview?

PS. When I will do code below in the ComposePopupView() it works fine

var aFiles = [JSON.parse('{"isDir": false,  "name": "koala.jpg", "bytes": 780831,"link": "http://localhost/data/koala.jpg","id": "id:UxmT1S5QcFAAAAAAAAAACw"}')];

if (aFiles && aFiles[0] && aFiles[0]['link'])
{
  console.log("Test");
  self.addDropboxAttachment(aFiles[0]);
}

So in this case how can I pass data aFiles from the UsersDocumentsPopupView() to ComposePopupView() modelview?

Heligoland answered 16/8, 2018 at 19:10 Comment(0)
H
0

I have ended up to using Knockout's pub/sub functionality.

Basic example below:

var postbox = ko.observable();

function MyModalViewA()
{
    var _self = this;
    var test = new Array();
    _self.rows = ko.observableArray([]);

    postbox.subscribe(function(newValue) {         
          _self.rows.push(newValue);
          //test = test.concat(newValue);
          console.log("Rows " + JSON.stringify(test));
      }, null, "NewRowAvailable"
      );
};

function MyModalViewB()
{
    var _self = this;

    _self.search = function() {

        var newRow = JSON.parse('{ "label" : "abc" }');
        postbox.notifySubscribers(newRow, "NewRowAvailable");      
    };
};

var vma = new MyModalViewA();
ko.applyBindings(vma, $("#vma").get(0));

var vmb = new MyModalViewB();
ko.applyBindings(vmb, $("#vmb").get(0));

The first view model subscribes to a specific topic and the second view model notifies through the postbox on that topic. There is no direct dependency on each other.

Certainly the postbox would not need to be global and could be passed into the view model constructor functions or just created inside a self-executing function.

Sample: http://jsfiddle.net/xpvt214o/708647/

Also, the postbox could just be a ko.subscribable() (which is included in the ko.observable functions).

Heligoland answered 30/8, 2018 at 15:15 Comment(0)
P
1

attachments is a property on an instance of ComposePopupView.

addDropboxAttachment is a method on the prototype of ComposePopupView that has to be called with the this context referring to an instance of ComposePopupView in order to work.

There are several ways to solve the issue. For example:

  • Most likely: you haven't created an instance yet:

    DropBox = __webpack_require__(/*! View/Popup/Compose */ 31);
    const dropBox = new DropBox();
    dropBox.addDropboxAttachment(aFiles[0]);
    
  • Likely: You've already created an instance but can't access it: Pass a reference to the instance you want to refer to along in your constructors.

  • Are you only going to ever create one instance of ComposePopupView? Have a look at the "Singleton" pattern. E.g.:

    // "Private" to the module
    const MySingletonClass = function() { /* ... */ }
    MySingletonClass.prototype.someMethod = function () { };
    
    let singletonInstance = null;
    
    // Only expose a way to get the instance:
    export const getSingletonInstance = function() {
      if (!singletonInstance) singletonInstance = new MySingletonClass();
      return singletonInstance;
    }
    
    // In another module:
    import { getSingletonInstance } from "./MySingletonClass";
    
    const dropBox = getSingletonInstance();
    dropBox.someMethod(aFiles[0]);
    
  • Planning on creating several instances that share the attachments data? Make it a static value.

Phenacaine answered 20/8, 2018 at 12:39 Comment(5)
Thanks for response. I have tried to get it updated using PubSub but I was't lucky and posted here #51931981 I will take a look at your example when I clear my head first ;)Heligoland
OK, I have tried to go for the singleton of ComposePopupView but somehow I was not able to get it to work correctly for Rainloop existing code. Then I took approach to share attachments data - was able to get working instance of ComposePopupView in my modal, attachments got updated with my data but then the view for ComposePopupView wasn't updated with new observable data (expected to display number of attachments based on that observable array). Also what do you have on mind by making attachments data static?Heligoland
Well, it came up that my modal is trying to update my view for UsersDocumentsPopupView() rather than update view for ComposePopupView(). Is there any way to do that?Heligoland
There are many ways to pass references along between viewmodels. If you link to a runnable snippet I can probably help, but it's kind of hard to solve without more context.Phenacaine
Full app code is here: repository.rainloop.net/v2/webmail/1.9.1/… and KnockoutJS part is located in the static/js/app.js as View/Popup/Compose module.Heligoland
H
0

I have ended up to using Knockout's pub/sub functionality.

Basic example below:

var postbox = ko.observable();

function MyModalViewA()
{
    var _self = this;
    var test = new Array();
    _self.rows = ko.observableArray([]);

    postbox.subscribe(function(newValue) {         
          _self.rows.push(newValue);
          //test = test.concat(newValue);
          console.log("Rows " + JSON.stringify(test));
      }, null, "NewRowAvailable"
      );
};

function MyModalViewB()
{
    var _self = this;

    _self.search = function() {

        var newRow = JSON.parse('{ "label" : "abc" }');
        postbox.notifySubscribers(newRow, "NewRowAvailable");      
    };
};

var vma = new MyModalViewA();
ko.applyBindings(vma, $("#vma").get(0));

var vmb = new MyModalViewB();
ko.applyBindings(vmb, $("#vmb").get(0));

The first view model subscribes to a specific topic and the second view model notifies through the postbox on that topic. There is no direct dependency on each other.

Certainly the postbox would not need to be global and could be passed into the view model constructor functions or just created inside a self-executing function.

Sample: http://jsfiddle.net/xpvt214o/708647/

Also, the postbox could just be a ko.subscribable() (which is included in the ko.observable functions).

Heligoland answered 30/8, 2018 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.