Issue with binding nested ViewModels/components in Knockoutjs using TypeScript does not resolve

Struggling with implementing a viewModel within another viewModel in knockout. Any assistance would be greatly appreciated. Using typescript and aiming to have a list of address controls, each with their individual viewmodel.

Initially, the project functioned well with just one address control. However, upon adding the controls to a parent container viewmodel and utilizing components, the child viewmodel is consistently empty (i.e. {} as indicated by the alert statements below):

Refer to my app.ts file for complete code at https://github.com/richbeales/knockout-test

///<reference path="../node_modules/retyped-knockout-tsd-ambient/knockout.d.ts"/>

import * as ko from 'knockout';
import {AddressViewModel, Address} from './components/paf-widget';

ko.components.register('paf-address', {
    viewModel: AddressViewModel, // { require: 'components/paf-widget' },
    template: { require: 'text!components/paf-widget.html' }
});

class AddressesViewModel {
    addressList: KnockoutObservableArray<AddressViewModel>;

    constructor() {
        this.addressList = ko.observableArray<AddressViewModel>();
    }

    addAddress = function () {
        alert(ko.toJSON(this)); // returns {"addressList":[{}]}

        var childVm = new AddressViewModel();
        alert(ko.toJSON(childVm)); // returns {}
        childVm.chosenAddress(new Address());
        childVm.chosenAddress().Organisation = "Test";
        alert(ko.toJSON(childVm)); // returns {}

        var a = new Address();
        a.AddressLine1 = "The Street";
        alert(ko.toJSON(a)); // returns {"AddressLine1":"The Street"}

        this.addressList.push(childVm);
    }
}

var vm = new AddressesViewModel();
ko.applyBindings(vm);

here is my index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Bootstrap with jQuery and KnockoutJS</title>
    <link rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"/>
    <link rel="stylesheet" href="css/style.css"/>
</head>
<body>

<header>

</header>

<h1>App</h1>

Choose an Address
 <pre data-bind="text: ko.toJSON(addressList, null, 2)"></pre>

<ul data-bind="foreach: {data: addressList, as: 'addrVm'}" style="list-style: none;">
    <li class="addressli">
        <pre data-bind="text: ko.toJSON(addrVm, null, 2)"></pre>
        <div data-bind="component: {name: 'paf-address', with: addrVm }"></div>
    </li>
</ul>
<button class="btn btn-primary" data-bind="click: addAddress">Add an address</button>

<footer>

</footer>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.2/require.min.js" data-main="js/app"></script>
<script src="require-config.js"></script>
</body>
</html>

Answer №1

You may be experiencing an unusual issue because you have loaded knockout in multiple ways and inconsistently.

Initially, you loaded knockout directly in your index.html, and then again with require.js in your app.js. As a result, your AddressViewModel is utilizing a different instance of ko compared to your AddressesViewModel, leading to the appearance of strange empty objects.

To resolve this problem, the simplest solution is to eliminate the direct reference to KO in your index.html:

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script>

Instead, properly import knockout into your paf-widget.ts using:

import * as ko from 'knockout';

Remember: after making these changes, ensure that your browser cache is cleared so it can correctly pick up the updated code.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

leveraging parcel for importing typescript dependencies

I am currently using parcel to process typescript for a web extension. I have installed JQuery and its type definitions via npm. In my typescript file, I have the following at the top: import $ from "jquery"; import "bootstrap"; However, when running run ...

Creating Typescript libraries with bidirectional peer dependencies: A complete guide

One of my libraries is responsible for handling requests, while the other takes care of logging. Both libraries need configuration input from the client, and they are always used together. The request library makes calls to the logging library in various ...

Angular2 ngFor, encountering undefined property

Having an issue where one of my properties is showing as "undefined" even though it is defined. Can't seem to find a solution: I have a parent component with the following data: @Component({ selector: "app-my-products", templateUrl: ...

Exploring the nuances of checking lists in TypeScript

In the following list: empList = ['one','two','finish','one','three'] I am required to evaluate conditions based on empList: Condition 1: If 'two' appears before 'finish', set this ...

Enhancing Type Safety in Typescript through Key/Property Type Guards

