This is my code
Viewmodel 1
function AppViewModel() {
var self = this;
self.boardtext = ko.observable();
self.board = ko.observableArray([
{ boardname: 'Board' },
{ boardname: 'Board' },
{ boardname: 'Board' }
]);
self.addboard = function () {
self.board.push({ boardname: self.boardtext() });
// initialize ListModal to zero
};
self.removeboard = function () {
self.board.remove(this);
}
}
Viewmodel 2
var initialData = [];
var ListModal = function (lists) {
var self = this;
self.cardtext = ko.observable();
self.lists = ko.observableArray(ko.utils.arrayMap(lists, function (list) {
return { listname: list.listname, cardlists: ko.observableArray(list.cardlists), showRenderTimes: ko.observable(false) };
}));
};
ko.applyBindings(new AppViewModel(), document.getElementById("container1"));
ko.applyBindings(new ListModal(initialData), document.getElementById("container2"));
As soon as i press addboard how can i set my ListModal to zero?
If you want to re-initialize listModal, try to wrap those 2 viewmodels in 1 viewmodel so that they can relate to each other. And then you can do the following:
var initialData = [];
var ListModal = function (lists) {
var self = this;
self.cardtext = ko.observable();
self.lists = ko.observableArray(ko.utils.arrayMap(lists, function (list) {
return { listname: list.listname, cardlists: ko.observableArray(list.cardlists), showRenderTimes: ko.observable(false) };
}));
}
function AppViewModel(parent) {
var self = this;
// this will keep the object of ViewModel
self.parentObject = parent;
self.boardtext = ko.observable();
self.board = ko.observableArray([
{ boardname: 'Board' },
{ boardname: 'Board' },
{ boardname: 'Board' }
]);
self.addboard = function () {
self.board.push({ boardname: self.boardtext() });
// re-initialize listModal
self.parentObject.listModal(new ListModal(initialData));
};
self.removeboard = function () {
self.board.remove(this);
};
}
function ViewModel() {
var self = this;
self.appViewModel = ko.observable(new AppViewModel(self));
self.listModal = ko.observable(new ListModal(initialData));
}
// provide another div which wrap container 1 and 2 together
ko.applyBindings(new ViewModel(), document.getElementById("container1And2"));
Related
I have prepared a basic fiddle of what I have here: http://jsfiddle.net/s103eqdc/
I have a function called relayButton, which loads and prepares initial data for view:
function relayButton(id, name, state, onChange) {
var self = this;
self.id = ko.observable(id);
self.name = ko.observable(name);
self.state = ko.observable(state);
self.state.subscribe(function(newValue) {
onChange(self, newValue);
});
}
But, how can I change the architecture of this simple code, so that, If there is a json data periodically loaded from server, it imidietly updates the proper relayId in the loop with checked or uncheked state?
You just need something to process the data when it comes from the backend and matches your relays id and updates the value.
I would do something like this
var app = window.app || {};
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function dataService() {
function refreshRelayData() {
return delay(200).then(function() {
return [{
id: '1',
name: 'relay1',
state: Math.round(Math.random())
},
{
id: '2',
name: 'relay2',
state: Math.round(Math.random())
},
{
id: '3',
name: 'relay3',
state: Math.round(Math.random())
}
];
});
}
return {
refreshRelayData:refreshRelayData
};
};
app.delay = delay;
app.dataService = dataService
function relayButton(id, name, state, onChange) {
var self = this;
self.id = ko.observable(id);
self.name = ko.observable(name);
self.state = ko.observable(state);
self.state.subscribe(function(newValue) {
onChange(self, newValue);
});
}
function ViewModel() {
var self = this;
self.availableRelays = ko.observableArray([]);
self.activeRelays = ko.computed(function() {
return self.availableRelays().filter(function(relay) {
return relay.state();
});
});
self.onRelayStateChange = function(item, newValue) {
console.log("State change event: " + item.name() + " (" + newValue + ")");
};
self.processData = function(data) {
data.forEach(function(item) {
self.availableRelays()
.filter(r => r.id() == item.id)
.forEach(r => r.state(item.state))
});
}
self.refreshData = function() {
app.dataService().refreshRelayData()
.then(data => self.processData(data));
}
self.init = function() {
self.availableRelays([
new relayButton(1, "relay1", 1, self.onRelayStateChange),
new relayButton(2, "relay2", 0, self.onRelayStateChange),
new relayButton(3, "relay3", 0, self.onRelayStateChange)
]);
};
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
viewModel.init();
setTimeout(function doSomething() {
viewModel.refreshData()
setTimeout(doSomething, 1000);
}, 1000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="foreach: $root.availableRelays">
<div class="switchBox">
<div class="switchName"><strong data-bind="text: ' ' + name()"></strong></div>
<div class="switchSlider">
<label class="relaySwitch">
<input class="relaySwitch-input" type="checkbox" data-bind="checked: state">
<span class="relaySwitch-label" data-on="On" data-off="Off"></span>
<span class="relaySwitch-handle"></span>
</label>
</div>
</div>
</div>
I see the basic example on github but I can't get it to work with my code. I should add that I'm using durandal.
How do I get the bindings to work? Am I doing anything wrong?
Input.js
define(['knockout'], function (ko) {
var ctor = function (value) {
//Properties
this.value = ko.observable(value);
this.placeholder = 'Input';
//Methods
this.getBindings = function () {
var bindings = {};
bindings.Input = {
value: this.value,
attr: {
placeholder: this.placholder,
},
};
bindings.Test = {
text: this.value,
};
return bindings;
};
};
return ctor;
});
Form.js
define(['knockout', 'Input'], function (ko, Input) {
var ctor = function (inputs) {
//Properties
this.inputs = ko.observableArray(inputs);
//Methods
this.getBindings = function () {
var bindings = {};
bindings.Inputs = {
foreach: this.inputs,
Item: function (context, classes) {
return context.$data.getBindings();
},
};
return bindings;
};
};
return ctor;
});
Module.js
define(['knockout', 'Input', 'Form'], function (ko, Input, Form) {
var ctor = function () { };
ctor.prototype.activate = function () {
var data = [
new Input(123),
new Input("Chris"),
new Input(true)
];
this.form = new Form(data);
};
ctor.prototype.binding = function () {
var bindings = this.form.getBindings();
ko.bindingProvider.instance.registerBindings(bindings);
};
return ctor;
});
Module.html This does not work.
<div id="Module">
<div data-class="Inputs">
<div>
<input data-class="Inputs.Item.Input" />
<span data-class="Inputs.Item.Test"></span>
</div>
</div>
</div>
Module.html This does work but I'm not using classBindingProvider for the foreach.
<div id="Module">
<div data-class="Inputs">
<div>
<input data-bind="value: value, attr: { placeholder: placeholder }" />
<span data-bind="text: value"></span>
</div>
</div>
</div>
There's no error message but the binding never happens. I just get 3 empty input fields.
I figured it out. I'll post the code that works.
I changed two things. First, I added <div data-class="Inputs.Item"> and then referenced the properties relative to that location (Input and Test). Second, I register the bindings immediately inside the getBindings functions, which will now turn them into initBindings.
Input.js
define(['knockout'], function (ko) {
var ctor = function (value) {
//Properties
this.value = ko.observable(value);
this.placeholder = 'Input';
//Methods
this.initBindings = function () { //FIX: getBindings => initBindings
var bindings = {};
bindings.Input = {
value: this.value,
attr: {
placeholder: this.placholder,
},
};
bindings.Test = {
text: this.value,
};
ko.bindingProvider.instance.registerBindings(bindings); //FIX: register instead of return
};
};
return ctor;
});
Form.js
define(['knockout', 'Input'], function (ko, Input) {
var ctor = function (inputs) {
//Properties
this.inputs = ko.observableArray(inputs);
//Methods
this.initBindings = function () { //FIX: getBindings => initBindings
var bindings = {};
bindings.Inputs = {
foreach: this.inputs,
Item: function (context, classes) {
context.$data.initBindings(); //FIX: Call the init.
},
};
ko.bindingProvider.instance.registerBindings(bindings); //FIX: register instead of return
};
};
return ctor;
});
Module.js
define(['knockout', 'Input', 'Form'], function (ko, Input, Form) {
var ctor = function () { };
ctor.prototype.activate = function () {
var data = [
new Input(123),
new Input("Chris"),
new Input(true)
];
this.form = new Form(data);
};
ctor.prototype.binding = function () {
this.form.initBindings(); //FIX: Call the init.
};
return ctor;
});
Module.html
<div id="Module">
<div data-class="Inputs">
<div data-class="Inputs.Item"> //FIX: no binding => Inputs.Item
<input data-class="Input" /> //FIX: Inputs.Item.Input => Input
<span data-class="Test"> //Fix: Inputs.Item.Test => Test
</span>
</div>
</div>
</div>
I'm new to Backbone and am currently trying to get the Save() method working on my individual models. I have everything else working at the moment, but when I call Save() on an individual item it tells me that the method doesn't exist. Any ideas? Thanks in advance.
Code:
var Root = this;
//MODELS
var Option = Backbone.Model.extend({});
var BooleanOption = Option.extend({
initialize: function () {
this.constructor.__super__.initialize.apply(this, arguments);
if (this.get("ValueAsString") === "Y") { this.set("IsChecked", true); };
},
IsChecked: false
});
var SelectOption = Option.extend({
initialize: function () {
this.constructor.__super__.initialize.apply(this, arguments);
this.set("Options", this.get("ValidationValue").split(","));
},
Options: []
});
var TextOption = Option.extend({
initialize: function () {
this.constructor.__super__.initialize.apply(this, arguments);
this.set("MaxLength", Number(this.get("ValidationValue").replace("x", "")));
},
MaxLength: null
});
//main collection model
var OptionsCollection = Backbone.Collection.extend({
model: function (attr, options) {
switch (attr.ValidationType) {
case "B":
return new BooleanOption(attr, options);
break;
case "O":
return new SelectOption(attr, options);
break;
case "C":
return new TextOption(attr, options);
break;
default:
return new Option(attr, options);
break;
}
},
urlBase: "http://localhost:40217/Shared/Options.svc/",
url: function () {
return this.urlBase + Root.getParam("ModuleID") + "?true";
}
});
//END MODELS
//VIEWS
var OptionView = Backbone.View.extend({
render: function (eventName) {
}
})
var BooleanOptionView = OptionView.extend({
initialize: function () {
this.listenTo(this.model, "change", this.render);
},
render: function (eventName) {
$("#content").append(this.el);
$(this.el).html(_.template($('#boolean-option-template').html(), this.model));
return this;
},
events: {
"change .chkBox": "test"
},
test: function () {
alert("valueChanged");
}
});
var SelectOptionView = OptionView.extend({
initialize: function () {
this.listenTo(this.model, "change", this.render);
},
render: function (eventName) {
$("#content").append(this.el);
$(this.el).html(_.template($('#select-option-template').html(), this.model));
return this;
},
events: {
"change .selectOption": "test"
},
test: function () {
alert("valueChanged");
}
});
var TextOptionView = OptionView.extend({
initialize: function () {
this.listenTo(this.model, "change", this.render);
},
render: function (eventName) {
$("#content").append(this.el);
$(this.el).html(_.template($('#text-option-template').html(), this.model));
return this;
},
events: {
"change .textOption": "test"
},
test: function () {
alert("valueChanged");
}
});
var MainView = Backbone.View.extend({
render: function (eventName) {
$("#content").append(this.el);
$(this.el).html(_.template($('#main-template').html(), this.model));
_.each(this.model.models, function (opt) {
if (opt.get("ValidationType") === "B") {
new BooleanOptionView({ model: opt }).render();
}
else if (opt.get("ValidationType") === "C") {
new TextOptionView({ model: opt }).render();
}
else if (opt.get("ValidationType") === "O") {
new SelectOptionView({ model: opt }).render();
}
}, this);
return this;
},
events: {
"click .saveBtn": "saveOptions"
},
saveOptions: function () {
_.each(this.model.models, function (mod) {
mod.Save(mod.attributes);
})
}
});
//END VIEWS
$(document).ready(function () {
var oc = new OptionsCollection();
oc.fetch({
success: function () {
$("#content").append(new MainView({model:oc}).render().el);
}
});
});
function getParam(sname) {
var params = location.search.substr(location.search.indexOf("?") + 1);
var sval = "";
params = params.split("&");
// split param and value into individual pieces
for (var i = 0; i < params.length; i++) {
temp = params[i].split("=");
if ([temp[0]] == sname) { sval = temp[1]; }
}
return sval;
}
Javascript is case sensitive.
mod.save(mod.attributes); // Instead of mod.Save
Is there any way I can do have a Javascript class that extends an object that was created through the revealing module pattern? I tried the following code, but is there away to achieve the same thing?
sv.MergeQuestionViewModel = function () {
this = sv.QuestionDetailViewModal();
this.init($("#mergeQuestionModel"));
};
sv.QuestionDetailViewModal = function () {
var $el,
self = this,
_question = ko.observable(),
_status = new sv.Status();
var _init = function (el) {
$el = el;
$el.modal({
show: false,
backdrop: "static"
});
};
var _show = function () {
$el.modal('show');
};
var _render = function (item) {
_question(new sv.QuestionViewModel(item));
_show();
};
var _reset = function () {
_question(null);
_status.clear();
};
var _close = function () {
$el.modal('hide');
_reset();
};
return {
init: _init,
show: _show,
render: _render,
reset: _reset,
close: _close
};
};
You could use jQuery.extend to achive this behaviour.
sv.MergeQuestionViewModel = function () {
$.extend(this, sv.QuestionDetailViewModal);
this.init($("#mergeQuestionModel"));
};
sv.QuestionDetailViewModal = (function () {
var el,
_init = function($el) {
el = $el;
console.log('init', el);
},
_render = function() {
console.log('render', el);
};
return {
init : _init,
render : _render
};
}());
var view = new sv.MergeQuestionViewModel();
view.render();
Test it on http://jsfiddle.net/GEGNM/
I've a fair amount of experience with JavaScript, and for this new project I'm on (cms for blog with notions of profitability) I thought I'd step it up and write the JavaScript in an MVC fashion. I've been using a bit of backbone and underscore, but it isn't clicking mentally. any way, I've written a bit of code to handle some events/effects but it just doesn't work. If anyone could sort me out I'd really appreciate it.
// Semi Perfect grade 0 JS - Golden age
//partial View Objects | Events
var pshare_dock = {
actor: $("#share_dock"),
drag: function () {
this.actor.draggable();
}
}
pshare_dock.expand = function () {
this.actor.dblclick(function () {
$(this).toggleClass("share_close");
});
}
var pmenu = {
hover: function () {
$("ul.drop li.drop").hover(function () {
$(this).find('ul').fadeIn(1);
}, function () {
$(this).find('ul').hide();
})
},
navigate: function () {
$("a.ajx").click(function (e) {
var link;
var container = $("#content_pane");
e.preventDefault();
link = $(this).attr("href") + "#content_pane";
container.load(link);
})
}
}
var pcontent_pane = {}
var ppost = {}
var pdatabase_entry = {}
//Views
var Homepage = function () {
this.share_dock = function () {
new pshare_dock();
}
this.menu = function () {
new pmenu();
}
this.content_pane = function () {
new pcontent_pane();
}
this.posts = function () {
new ppost();
}
}
//Controller
var GoldenAgeRouter = Backbone.Router.extend({
routes: {
"!/": "defaultRoute",
"*actions": "defaultRoute"
},
defaultRoute: function (actions) {
var homeView = function () {
new Homepage();
}
}
})
$(document).ready(function () {
var Golden_age = function () {
new Homepage();
}
})
the question is essentially what all is wrong with this?
You're wrapping your instantiations in an anonymous function but not invoking them:
var Golden_age = new Homepage(); // Invoked.
var Golden_age = function(){ new Homepage(); } // Stored function, not invoked.