Knockout nested bindings - javascript

I'm having a problem using knockout and a form and getting bindings to apply without throwing errors.
I would like to split the logic for the form into several view models but I'm getting errors with bindings in bars and foos not being found when I attempt to bind foobar
I've tried to display this in the example below.
Is there a way to achieve the desired behaviour? Is there a way to say combine all the bindings from the three view models then assign them to foobar?
bars_observable is a ko.observable created in the contructor of barViewModel.
<div id="foobar">
<form data-bind="with: newFooBar, submit: submitFooBar">
<section id="bars">
<div data-bind="text: bars_observable"></div>
</section>
<section id="foos">
foo stuff
</section>
</form>
</div>
<script type="text/javascript">
$(function () {
var foobarViewModel, fooViewModel, barViewModel;
foobarViewModel = new ViewModels.FoobarViewModel({
fooViewModel: new ViewModels.FooViewModel({}),
barViewModel: new ViewModels.BarViewModel({})
});
ko.applyBindings(foobarViewModel, document.getElementById("foobar"));
});
</script>
The error would be
"Uncaught Error: Unable to parse bindings. Message: ReferenceError: bars_observable is not defined;"

I would recommend to put fooViewModel and barViewModel objects into FoobarViewModel. In this case you have to call ko.applyBindings only once.
<div id="foobar">
<form data-bind="with: newFooBar, submit: submitFooBar">
<section id="bars" data-bind="with: barViewModel">
<div data-bind="text: bars_observable"></div>
</section>
<section id="foos" data-bind="with: forViewModel">
foo stuff
</section>
</form>
</div>
<script type="text/javascript">
$(function () {
var foobarViewModel = new ViewModels.FoobarViewModel({});
ko.applyBindings(foobarViewModel, document.getElementById("foobar"));
});
function ViewModels.FoobarViewModel() {
var self = this;
self.fooViewModel = new ViewModels.FooViewModel({});
self.barViewModel = new ViewModels.BarViewModel({});
...
}
</script>

If you are writing a modular system, creating a top level view model may not be possible. In knockout 2.0 you can tell knockout to stop parsing bindings at a certain point. Then you can call applyBindings again for the container. Here is an article which explains how to do it.
http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html

Related

eval() alternative in Meteor to a variable name declaring a Mongo collection

This Meteor code has let Vehicles= new Mongo.Collection('vehicles'); and a html <div id='Vehicles', I like use the <get obj in var named Veh>.find({}).count() the below code works fine but I read to avoid the use of eval(). Reading up on alternatives could not satisfy me to formalize a solution. Please help with a solution or show how to write this in a non eval() way. Thanks
Update: It needs to run on the server "nodeJs" and not in the browser
//cliant/main.js
$('input').on('blur', function(e) {
let collectionVar= $(this).parents('div')[2].id // Vehicles is the same as the collection variable.
console.log(eval(collectionVar).find({}).count()) // works but need an alternative to eval()
})
<template name="Vehicles">
<div id="Vehicles" class="subject-container" >
<div class="section-head">
<div class="control">
<input id="plate" type="text" size="6" placeholder="plate" value={{vehicle.plate}}>
</div>
</div>
</div>
</template>
You should use Template events instead of jQuery event listeners to get the most out of your data during UI events.
You can then easily attach data-* attributes to the component to avoid any parent fiddling:
<template name="Vehicles">
<div id="Vehicles" class="subject-container" >
<div class="section-head">
<div class="control">
<input id="plate"
data-collection="Vehicles" <!-- ref the collection -->
type="text"
size="6"
placeholder="plate"
value={{vehicle.plate}}>
</div>
</div>
</div>
</template>
You can then use either a global Object, that references collecitons by name or dburles:mongo-collection-instances to get the collection by name (I would favour the second, because it does not further pollute the global namespace):
Template.Vehicles.events({
'blur #plate' (event, templateInstance) {
const collectionName = templateInstance.$(event.currentTarget).data('collection')
// wihtout jQuery: = event.currentTarget.getAttribute('data-collection')
const collection = Mongo.Collection.get(collectionName)
}
})
References
https://atmospherejs.com/dburles/mongo-collection-instances
http://blazejs.org/api/templates.html#Event-Maps

Angular JS view hasn't been updated properly

