Coder Social home page Coder Social logo

rniemeyer / knockout-amd-helpers Goto Github PK

View Code? Open in Web Editor NEW
193.0 16.0 34.0 671 KB

This plugin is designed to be a lightweight and flexible solution to working with AMD modules in Knockout.js.

License: MIT License

JavaScript 96.78% HTML 1.34% CSS 1.88%

knockout-amd-helpers's Issues

AfterRender not firing when module is initialized with a null observable

When the module data-binding is bound to an observable in the ViewModel and that ViewModel is originally set to null, changing the observable value afterward won't fire the afterRender callback.

Example in JSFiddle: http://jsfiddle.net/tvillaren/RA8Lf/2/ (initial code taken from #7).
Click on "Toggle" sets observable to a new module, but the afterRender callback is not called.

Possible Workaround:
The observable's default value can be set to

this.activeModule = ko.observable({
        name: null, 
        template: null, 
        afterRender: null
});

Following changes to the observable will fire the afterRender callback in that case.

Knockout-amd-helpers does not play well with r.js namespacing

Hi Guys,

I just ran into an issue where i use namespacing in my r.js setup (http://requirejs.org/docs/faq-advanced.html#rename). Chrome is giving me the exception:

app.js:2811 Uncaught TypeError: Unable to process binding "template: function (){return { name:"CatalogRequestSingle"} }"(โ€ฆ)

In version 0.7.4 require is taken from the window object. So i changed the code so that require is actually a dependency:

define(["knockout", 'require'], function (ko, require){

And now it works!

Is there a better way to do this? Or is my change just fine?

afterRender-method inside view model

While we do have the auto-executed initialize-method inside the view model of a module, this is executed before the template was successfully rendered. What I need (and I'd guess others would, too) would be an easy way to have an afterRender-method inside the view model. Relaying this event from inside the parent view model to the child model gets really tedious, especially when one has multiple modules on one page.
I tried to implement it myself, but my approach is more or less a hack (which doesn't even work right at the moment ;) )

Bubbling up an event from within a module for the parent to handle / take some action on.

Hi Ryan,

Quick question.Here's my setup again-

//parent page Html
<div data-bind="module: moduleInfo"></div>

//parent view model
this.moduleInfo=ko.observable(
{ data: "",
  disposeMethod: "dispose",
  id: "moduleId",
  initializer: "initialize",
  name: "modulevm",
  template: "moduleTmpl"
}
);

//module's view model - modulevm
//function that handles a button click event
this.ButtonClick=new function(){
//Do some work.
//How do I let parent know of this event?
}

Is there a recommended way of doing this? Any thoughts / suggestions?

Passing data from Tempate to Module

Thanks for this great helper lib, it's awesome!

Is there a way to pass data from the tempate to the initializer of the module?

for example:

cars.html

<div data-bind="template: {name: 'car', brand: 'mazda'"></div>
<div data-bind="template: {name: 'car', brand: 'honda'"></div>

car.template.html

<div data-bind="module: { name: 'car' }">
*display stuff about a car*
</div>

car.js

define(['knockout'], function(ko) {

    var module = {};

    module.initialize = function(brand) {
        *setup the module for a particular car brand*
    };

    return module;
});

Module Binding: require() for the template source should not have to wait until the module is downloaded

With the current implementation, ko.templateSources.requireTemplate.prototype.text() will be executed only after templateBinding.data observable is mutated with a non-null value, i.e. when the module is downloaded and instantiated. That is when the 'if' condition on the underlying template binding will be satisfied.

The performance of the Module binding should improve significantly if the template source is downloaded in parallel with the module source. The init() function of the "module" binding can check whether the module name points to an external template, and execute a require() call without a callback. This will initiate the download of the template source before Knockout template is executed. amdTemplateEngine will execute require() for the template source exactly as it does today. Require.js will be smart enough to realize that download of the template source has already started (or perhaps is already done), and invoke the callback much sooner than it would have done if the download had to be initiated then.

Integration with other text-based templating engines

First off, just like to comment that I love the simplicity and the integration point with requirejs of this knockout plugin.

I noticed that since this is overriding the template binding, it would effectively block any other knockout templating engines from working - is that true (I'm no expert on ko internals)?

Do you see supporting other text-based templating engines as something pluggable and within the scope of knockout-amd-helpers? The utilization of require's text plugin is a great way to integrate with other templates engines.

Thanks

renderTemplateSource produces an empty string

renderTemplateSource in Knockout calls the text function from the templateSource and then uses it right away to create HTML Elements.

nativeTemplateEngine.js#L15

This doesn't work with with the text function in Knockout AMD Helpers as the text is loaded asynchronously.

amdTemplateEngine.js#L24

And while that request is being made the current template is returned, which is an empty string.

amdTemplateEngine.js#L37

I tried to figure out if this could be corrected, but the function chain is synchronous.

Is there a work around for this?

I assume pre-loading the templates would work as long as I gave them enough time to return, but I'd rather have the lazy loading.

Another option might be creating renderTemplateSourceAsync. I don't think that would work with existing ko code, but it'd work for my use case.

Accessing module binding information from within a module

Hi Ryan,
Quick question - I've been using dynamic module binding (using 'ko amd helpers') in my project and I've run into a situation where I think I need to be able to access module binding information from within a module's view model.

For example -

//Html
<div data-bind="module: moduleInfo"></div>

//parent view model
this.moduleInfo=ko.observable(
{ data: "",
  disposeMethod: "dispose",
  id: "moduleId",
  initializer: "initialize",
  name: "modulevm",
  template: "moduleTmpl"
}
);

//module's view model - modulevm
//have a need to access the 'id' of the module from within 'dispose'.

As you can see, I've attributes other than the standard ones needed for the amd plugin. I am looking to get the 'id' value from within the 'dispose' method of the module. Is there a way to accomplish this.

Dynamically assigning a template path in renderTemplate()

I wonder if there is a way to do something like this:
ko.renderTemplate( 'tplName', myViewModel, { path: '/app/templates/custom/XYZ' }, this, "replaceNode" );
I mean, if there is a way to re-write ko.amdTemplateEngine.defaultPath on the fly?

Ready to use KO module using requirejs "map"

Hello,

I was reading that article: http://blog.scottlogic.com/2014/02/28/developing-large-scale-knockoutjs-applications.html and I thought it made sense to use the map functionality of requirejs to have a fully loaded knockout module (i.e. with amd-helpers, customBindingHandlers and so on).

// require-config.js
var require = {
    paths: {
        'knockout-raw': 'lib/knockout.debug',
        'knockout-amd-helpers': 'lib/knockout-amd-helpers/build/knockout-amd-helpers',
    },
    shim: {
        'knockout-amd-helpers': {
            deps: ['knockout-raw'] // 17/05/2016 - Helper: ['knockout'] only is not working.
        },
    map: {
        '*': {
            'jquery': 'jquery-private',
            'knockout': 'knockout-extended'
        },
        'jquery-private': {
            'jquery': 'jquery'
        }
    }
};

// knockout-extended.js
define([
    'knockout-raw',
    'knockout-amd-helpers',
    'customBindingHandlers/foo',
    'customBindingHandlers/bar'
], function (ko) {
    ko.bindingHandlers.module.templateProperty = 'template';
    return ko;
});
// build.js
({
    name: '.path/to/almond',
    include: ['config', 'main'],
    mainConfigFile: './require-config.js',
    optimize: 'none',
    out: 'main-built.js'
})

However, knockout-amd-helpers already has a

// knockout-amd-helpers 0.7.4 | (c) 2015 Ryan Niemeyer |  http://www.opensource.org/licenses/mit-license
define(["knockout"], function(ko) {

and the output of r.js gives this for knockout base module:

        // [1] AMD anonymous module
        define('knockout-raw',['exports', 'require'], factory);

it results in an error at this line: https://github.com/rniemeyer/knockout-amd-helpers/blob/master/build/knockout-amd-helpers.js#L6

Would the only solution be to edit the code of knockout-amd-helper and set it to knockout-raw (which is working)?

loading from html / js

hi,
when i include

<div data-bind="template: {name: 'home' }"></div>

it all works as expected!

But when I want to call

ko.renderTemplate('home',{}) 

directly from javascript the following is happening:

The call requires the 'home' template and starts downloading it.
But: the template is rendered before it is downloaded?!
So I end up with empty div tags.

But when I do something like this (with timeout) it starts working
(note: I am using knockback, instead of knockout here. but it also calls the ko.renderTemplate after doing some vodoo).

console.log(kb.renderTemplate('home', { }));
setTimeout(function(){
    console.log(kb.renderTemplate('home', {}));
}, 150)

the second console.log gives me the expected output. First one is empty div. (Timeout needs to be large enough here)

To further my problem description:

...I am trying to implement the knockback in a requirejs module
In the below snippet it needs to load the 'page' - template
(sample from here: https://github.com/kmalakoff/knockback-navigators)

router.route('', null, page_navigator.dispatcher(function(){
  page_navigator.loadPage( kb.renderTemplate('page', new PageViewModel(pages.get('main'))) );
}));

currently I am unable to make the kb.renderTemplate('page',{}) call work properly - because i don't want to include all my templates via script-tags.

Not sure if this is an issue of this plugin...
But if anybody can point me towards a solution, pls comment ;)

knockout-amd-helpers need new typings

knockout 3.5.0 now ships with new knockout typings inside knockout npm package. This is no longer compatible with "@types/knockout-amd-helpers".
Can either "@types/knockout-amd-helpers" be updated or can you add new typings that would be inside the npm package as well?

Working with Typescript AMD Modules

I would love to see an example of this. It all loads, but I find that the default template engine is not being replaced when it gets to my viewmodel in another module, and tries to use the template binding to render the view. A working example would be great.

not working with webpack very well.

I am getting the below error every time I build with webpack:

Critical dependency: the request of a dependency is an expression

Any ideas how this can be fixed?

SCRIPT5007: Function.prototype.apply error in IE8

When writing data-bind="module: { name: 'Modulename', data: initData }" IE8 throws a SCRIPT5007 error.This happens when optional args are undefined.

fixed it with:

Wrapper = function() {
if(args === undefined){
args = [];
};
return Constructor.apply(this, args);
};

on line 11 of knockout-amd-helpers.js

afterRender still fires only for the first template when using external templates with requirejs/text

Hi!
I'm still encountering the problem that the afterRender callback gets called only once for the first template when using the template binding and loading external templates with requirejs/text.

It works when I use script tags as templates but when I load external templates using the AMD loader's text plugin the afterRender gets called only once. It also only happens when there are at least two different templates (when both templates in the following example have the same name the problem does not occur).

Here is an example of my (main) script code:

require.config({
    baseUrl: 'Scripts',
    paths: {
        knockout: '3rdparty/knockout-3.1.0.debug',
        koamdhelper: '3rdparty/knockout-amd-helpers',
        text: '3rdparty/text'
    }
});

require(['knockout', 'koamdhelper', 'text'], function (ko) {

    ko.amdTemplateEngine.defaultPath = "templates";
    ko.amdTemplateEngine.defaultSuffix = ".tmpl.html";
    ko.amdTemplateEngine.defaultRequireTextPluginName = "text";

var viewModel = {
    elements: ko.observableArray(),
    getTemplateName: function (item) {
        return item.template();
    },
    afterRender: function (domElement, item) {
        console.log(item);
    }
};

viewModel.elements.push({
    name: ko.observable("firstElement"),
    template: ko.observable("textboxTemplate")
});

viewModel.elements.push({
    name: ko.observable("secondElement"),
    template: ko.observable("textareaTemplate")
});

ko.applyBindings(viewModel);
});

and the html:
<div data-bind="template: { foreach: elements, name: getTemplateName, afterRender: afterRender }"></div>

I have two templates "textareaTemplate.tmpl.html" and "textboxTemplate.tmpl.html".

I also found out that the callback for name (getTemplateName in the above example) gets called twice for each element (four times instead of two times in the above example).

API available in bonded module needs to be called after template finishes rendering

Hi, We are using this module binding extensively in our project. But we are facing a seems to be simple issue.

In module binding we are passing a function (view model) as the module to bind with. This module contains some API that needs to be triggered when provided template finishes rendering.

I did say "afterRender" configuration that we can pass, but then it expects fucntion provided as afterRender on parent module (on whose template this module binding lies). But our requirement to execute API of module (that is passed in the binding) to execute after template renders.

e.g

Here TestViewModel contains that API that needs to be executed after TestView finishes rendering.

Let me know if more info needed.

Thanks in advance

benefitting from the requirejs-optimizer

Hi,
I am bundling all my JavaScript files into a single file via the require.js optimizer. Unfortunately, while I was successful in including html-templates, those were not used by this library and were still being downloaded one by one.

Is there a specific setting or a guideline/naming one has to follow for that to function?

Regards,
nmehlei

Provide simple way to load external templates from within custom bindings

In the current implementation of these helpers, composing the logic that loads external templates is a bit kludgy as I have to utilize the module binding handler in my custom binding in order to trigger the external loading. This comes with the cost of also defining a new AMD module in order to perform the simple template loading.

For example:

define([
    'knockout',
    'jquery',

    'vendor/js/blur'
], function (
    ko,
    $
) {
    'use strict';

    ko.bindingHandlers.templateWrapper = {
        init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
            var $element = $(element);
            var bindingData = ko.unwrap(valueAccessor());

            // module binding requires an AMD module in order to load the template
            define('components/taco/TacoBindingModule', [], function () {
                return {
                    foo: ko.observable("taco")
                };
            });

            var accessor = function () {
                return {
                    name: 'components/taco/TacoBindingModule',
                    template: 'components/taco/tacoTemplate'
                };
            };

            ko.bindingHandlers.module.init(modalContainerNode, accessor, allBindings, viewModel, bindingContext);
        }
    };
});

it would be nice if I did not have to create a whole new AMD module to just load the template. Something like the following would make this much easier to do w/in a custom binding:

define([
    'knockout',
    'jquery',

    'vendor/js/blur'
], function (
    ko,
    $
) {
    'use strict';

    ko.bindingHandlers.templateWrapper = {
        init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
            var $element = $(element);
            var bindingData = ko.unwrap(valueAccessor());

            var accessor = function () {
                return {
                    name: 'components/taco/TacoBindingModule',
                    template: 'components/taco/tacoTemplate',
                    date { foo: ko.observable("taco") }
                };
            };

            ko.bindingHandlers.module.init(modalContainerNode, accessor, allBindings, viewModel, bindingContext);
        }
    };
});

This way, it saves consumers the extra step of being required to define an AMD module. It would also help with making the template loading aspect of this library more composable with custom bindings as well as allowing custom bindings to create internal view models for their templates.

Getting the reference to the bound element

I am forking this so that I can get the bound element from within the constructor referenced via the 'module' data-binding. Are you interested in receiving this contribution?

module and template loaders don't work with require.config moduleIDs packages

It looks like the problem is related to

line:117

require([addTrailingSlash(ko.bindingHandlers.module.baseDir) + moduleName], function(mod) {

& line:177

templatePath = engine.defaultRequireTextPluginName + "!" + addTrailingSlash(engine.defaultPath) + this.key + engine.defaultSuffix;
require([templatePath], this.template);

Where if the default path were not always added, the a [require.js package][http://requirejs.org/docs/api.html#packages] would load as defined by the projects require.config, however using this pattern limits the options of integrating with the projects config, and as such my modules and templates are not loading correctly.

I'm considering a fix that would add a ko.amdTemplateEngine.defaultConfig = true that would basically use the projects default moduleID resolution methods, instead of prepending the default path.

Thinking out-loud hear, maybe each bind/module.options should allow for a different default on moduleID search path, some might want to use one pattern for some modules, and another pattern for other modules.

thoughts?

PS. I don't need this feature now but some projects will have multiple require configs, under unique require.contexts, which for example could generate two require references, ex. requireOne() for require context one, and requireTwo() for the second require context

[Request] Option to require additional files by convention

Would be nice to be able to configure additional requires by convention. So "module: 'MyModule'" could additionally require "css!styles/MyModule" or "less!styles/MyModule", here used with the require-css or require-less modules.
That way styles for views can be loaded automatically without creating a dependency in the viewmodel.

At it simplest it could just be a hook every time template or module is used.

Module 'initializer' fires twice

I'm currently working on a SPA project with multiple views (each having its own view model).The application is setup with hash based navigation using SammyJS. Based on the hash requested, a view /view model combination gets activated (dynamic module binding using 'ko amd helpers').

I'm noticing inconsistencies where the 'initializer' method defined on a module gets fired twice when the browser back button is used. I'm also seeing the same behavior when debugging the project in Visual Studio 2012.

Any thoughts on this? Has this been reported before?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.