Incorporate a computedObservable into an already established JavaScript view model

I've developed a ViewModel that looks like this.

function PageSectionVM(pageSection) {
    var self = this;
    self.SectionName = ko.observable();
    self.Markup = ko.observable();

    self.update(pageSection);
}

In addition, I've implemented the update method as called in the constructor function above.

PageSectionVM.prototype.update = function (pageSection) {
    var self = this;
    pageSection = pageSection || {};

    self.SectionName(pageSection.SectionName);
    self.Markup(pageSection.Markup);
};

This ViewModel is kept in its own file and I want to be able to reuse it in multiple pages. In one specific page, I aim to 'extend' this viewmodel by adding a new function. I attempted to do this by appending a new function to PageSectionVM's prototype like this.

PageSectionVM.prototype.tabName = function () {
    var self = this;
    return "#tab-" + self.SectionName();
};

However, when I include this as a knockout binding statement, it outputs the actual text of the function instead of the expected result. I suspect there might be something missing in my implementation. When I incorporate tabName as a computedObservable within the original viewmodel, it functions correctly. But, this means inserting specific code for a single purpose into the 'general' viewmodel code, which I'd prefer to avoid.

The knockout binding statement I'm using is

<a data-bind="attr:{href: tabName}, text:SectionName"></a>

This is placed within a foreach loop on an observableArray of PageSectionVMs. The text property displays properly, but the href ends up showing the literal text of the function rather than its output.

Any assistance would be highly appreciated.

Answer №1

When incorporating this into a knockout binding statement, it retrieves the function's text instead of the function's output.

This is expected behavior as Knockout bindings operate in the following manner:

  • Check if the bound value is an observable
  • If so, unwrap it (i.e. "execute it")
  • Convert the remaining value to a string and utilize it in the view

Note: All observables are functions, but not all functions are observables.

Therefore, if your binding appears as text: SectionName and SectionName() is just a regular function, you will receive the function's text.

Observables cannot be used in a prototype due to their functionality with dependency tracking and handling of this. They must exist within the instance.

This leaves you with the following options:

  • Include them in the instance as @WayneEllery recommended
  • Manually call the functions in the view (text: SectionName())
  • Utilize ko.utils.extend() to add additional observables/computeds to existing PageSectionVM instances.

Answer №2

It's puzzling why you're encountering issues with using a computed property. If you prefer to utilize the prototype method, you can achieve it in the following way:

In addition, I've included url encoding and initialized the model.

function PageSectionVM(pageSection) {
    var self = this;
    self.SectionName = ko.observable(pageSection.SectionName);
    self.Markup = ko.observable(pageSection.Markup);    
    self.TabName = ko.computed(this.getTabName, self);
};

PageSectionVM.prototype.getTabName = function () {
    var self = this;
    return "#tab-" + encodeURIComponent(self.SectionName());
};

http://jsfiddle.net/5vUhe/

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

Bootstrap: Retrieve an image from a modal

I am working on a modal that contains a variety of selectable images. When an image is clicked, I want to change the text of the button in the modal to display the name of the selected image. Additionally, I would like to grab the selected image and displa ...

What is the best way to convert exponential values to decimals when parsing JSON data?

var value = '{"total":2.47E-7}' var result = JSON.parse(value); Looking to convert an exponential value into decimal using JavaScript - any suggestions? ...

Generating a task list in React and facing an issue related to an array

