rniemeyer / knockout-amd-helpers Goto Github PK
View Code? Open in Web Editor NEWThis plugin is designed to be a lightweight and flexible solution to working with AMD modules in Knockout.js.
License: MIT License
This plugin is designed to be a lightweight and flexible solution to working with AMD modules in Knockout.js.
License: MIT License
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.
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?
So we could have reusable widgets!
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 ;) )
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?
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;
});
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.
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
in Knockout calls the text function from the templateSource
and then uses it right away to create HTML Elements.
This doesn't work with with the text function in Knockout AMD Helpers as the text is loaded asynchronously.
And while that request is being made the current template is returned, which is an empty string.
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.
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.
Hey,
seems like v.0.7.3 is already finished, since it's listed under releases.
Shouldn't it be available via NuGet aswell?
afterRender fires first for the blank template and then a second time after the template has rendered.
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?
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)?
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 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?
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.
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?
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
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).
When using the afterRender binding, only the afterRender function defined in the first module will get called upon switching the modules via an observable.
I created the following fiddle to quickly reconstruct the issue (console shows debug messages) eelkevdbos/afterRender
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
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
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.
Would it be possible to use the original nodes inside a module, such as is possible in knockout components?
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?
Sorry, my mistake... Seems everything works fine.
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
I am probably doing something wrong but I cannot get beforeRemove
event to be fired for template binding here just like here. I was expecting this function to be fired on this call.
Any idea?
Hi,
a NuGet package would help with a quick integration into new projects and would serve as an update notification.
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.
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?
I am trying to see if template binding works with with
but it seems not: https://github.com/tugberkugurlu/frameworkless-spa/blob/8e6b2b53005bcff670ffa1c4dd174f5a1216418b/index.html#L16
Am I doing something wrong or is this simply not possible?
great bindings BTW, thx for sharing them!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.