I have found an issue in AngularJS which relates to wrong update of view. It occurs from time to time. The problem is when model gets a new value, view is not updated by new model value, but old value is appended by new model value.
While troubleshooting I checked that model contains a correct value.
Here is a view.
<div class="container">
<div ng-repeat="p in point" id="{{'point-' + p.Id}}" class="{{p.BackgroundClass}}">
<div class="point-number">{{p.Id}}</div>
<div class="{{p.ImageClass}}"></div>
<div class="point-amount">{{p.Amount}}</div>
<div class="point-quantity">{{p.Quantity}}</div>
</div>
</div>
Controller code which contains SignalR events processing:
wetApiHubProxy.on('updatePointState', function (pointId, backgroundClassProp, imageClassProp) {
pointsService.getPointById(pointId).then(function (point) {
point.BackgroundClass = backgroundClassProp;
console.log('imageClassProp ' + point.ImageClass);
point.ImageClass = imageClassProp;
});
});
p.ImageClass is changing quite often. Changes/updates of view work in a correct way until sometimes occurs concatenation of old and new value.
Old p.ImageClass value is "point-state-configure".
New p.ImageClass value is "pump-state-off".
As a wrong result I have, where ImageClass contains concatenated values:
<div ng-repeat="p in points" id="point-4" class="point point-off" role="button" tabindex="0" style="">
<div class="point-number ng-binding">4</div>
<div class="point-state-configure pump-state-off" style=""></div>
<div class="point-amount ng-binding">926.93</div>
<div class="point-quantity ng-binding">417.35 L</div>
</div>
I have tried to call $scope.$apply() and $evalAsync, but that was hopeless. The strangest thing that issue occurs spontaneously. The only constant condition it's when $rootscope contains bigger amount of child scopes. Can anyone tell what place to dig and how to get rid of this problem?
class attribute is not intended to be used this way. You should use the ng-class directive instead.
I've created an example for you: https://jsfiddle.net/coldcue/o7q6gfs4/
JavaScript
angular.module('testApp', [])
.controller("TestController", function($scope) {
// Initialize the value
$scope.state = "state-blue";
// Change class on click
$scope.click = function() {
$scope.state = ($scope.state === "state-blue") ? "state-red" : "state-blue";
}
});
HTML
<div ng-controller="TestController">
<div ng-class="state">
Some label
</div>
<input type="button" ng-click="click()" value="Click me">
</div>
But there are many more ways to use ng-class, read more here: https://docs.angularjs.org/api/ng/directive/ngClass

Multiple Knockout binding with function and variable

Multiple Knockout binding.
I want to use just one apply binding instead of two apply binding. One is variable other is function. i am using requireJS also.
HTML:
<button id= "Hand" name="Hand"
data-bind="click: Handler2">
</button>
KnockoutJS
function (ko, $)
{
function DM1ViewModel() {
var self = this;
self.bId = ko.observable('TEST456');
}
$('#hide').hide();
var DMD2 = {
Handler2: function() {
window.location='http:www.google.com';
}
};
ko.applyBindings(new DM1ViewModel(), document.getElementById('Container'));
ko.applyBindings(DMD2);
});
As it stands, there's really no reason to applyBindings on your DMD2 objects since there aren't any observables there.
However, to answer your question more generally, you have two options:
Call applyBindings for DMD2 against an element that doesn't contain your Container element, and isn't already contained within your Container element.
Javascript:
// DM1ViewModel is the same
var DMD2ViewModel = function() {
this.Handler2 = function() {
window.location='http:www.google.com';
};
}
ko.applyBindings(new DM1ViewModel(), document.getElementById('DM1Container'));
ko.applyBindings(new DMD2ViewModel(), document.getElementById('DMD2Container'));
HTML
<div id="Container">
<div id="DM1Container">
<h2 data-bind="text: bId"></h2>
</div>
<div id="DMD2Container">
<h2 data-bind="click: Handler2">Click me</h2>
</div>
</div>
Make one parent view model that has each of your existing view models as observables and the use the with binding
Javascript:
var PageViewModel = function(){
this.dm1 = ko.observable(new DM1ViewModel());
this.dm2 = ko.observable(DMD2); // currently isn't a function, so can't call new
}
ko.applyBindings(new PageViewModel(), document.getElementById('Container'));
In your html:
<div id="Container">
<div data-bind="with: dm1">
<h2 data-bind="text: bId"></h2>
</div>
<div data-bind="with: dm2">
<h2 data-bind="click: Handler2">Click me</h2>
</div>
</div>

