Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Some questions working with Knockout components (using RequireJS for loading)

I'm currently developing a test web app to test Knockout's component features. I got it working using RequireJS to load the components. Although I was able to do so, I got stuck in some areas, and some areas, I am not sure if I am doing the right thing.

The app consists of a page with a header, a left side bar, and the content area on the space remaining in the right side. These 3 are all registered as components. The left side bar serves as the navigation and has an unordered list where list items are populated by doing an AJAX call. When you click a list item, the content on the right side should show a component/view that corresponds to the clicked list item.

With these, here are my questions:

  1. I am only able to load the items for the left side bar in the actual component. Is this OK? I mean, when the left side bar component loads, it does the AJAX call then loads data on its view model. I did this because this is the only thing I could think of to load the data to the view model. In reality, I prefer to load the data on app start then when side bar loads, put the data in it. So my question is: is the approach I did OK, and is it possible to do my preferred method? If so, how?

  2. I don't know how to properly communicate the click in the side bar to show the content on the right side. How can I do this with the least dependency on each other, or just how can I do this?

  3. Can a parent component with a view model have a child component in its template but that child component gets its data from the parent view model? If so, how to do it?

These are the issues that's been keeping me reading for days but still can't get it to work or am still confused. If you have an answer even to a single item only, please don't hesitate to post. Hopefully, all of them will get answered.

EDIT (Source Code)

For the header:

Template

<div>
</div>

VM

define(["text!./admin-main-header.html"], function(template) {

    function AdminMainHeaderViewModel() {
    }

    return { viewModel: AdminMainHeaderViewModel, template: template };
});

For the side bar:

Template

    <div class="vmsc" data-bind="with: adminModules, vertMenu: adminModules">
    <ul data-bind="foreach: modules">
        <li class="has-sub"><a href="javascript:void(0);"><span data-bind="text: name"></span></a>
            <ul data-bind="foreach: $data.submodules">
                <li><a href="javascript:void(0);"><span data-bind="text: name"></span></a></li>
            </ul>
        </li>
    </ul>
</div>

VM

    define(["jquery", "knockout", "ko-mapping", 
    "jquery-vertmenusc", "text!./vert-menu.html"], function($, ko, komap, vmsc, template) {

    ko.mapping = komap;

    function VertMenuScViewModel() {        
        var self = this;
        self.adminModules = ko.observable();

        $.get("http://localhost:8080/_mock_data/admin-modules.txt", function(data) { 
            self.adminModules(ko.mapping.fromJSON(data));
        });
    }

    return { viewModel: VertMenuScViewModel, template: template };
});

For the content:

Template

<div>
</div>

VM

define(["text!./admin-main-content.html"], function(template) {

    function AdminMainContentViewModel() {
    }

    return { viewModel: AdminMainContentViewModel, template: template };
});

Main HTML:

    <!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Sample</title>               

    <!-- Scripts -->
    <script src="js/require.min.2.1.20.js" data-main="js/app/require.config.js"></script>
</head>

<body>
    <div>

        <admin-main-header></admin-main-header>

        <div>       
            <admin-main-nav></admin-main-nav>
            <admin-main-content></admin-main-content>
        </div>

    </div>
</body>
</html>

I have no code yet in the content as I don't know how should I pass data. I hope this additional info helps. Thanks.

like image 885
g_b Avatar asked Jun 04 '26 11:06

g_b


1 Answers

I think the feature that will help you the most is using params in the components. I started working with your example code, but it was too complicated to make a straightforward working example. But your parent HTML may look like this:

  <div>
    <admin-main-nav params='lastClicked:chosen'></admin-main-nav>
    <admin-main-content params='toDisplay:chosen'></admin-main-content>
  </div>

where chosen is an observable in your main viewmodel. Your nav viewmodel constructor might start out with something like

function VertMenuScViewModel(params) {
  var self = this;
  self.adminModules = ko.observable();
  self.lastClicked = params.lastClicked;

So now the lastClicked member of the nav viewmodel is the same observable as the chosen from the parent. This is how you can share observables down to components. You can, for example, have the nav component writing to it and the content component reading from it (to set its content). Neither knows about the other, they just know they've been given an observable to use for a purpose.

This is also how you can load data in your main viewmodel and pass it to the nav component.

like image 58
Roy J Avatar answered Jun 06 '26 02:06

Roy J



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!