Is it possible to create a typeguard that can confirm the existence (or specific type) of a property in an object? For example: Consider an interface Foo: interface Foo { bar: string; baz: number; buzz?: string; } An object of type Foo may ...

Issue with nestjs build due to ts-loader module in dev-dependency

I've encountered a Module Error with ts-loader during a docker build ERROR [6/6] RUN nest build 3.9s ------ > [6/6] RUN ...

The error message "Property is not found on type 'Object'" suggests that the property being accessed does not

I wrote a function called getAll getAll<T>() { return this.http.get(`${environment.apiUrl}/products`); } Here is how I am invoking it: this.productService.getAll() .pipe(first()) .subscribe(products => { debugger let s ...

What is the method to group a TypeScript array based on a key from an object within the array?

I am dealing with an array called products that requires grouping based on the Product._shop_id. export class Product { _id: string; _shop_id: string; } export class Variant { variant_id: string; } export interface ShoppingCart { Variant: ...

The interface is unable to populate the Array of Elements

When using Angular, I send a request and save the response in a variable: conversations: Conversation[]; // ChatService getConversations() { return this.http.get<Conversation[]>('/chat/conversations'); } this.chatService.getConversat ...

Starting up a pre-existing Angular project on your local machine

I am completely new to Angular and facing difficulties running an existing project on my machine. Despite conducting numerous tests and following various articles, I still cannot get the project to run. Here is the layout of my project files: I have succ ...

Using an array of objects as a data source for the Material Angular table

My user data is causing some issues and looks like this... [{"firstName":"Pinkie","lastName":"Connelly","username":"Darlene.Marvin","email":"<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="19506a767b7c75464b7c77777c6b5971766d74 ...

Encountered an issue in Angular 6: Unable to access property '0' of undefined

There's an error popping up in the console saying, Cannot read property '0' of undefined, but surprisingly I'm still getting the expected result. Below is my HTML code snippet: <div class="col-md-3"> <div class="slider-prod ...

Automatically insert a hyphen (-) between each set of 10 digits in a phone number as it is being typed into the text

I'm attempting to automatically insert a hyphen (-) in between 10 digits of a phone number as it is being typed into the text field, following North American standard formatting. I want the output to look like this: 647-364-3975 I am using the keyup ...

Guide to defining font style in vanilla-extract/CSS

I'm trying to import a fontFace using vanilla-extract/css but I'm having trouble figuring out how to do it. The code provided in the documentation is as follows: import { fontFace, style } from '@vanilla-extract/css'; const myFont = fo ...

Error: The variable "document" is not defined in the context of NextJS TypeScript

Here is the import trace for the requested module: ./app/StarrySky.tsx ./app/resume/page.tsx ✓ Compiled in 425ms (711 modules) ⨯ app\StarrySky.tsx (9:4) @ document ⨯ ReferenceError: document is not defined Trying to set const vw equal to the ma ...

Updating the styles of React Native components using stylesheets

I've created a unique React component with the following structure: import { StyleSheet } from 'react-native'; import { Input, Item } from 'native-base'; import Icon from 'react-native-vector-icons/FontAwesome'; import { ...

Guide on releasing a TypeScript component for use as a global, commonJS, or TypeScript module

I have developed a basic component using TypeScript that relies on d3 as a dependency. My goal is to make this component available on npm and adaptable for use as a global script, a commonJS module, or a TypeScript module. The structure of the component is ...

How to extract component prop types in Vue 3 with typescript for reusability in other parts of your application

When you specify the props under the "props:" key of a Vue component, Vue can already automatically determine their types, which is quite convenient. However, I am wondering if there is an utility type in Vue that can be used to extract the props' ty ...

Required Ionic form field alert

Currently, I am developing a new app using ionic 3 and I am facing an issue with making inputs mandatory in my ionic-alert controller. Despite going through the ionic-component documentation and api documentation, I couldn't find a solution on how to ...

The Angular build is unsuccessful due to the presence of components from a separate Angular project

Whenever I execute ng build project1 --prod, the build fails and displays this error message: ERROR in : Cannot determine the module for class MyComponent in .../project2/app/my.component.ts! Add MyComponent to the NgModule to fix it.. Although the sol ...