document.getElementById(#mapid) returns null

referring to this question here, context is not defined javascript
I am just throwing out another question in regards of the error where document.getElementById is returning a null even though the template is already compiled.
A brief background, I am using handlebar template, jquery and I want a googlemap to appear in 1 of the handlebar template.
my html:
<script id="employee-tpl" type="text/x-handlebars-template">
<div class='header'>Back<h1>Gloop</h1></div>
<div class='details'>
<img class='employee-image' src='img/{{firstName}}_{{lastName}}.jpg' />
<h1>{{name}}</h1>
<h2>{{address}}</h2>
<h2>{{phone}}</h2>
<span class="location"></span>
<ul>
<li><div id="map-canvas"> </div></li>
</ul>
</div>
</script>
and my js
this.render = function(){
this.el.html(EmployeeView.template(employee));
console.log(document.getElementById("map-canvas"));
};
this.initialize = function(){
this.el=$('<div/>');
};
this.initialize();
}
EmployeeView.template = Handlebars.compile($("#employee-tpl").html());
notice that the call to console will log a null value and this js is called at the bottom of my html file
<script src="lib/iscroll.js"></script>
<script src="lib/handlebars.js"></script>
<script src="lib/jquery-1.8.2.min.js"></script>
<script src="js/storage/cafe-memory-store.js"></script>
<script src="js/EmployeeView.js"></script>
<script src="js/main.js"></script>
</body>
</html>
my understanding it that the my compilation will render the div which then should be able to be seen but this is not the case. Anyone knows why?
Is that fiddle all the code you have? If I add this to the JS it works.. but it's hard to see what you're after.
var source = $("#employee-tpl").html();
var template = Handlebars.compile(source);
var data = { //your data here }
$('body').append(template(data));

Popup using knockout js

i'm migrating one of my older jquery plugins from DOM jungle to this fancy mvvm framework knockout.
Which technique would i use to properly display a popup container? I ahve to populate it 'by call' since i get a json feed every time.
I tried an approach using the with binding, but it still attempts to populate the partial at its first runtime.
<!-- ko with: daySubmitFormViewModel -->
<div class="ec-consulation-lightbox">
<form id="cForm" class="form-container">
// Some bindings here.
</form>
</div>
<!-- /ko with: -->
It can be done without custom binding as well. Example is below
<div class="modalWindowBackground" data-bind="visible: popupDialog" >
<div class="modalWindow" data-bind="with:popupDialog">
<div class="content">
<h2 data-bind="text: title"></h2>
<p>
<span data-bind="text: message"></span>
</p>
<div class="buttonSpace">
<input type="button" class="closeButton" data-bind="value: closeButtonText, click: $root.hidePopupDialog" />
</div>
</div>
</div>
</div>
Viewmodel code:
self.showAlert = function (title, message, closeButtonText) {
self.popupDialog({ title: title, message: message, closeButtonText: closeButtonText });
};
self.hidePopupDialog = function () {
self.popupDialog(null);
};
//Code which opens a popup
self.remove = function () {
.... some code ...
if (someCondition) {
self.showAlert('SomeTitle', 'Message', 'OK');
return;
}
.... some code ...
};
Create a custom binding, have its open / close function trigger on a observable.
I've done a custom binding for jQuery Dialog that uses this approuch in combination with KO
templates.
<div id="dialog" data-bind="dialog: { autoOpen: false, modal: true, title: dialogTitle }, template: { name: 'dialog-template', data: dialogItem, 'if': dialogItem }, openDialog: dialogItem"></div>
You can find my binding here along with some others
https://github.com/AndersMalmgren/Knockout.Bindings
Live demo http://jsfiddle.net/H8xWY/102/
https://github.com/One-com/knockout-popupTemplate
That pretty much does what you ask for. It's deeply configurable, and under steady development (we use it in our web applications ourselves).
Disclaimer: I'm a One.com developer. I am also the person who originated the above mentioned lib.

Categories