I am facing an issue with conditionally rendering a task group based on whether they are marked as completed or pending. function TaskList() { const { tasks } = useContext(TaskContext); return tasks.length === 0 ? ( <h2>No tasks added yet< ...

Techniques for triggering JavaScript on elements that have been dynamically loaded via Ajax

When it comes to ensuring that a certain functionality works both when the document is ready and after an Ajax call, there are some considerations to keep in mind for optimal performance. An approach I found effective involves defining the desired code wi ...

Troubleshooting: Node.js and EJS throwing 404 Error on delete function

I am encountering an issue where I am successfully retrieving the project ID, but when the delete method is called, it results in a 404 error. The code used for this scenario is as follows: Front End <% for(var i=0; i<projects.length; i++) {%> ...

I need assistance with a feature on my website where a new form is added every time the invite button is clicked, and the form is deleted when the delete button is

Invite.js This invite component includes an invite button outside the form and a delete button inside the form. The goal is to delete the form when the delete button is clicked. I have utilized useState and sourced this form from material-ui. Can anyone ...

Creating a seamless navigation experience using Material UI's react Button and react-router-dom Link

Is there a way to have the Material UI react Button component behave like a Link component from react-router-dom while preserving its original style? Essentially, how can I change the route on click? import Button from '@material-ui/core/Button' ...

What is the best way to have child controllers load sequentially within ng-repeat?

Currently, I have a main controller that retrieves data containing x and y coordinates of a table (rows and columns). Each cell has a child controller responsible for preparing the values it will display based on the x and y values from the parent control ...

What is the best way to tally a score after analyzing two spans in Javascript?

I have 3 spans where 2 contain an alphabet each. The third span is left blank for the result of comparing the first 2 spans. I need to display the result in the last span, showing a value of 1 if the two spans contain the same alphabet. Can anyone sugges ...

Customize table rows to stand out depending on the current time

Hey there, I'm looking to dynamically highlight rows based on the current time. For example, if "a" is scheduled for 7:00 and it's now 7:10, then I want "b" to be highlighted instead. Similarly, if "f" is set for 18:00 and it's currently 18 ...

Commitment is not dependent on the completion of promised functions

I've been immersed in a project involving web scraping lately. Although I've made significant progress, I'm currently facing a roadblock. Let me walk you through the workflow: Scrapers are initiated in the scraping-service module. The func ...

Difficulty with event listener in React Material UI Autocomplete

My goal is to configure Material UI's Autocomplete component in a way that allows for automatic selection of the closest match when the tab key is pressed. I also need to capture the e.target.value based on the input. However, I have encountered an is ...

Is there a simpler method to access the source element for an event?

I'm just starting to learn JavaScript and jQuery, and right now I have the following code in my HTML: <a id="tog_table0" href="javascript:toggle_table('#tog_table0', '#hideable_table0');">show</a> After that, I hav ...

Setting a consistent theme or style for all HTML/React tags using a selector inside a specific component

Here's a simplified example of what I'm trying to do: I'm using Material UI Styles for styling my components. I want to style all the <Link> tags in my component. For instance: const useStyles = makeStyles(theme => ({ menuLink: ...

What is the best way to incorporate a comment section in my existing comment box in order to display comments beneath it?

Is there a way to include a comment feature in my comment box so that comments are displayed beneath it? The comment box itself is set up, but this specific addition is missing. Here's my HTML code... <!DOCTYPE html> <html lang="en&quo ...

Methods for dynamically adjusting content based on window width in Three.js

Currently, I have implemented a basic window resizing code in my project: function onWindowResize() { windowHalfX = window.innerWidth / 2; windowHalfY = window.innerHeight / 2; camera.aspect = window.innerWidth / window.innerHeight; came ...

Creating an array of objects sorted in alphabetical order

My task involves working with an array of objects that each have a name property: var myList = [{ name: 'Apple' }, { name: 'Nervousness', }, { name: 'Dry' }, { name: 'Assign' }, { name: 'Date' }] ...

What sets apart an object within the scalajs scope from the exact same object within the js.global scope?

Attempting to create a basic example for rendering a cube using the THREEJS library. package three import org.scalajs.dom import scala.scalajs.js import scala.scalajs.js.Dynamic._ import scala.scalajs.js.annotation.JSName ... object ThreeExample { d ...

What are the steps to set up ChartJS on a personal computer?

Currently, I am working on creating charts with ChartJS using the CDN version. However, I would like to have it installed directly on my website. After downloading ChartJS v4.1.1, I realized that it only contains typescript files. Since I cannot use TS fil ...

Modify the CSS properties of the asp:AutoCompleteExtender using JavaScript

Is there a way to dynamically change the CompletionListItemCssClass attribute of an asp:AutoCompleteExtender using JavaScript every time the index of a combobox is changed? Here is the code snippet: ajaxtoolkit: <asp:AutoCompleteExtender ID="autocom" C ...