Binding a property to modelview function in knockout - javascript

I have:
userAccess object:
var userAccess = new (
function() {
this.userLogedIn = false;
}
);
I have modelview, binded to UI
var modelview = new (
function(){
this.itemVisible =
function(data) {
if(data.id === "ID2")
return userAccess.userLogedIn;
return true;
};
this.items = [{id:"ID1", text:"text1"}, {id:"ID2", text:"text2"}];
}
);
on UI, inside foreach binding I have:
<span data-bind="text: text, visible:$parent.itemVisible($data)"> </span>
so the visibility of the span element is binded to modelview's function.
The function determines a visibility of the current item based on its ID and value of userAccess.
Problem:
The two way binding doesn't work in this scenario. For example if I make userAccess.userLogedIn = true the element "ID2" doesn't become visible.
This is because of lack of observable, but I can not, seems to me, fit an observable in this pattern.
I know also that I can update binding manually, but would like to avoid this, if this is possible.
I have feeling that I'm missing something obvious here.
Complete source on CodePen

You should probably refactor your whole setup to use observables. Otherwise, the usage of knockout does not make much sense due to the lack of automated view updates (as you noticed).
var userAccess = new (
function() {
// It is likely that this value will change, so make it an observable!
this.userLogedIn = ko.observable(false);
}
);
// Create a "class" for the items in the list be able to encapsulate behavior /
// properties such as "is this item visible"?
var Item = function(id, text) {
var self = this;
self.id = id; // <-- will most likely never change (?) => not an observable
self.text = ko.observable(text);
// Use a "computed observable" for things that require more sophisticated logic
self.visible = ko.computed(function() {
if (self.id === "ID2") {
return userAccess.userLogedIn(); // <-- observable = () required!
} else {
return true;
}
});
};
var modelview = new (
function() {
this.items = ko.observableArray([
new Item("ID1", "text1"), new Item("ID2", "text2")
]);
}
);
and in the HTML
<span data-bind="text: text, visible: visible"> </span>
Example: http://jsfiddle.net/a89VL/

Related

Knockout computed property fires on loading

I'm starting with knockout and my computed observable seems to fire always when the viewmodel is instantiated and i don't know why.
I've reduced the problem to the absurd just for testing: the computed property just prints a message in the console and it is not binded to any element at the DOM. Here it is:
(function() {
function HomeViewModel() {
var self = this;
(...)
self.FullName = ko.computed(function () {
console.log("INSIDE");
});
(...)
};
ko.applyBindings(new HomeViewModel());
})();
How can it be avoided?
Update:
Here is the full code of the ViewModel just for your better understanding:
function HomeViewModel() {
var self = this;
self.teachers = ko.observableArray([]);
self.students = ko.observableArray([]);
self.FilterByName = ko.observable('');
self.FilterByLastName = ko.observable('');
self.FilteredTeachers = ko.observableArray([]);
self.FilteredStudents = ko.observableArray([]);
self.FilteredUsersComputed = ko.computed(function () {
var filteredTeachers = self.teachers().filter(function (user) {
return (user.name.toUpperCase().includes(self.FilterByName().toUpperCase()) &&
user.lastName.toUpperCase().includes(self.FilterByLastName().toUpperCase())
);
});
self.FilteredTeachers(filteredTeachers);
var filteredStudents = self.students().filter(function (user) {
return (user.name.toUpperCase().includes(self.FilterByName().toUpperCase()) &&
user.lastName.toUpperCase().includes(self.FilterByLastName().toUpperCase())
);
});
self.FilteredStudents(filteredStudents);
$("#LLAdminBodyMain").fadeIn();
}).extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 800 } });
self.FilteredUsersComputed.subscribe(function () {
setTimeout(function () { $("#LLAdminBodyMain").fadeOut(); }, 200);
}, null, "beforeChange");
$.getJSON("/api/User/Teacher", function (data) {
self.teachers(data);
});
$.getJSON("/api/User/Student", function (data) {
self.students(data);
});
}
ko.applyBindings(new HomeViewModel());
})();
I need it to not be executed on load because on load the self.students and self.teachers arrays are not jet populated.
NOTE: Just want to highlight that in both codes (the absurd and full), the computed property is executed on loading (or when the ViewModel is first instantiated).
There are two main mistakes in your approach.
You have a separate observable for filtered users. That's not necessary. The ko.computed will fill that role, there is no need to store the computed results anywhere. (Computeds are cached, they store their own values internally. Calling a computed repeatedly does not re-calculate its value.)
You are interacting with the DOM from your view model. This should generally be avoided as it couples the viewmodel to the view. The viewmodel should be able operate without any knowledge of how it is rendered.
Minor points / improvement suggestions:
Don't rate-limit your filter result. Rate-limit the observable that contains the filter string.
Don't call your computed properties ...Computed - that's of no concern to your view, there is no reason to point it out. For all practical purposes inside your view, computeds and observables are exactly the same thing.
If teachers and students are the same thing, i.e. user objects to be displayed in the same list, why have them in two separate lists? Would it not make more sense to have a single list in your viewmodel, so you don't need to filter twice?
Observables are functions. This means
$.getJSON("...", function (data) { someObservable(data) });
can be shortened to
$.getJSON("...", someObservable);.
Here is a better viewmodel:
function HomeViewModel() {
var self = this;
self.teachers = ko.observableArray([]);
self.students = ko.observableArray([]);
self.filterByName = ko.observable().extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 800 } });
self.filterByLastName = ko.observable().extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 800 } });
function filterUsers(userList) {
var name = self.filterByName().toUpperCase(),
lastName = self.filterByLastName().toUpperCase(),
allUsers = userList();
if (!name && !lastName) return allUsers;
return allUsers.filter(function (user) {
return (!name || user.name.toUpperCase().includes(name)) &&
(!lastName || user.lastName.toUpperCase().includes(lastName));
});
}
self.filteredTeachers = ko.computed(function () {
return filterUsers(self.teachers);
});
self.filteredStudents = ko.computed(function () {
return filterUsers(self.students);
});
self.filteredUsers = ko.computed(function () {
return self.filteredTeachers().concat(self.filteredStudents());
// maybe sort the result?
});
$.getJSON("/api/User/Teacher", self.teachers);
$.getJSON("/api/User/Student", self.students);
}
With this it does not matter anymore that the computeds are calculated immediately. You can bind your view to filteredTeachers, filteredStudents or filteredUsers and the view will always reflect the state of affairs.
When it comes to making user interface elements react to viewmodel state changes, whether the reaction is "change HTML" or "fade in/fade out" makes no difference. It's not the viewmodel's job. It is always the task of bindings.
If there is no "stock" binding that does what you want, make a new one. This one is straight from the examples in the documentation:
// Here's a custom Knockout binding that makes elements shown/hidden via jQuery's fadeIn()/fadeOut() methods
// Could be stored in a separate utility library
ko.bindingHandlers.fadeVisible = {
init: function(element, valueAccessor) {
// Initially set the element to be instantly visible/hidden depending on the value
var value = valueAccessor();
$(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable
},
update: function(element, valueAccessor) {
// Whenever the value subsequently changes, slowly fade the element in or out
var value = valueAccessor();
ko.unwrap(value) ? $(element).fadeIn() : $(element).fadeOut();
}
};
It fades in/out the bound element depending the bound value. It's practical that the empty array [] evaluates to false, so you can do this in the view:
<div data-bind="fadeVisible: filteredUsers">
<!-- show filteredUsers... --->
</div>
A custom binding that fades an element before and after the bound value changes would look like follows.
We subscribe to value during the binding's init phase.
There is no update phase in the binding, everything it needs to do is accomplished by the subscriptions.
When the DOM element goes away (for example, because a higher-up if or foreach binding triggers) then our binding cleans up the subscriptions, too.
Let's call it fadeDuringChange:
ko.bindingHandlers.fadeDuringChange = {
init: function(element, valueAccessor) {
var value = valueAccessor();
var beforeChangeSubscription = value.subscribe(function () {
$(element).delay(200).fadeOut();
}, null, "beforeChange");
var afterChangeSubscription = value.subscribe(function () {
$(element).fadeIn();
});
// dispose of subscriptions when the DOM node goes away
// see http://knockoutjs.com/documentation/custom-bindings-disposal.html
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
// see http://knockoutjs.com/documentation/observables.html#explicitly-subscribing-to-observables
beforeChangeSubscription.dispose();
afterChangeSubscription.dispose();
});
}
};
Usage is the same as above:
<div data-bind="fadeDuringChange: filteredUsers">
<!-- show filteredUsers... --->
</div>

Determine if observable has changed in computed

I am implementing a cache function in a computed observable.
Is there any way to invalidate the cache below if the items collection differs since the last call?
I have seen examples of dirty checking where a serialized version of the observable is used to determine if the collection has changed, but it's too expensive for me, since there may be hundreds of items.
var itemCache;
var manipulatedItems = ko.pureComputed(function(){
var items = someObervable();
if(!itemCache /* || someObervable.hasChangedSinceLastCall */) {
itemCache = heavyWork(items);
}
return itemCache;
});
var heavyWork = function(items){
// do some heavy computing with items
return alteredItems;
};
In my viewmodel:
myViewModel.itemList = ko.pureComputed(function(){
var result = manipulatedItems();
return result;
});
Since computed observables always cache the last value, there's no reason to store it separately. In fact, storing it separately can cause trouble with getting the latest data in your application.
var manipulatedItems = ko.pureComputed(function () {
var items = someObervable();
return heavyWork(items);
});
var heavyWork = function (items) {
// do some heavy computing with items
return alteredItems;
};
Yes, but you need to use .subscribe and keep the relevant moments in vars inside your own closure. There is no "last-modified-moment" property on observables or inside ko utils to be found.
In your repro, you could do something like this:
var lastChange = new Date();
var lastCacheRebuild = null;
var itemCache;
someObervable.subscribe(function(newObsValue) { lastChange = new Date(); });
var manipulatedItems = ko.pureComputed(function(){
var items = someObervable();
if(!itemCache || !lastCacheRebuild || lastChange > lastCacheRebuild) {
lastCacheRebuild = new Date();
itemCache = heavyWork(items);
}
return itemCache;
});
As far as the repro is concerned you could even put the items = someObservable() bit inside the if block.
PS. This is not recursive, i.e. the subscription is only on someObservable itself, not on the observable properties of things inside that observable. You'd have to manually craft that, which is specific to the structure of someObservable.

KnockoutJS not picking up jQuery .change() event

I've read every related post on this and spent last two days trying to figure out what I am doing wrong here without much success.
Working with this JS fiddle here as an example: http://jsfiddle.net/rniemeyer/dtpfv/ I am trying to implement a dirty flag. The only difference is that I am changing a data inside a regular span vs an input filed and using mapping plugin vs manually assigning observables.
$(document).ready(function(){
ko.dirtyFlag = function(root, isInitiallyDirty) {
var result = function() {},
_initialState = ko.observable(ko.toJSON(root)),
_isInitiallyDirty = ko.observable(isInitiallyDirty);
result.isDirty = ko.computed(function() {
return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
});
result.reset = function() {
_initialState(ko.toJSON(root));
_isInitiallyDirty(false);
};
return result;
};
$.getJSON('/environments/data.json', function(jsondata) {
var mapping = {
create: function (options) {
var innerModel = ko.mapping.fromJS(options.data);
for (var i=0; i < innerModel.deployments().length; i++) {
innerModel.deployments()[i].dirtyFlag = new ko.dirtyFlag(innerModel.deployments()[i]);
}
return innerModel;
}
}
var viewModel = ko.mapping.fromJS(jsondata, mapping);
self.save = function() {
console.log("Sending changes to server: " + ko.toJSON(this.dirtyItems));
};
self.dirtyItems = ko.computed(function() {
for (var i = 0; i < viewModel().length; i++ ) {
return ko.utils.arrayFilter(viewModel()[i].deployments(), function(deployment) {
return deployment.dirtyFlag.isDirty();
});
}
}, viewModel);
self.isDirty = ko.computed(function() {
return self.dirtyItems().length > 0;
}, viewModel);
self.changeTag = function (data, event) {
// Neither .change() nor .trigger('change') work for me
$(event.target).parents().eq(4).find('span.uneditable-input').text(data.tag).change()
// This value never changes.
console.log('Dirty on Change: '+self.dirtyItems().length)
}
ko.applyBindings(viewModel);
});
})
Here is stripped down piece of the HTML that triggers the changeTag() function, which replaces current_tag() with a selection from the drop down menu. This, however, does not trigger KnockoutJS update.
<div>
<span data-bind="text: current_tag() }"></span>
<div>
<button data-toggle="dropdown">Select Tag</button>
<ul>
<!-- ko foreach: $.parseJSON(component.available_tags() || "null") -->
<li></li>
<!-- /ko -->
</ul>
</div>
</div>
I am on day two of trying to figure this out. Any idea what I am doing wrong here? Am i supposed to use an input field and not a regular span element? Do i need to be changing values my viewModel directly instead of using jQuery to manipulate DOM? (I have actually tried that, but changing viewModel and then re-binding to it seems to slow things down, unless I am doing it wrong)
Thank you.
As said in the comments, it is considered 'bad Knockout practise' to update a value through jQuery, when you can also update the observable directly. Knockout promotes a data-driven approach.
In response to your last comment (not sure yet how to answer comments on stack overflow): the reason the UI isn't picking up the change is because you assigned the value wrong:
var x = ko.observable(1); // x is now observable
x = 3; // x is no longer observable. After all, you've assigned the value 3 to it. It is now just a number
x(3); // this is what you're after. x is still an observable, and you assigned a new value to it by using Knockout's parentheses syntax. If x is bound to the ui somewhere, you'll see the value 3 appear
So you want to do
jsondata[environment()].deployments[deployment()].current_tag(ko.dataFor(event.target).tag);

How do you handle with control binding in jquery

I have a question about jquery and DOM manipulation. How do you handle with DOM controls for e.g.
I have to get value from text input so I could this in ways:
var SomeClass = function() {
var control;
this.setControl = function(c) {
control = c;
}
this.getValue = function() {
return control.val();
}
}
$(document).ready(function() {
var sc = new SomeClass(); // of course control could be passed in contructor as well
sc.setControl($('#CONTROL'));
console.log(sc.getValue());
});
OR
var SomeClass = function() {
var control = $('#CONTROL');
this.getValue = function() {
return control.val();
}
}
$(document).ready(function() {
var sc = new SomeClass();
console.log(sc.getValue());
});
what is your opinion? What is better or maybe this is pile of trash therefore what is the best solution. Plz dont send me to backbone, spine and so on Im interesed in only in jquery.
best!
EDIT:
do you separate logic from UI or you are mixing it?
more complicated example
in js file you have a class that uses text control and in the secound js file also you need values from this input. What you are doing? you just call everytime $('#control') or create a third js file where would be a separated "class" to manipulate this input?
It would make more sense to move the setValue() inside the constructor:
SomeClass = function(c) {
var control = c;
return {
getValue: function() {
return control.val();
}
}
}
var x = new SomeClass($('input'));
alert(x.getValue());
However, I'm not sure how valuable this kind of information hiding will be. Perhaps as some kind of view wrapper.
In many cases you wouldn't need this wrapper, so just:
var $x = $('input'); // keep reference to a bunch of <input> elements.

"Hello World" in MVC Pattern

In an interview for some company, I was asked this question.
What design patterns do you know...then I was told to write simplest "hello world" application based on MVC Design Pattern.
I came up with a JavaScript program
var arr = ["a","b","c","d"]; // this is an array, same as store or model
alert(arr[0]); // this is controller
//and browser alert is a view.
later I was told that alert is a view. The basic concept about MVC I know is any changes in Model are reported to View. And there is a controller in between to call the methods.
Can you correct my approach, or come up with an alternate solution for hello world MVC application. Also explain subtle aspects of MVC.
Thanks.
var M = {}, V = {}, C = {};
M.data = "hello world";
V.render = function (M) { alert(M.data); }
C.handleOnload = function () { V.render(M); }
window.onload = C.handleOnLoad;
Controller (C) listens on some kind of interaction/event stream. In this case it's the page's loading event.
Model (M) is an abstraction of a data source.
View (V) knows how to render data from the Model.
The Controller tells to View to do something with something from the Model.
In this example
the View knows nothing about the Model apart from it implements some interface
the Model knows nothing of the View and the Controller
the Controller knows about both the Model and the View and tells the View to go do something with the data from the Model.
Note the above example is a severe simplification for demonstrating purposes. For real "hello world" examples in the JS MVC world go take a look at todoMVC
Better Example
var M = {}, V = {}, C = {};
/* Model View Controller Pattern with Form Example */
/* Controller Handles the Events */
M = {
data: {
userName : "Dummy Guy",
userNumber : "000000000"
},
setData : function(d){
this.data.userName = d.userName;
this.data.userNumber = d.userNumber;
},
getData : function(){
return data;
}
}
V = {
userName : document.querySelector("#inputUserName"),
userNumber : document.querySelector("#inputUserNumber"),
update: function(M){
this.userName.value = M.data.userName;
this.userNumber.value = M.data.userNumber;
}
}
C = {
model: M,
view: V,
handler: function(){
this.view.update(this.model);
}
}
document.querySelector(".submitBtn").addEventListener("click", function(){
C.handler.call(C);
});
/* Model Handles the Data */
/* View Handles the Display */
MVC Architecture
I have written an article about MVC architecture. Here is only some code present,hope anyone finds it helpful.
//Modal
var modal = { data: "This is data"};
//View
var view = { display : function () {
console.log ("////////////////////////////");
console.log ( modal.data);
console.log ("////////////////////////////");
}
};
//Controller
var controller = ( function () {
view.display();
})();
From the above example just understand that there are three different units in this design where each has a specific job to perform. Let's build the MVC design from the above infra structure .There can be more than one view and Observer, here only another view is created first.
// Modal
var modal = { data: "This is data"};
// View
var slashView = { display : function () {
console.log ("////////////////////////////");
console.log ( modal.data);
console.log ("////////////////////////////");
}
};
var starView = { display : function () {
console.log ("****************************");
console.log ( modal.data);
console.log ("****************************");
}
};
// Controller
var controller = ( function () {
slashView.display();
starView.display();
})();
What is understood here is that the modal must not be dependent upon either the view or viewers or the operations performed on the data. The data modal can stand alone but the view and controller are required because one needs to show the data and the other needs to manipulate it. Thus view and controller are created because of the modal and not the other way round.
//Modal
var modal = {
data : ["JS in object based language"," JS implements prototypal inheritance"]
};
// View is created with modal
function View(m) {
this.modal = m;
this.display = function () {
console.log("***********************************");
console.log(this.modal.data[0]);
console.log("***********************************");
};
}
function Controller(v){
this.view = v;
this.informView = function(){
// update the modal
this.view.display();
};
}
// Test
var consoleView = new View(modal);
var controller = new Controller(consoleView);
controller.informView();
From the above it can be seen that there has been a link established between view and the controller. And this is one of the requirement of MVC pattern. To demonstrate the change in the modal let's change the program and observe that change in the state of modal is done independently and reflects in view.
//Modal
function Modal(){
this.state = 0;
this.data = ["JS is object based language","JS implements prototypal inheritance"];
//
this.getState = function (){
return this.state;
};
this.changeState = function (value) {
this.state = value;
};
}
// View is created with modal
function View(m) {
this.modal = m;
this.display = function () {
console.log("***********************************");
console.log(this.modal.data[modal.getState()]);
console.log("***********************************");
};
}
//controller is created with the view
function Controller(v){
this.view = v;
this.updateView = function(){
// update the view
this.view.display();
};
}
// Test
var modal = new Modal();
var consoleView = new View(modal);
var controller = new Controller(consoleView);
controller.updateView();
// change the state of the modal
modal.changeState(1);
controller.updateView();
When the state of the modal is changed the controller has sent the message to the view to update itself. It is fine, but still one main concept is left to be implemented and that is the observer or controller needs to be identified by the modal . In order to see this happening, there has to be a link between modal and the controller so that any number of controller can show the interest in the modal, this is considered as registering the observer to the modal. This relation ship is implemented using the concept that observer does not exist in the air. Its existence come because of having interest in the modal thus when it is created it has to be created using the modal that it needs to show interest or in other words it has an access to the modal. Let's look at the example below and see how this MVC design pattern is achieved simply and elegantly using JavaScript.
function Modal(){
var stateChanged = false;
var state = 0;
var listeners = [];
var data = ["JS is object based language","JS implements prototypal inheritance"];
// To access the data
this.getData = function(){
return data;
};
// To get the current state
this.getState = function (){
return state;
};
// For simplicity sake we have added this helper function here to show
// what happens when the state of the data is changed
this.changeState = function (value) {
state = value;
stateChanged = true;
notifyAllObservers();
};
// All interested parties get notified of change
function notifyAllObservers (){
var i;
for(i = 0; i < listeners.length; i++){
listeners[i].notify();
}
};
// All interested parties are stored in an array of list
this.addObserver = function (listener){
listeners.push(listener);
};
}
// View class, View is created with modal
function View(m) {
this.modal = m;
this.display = function () {
console.log("***********************************");
var data = this.modal.getData();
console.log(data[modal.getState()]);
console.log("***********************************");
};
}
// Controller or Observer class has access to both modal and a view
function Controller(m,v){
this.view = v;
this.modal = m;
this.modal.addObserver(this);
// update view
this.updateView = function(){
this.view.display();
};
// Receives notification from the modal
this.notify = function(){
// state has changed
this.updateView();
};
}
// Test
var modal = new Modal();
var consoleView = new View(modal);
var controller = new Controller(modal,consoleView);
// change the state of the modal
modal.changeState(1);
modal.changeState(0);
modal.changeState(1);
modal.changeState(0);
From the above it can be seen that the observer has register itself using modal addObsever function and establishes a link to the modal. Once all instances are created. Modal state was changed manually to show the effect in the view. Typically in GUI environment, the change is usually done either with a user pressing any button or from any other external input. We can simulate the external input from the random generator and observe the effect. Here in the example below, some more elements are added in the data to show the effect clearly.
function Modal(){
var stateChanged = false;
var state = 0;
var listeners = [];
var data = [
"JS is object based language","JS implements prototypal inheritance",
"JS has many functional language features", "JS is loosely typed language",
"JS still dominates the Web", "JS is getting matured ","JS shares code
through prototypal inheritance","JS has many useful libraries like JQuery",
"JS is now known as ECMAScript","JS is said to rule the future of Web for
many years"];
//
this.getData = function(){
return data;
};
//
this.getState = function (){
return state;
};
this.changeState = function (value) {
state = value;
stateChanged = true;
notifyAllObservers();
};
function notifyAllObservers (){
var i;
for(i = 0; i < listeners.length; i++){
listeners[i].notify();
}
}
this.addObserver = function (listner){
listeners.push(listner);
};
}
// View is created with modal
function View(m) {
this.modal = m;
this.display = function () {
console.log("****************************************************");
var data = this.modal.getData();
console.log(data[modal.getState()]);
};
//Adding external simulation of user sending input
this.pressButton = function(){
var seed = 10;
var number = Math.round(Math.random() * seed) ;
// change the state of modal
this.modal.changeState(number);
};
}
// Controller class needs modal and view to communicate
function Controller(m,v){
this.view = v;
//console.log(this.view.display);
this.modal = m;
this.modal.addObserver(this);
this.updateView = function(){
// update the view
//console.log(this.view);
this.view.display();
};
this.notify = function(){
// state has changed
this.updateView();
};
}
// Test
var modal = new Modal();
var consoleView = new View(modal);
var controller = new Controller(modal,consoleView);
// change the state of the modal
for ( var i = 0 ; i < 10; i++){
consoleView.pressButton();
}
The above example demonstrates the use of MVC frame work where a modal is kept independent of the view and and the controller. The modal representing the data is responsible of notifying all the interested parties that has shown the interest and registered themselves with the modal. As soon as any change happens notification is send to the parties and action is left upon them. The example below is slightly different from the above where only newly added data is shown by the observer.
function Modal(){
var stateChanged = false;
var listeners = [];
var data = ["JS is object based language"];
// To retrieve the data
this.getData = function(){
return data;
};
// To change the data by any action
this.modifyData = function (string) {
( data.length === 1 )? data.push(string): data.unshift(string);
stateChanged = true;
notifyAllObservers();
};
// Notifies all observers
function notifyAllObservers (){
var i;
for(i = 0; i < listeners.length; i++){
listeners[i].notify();
}
}
// Requires to register all observers
this.addObserver = function (listener){
listeners.push(listener);
};
}
// View is created with modal
function View(m) {
this.modal = m;
this.display = function () {
console.log("****************************************************");
var data = this.modal.getData();
console.log(data[0]);
console.log("****************************************************");
};
//Adding external simulation of user sending input
this.pressButton = function(string){
// change the state of modal
this.modal.modifyData(string);
};
}
// View class
function Controller(m,v){
this.view = v;
this.modal = m;
this.modal.addObserver(this);
// Updates the view
this.updateView = function(){
this.view.display();
};
// When notifies by the modal send the request of update
this.notify = function(){
// state has changed
this.updateView();
};
}
// Test
var modal = new Modal();
var consoleView = new View(modal);
var controller = new Controller(modal,consoleView);
consoleView.pressButton();
consoleView.pressButton("JS dominates the web world");
consoleView.pressButton("JQuery is a useful library of JS");
The last thing which one may like to add is to delete the observer when not needed.This can be done through adding a method called removeObserver(object) in the modal calls. The above MVC design pattern can be more refined by using subcalssing and having common function present in the top class making the design as simple as possible but it is left on some other article. Hope it helps.
MVC is a design pattern that should be used to structure your application. MVC stands for Model, View, Control. It basically sais that you should separate your business-logic (Model) from your User Interface (View) and your Control-Logic.
For example:
You have a user class, that loads users from the database, can save em. This is your model.
You have a Controller that uses the User class to log a user in.
After the controller is done, it displays a Template containing the Text "Welcome $username".
Also, the Model should not know about the View and the Controller, the View should not know about the Controller, whereas the Controller knows about the Model and the View.
Wikipedia on MVC: http://de.wikipedia.org/wiki/Model_View_Controller
I think you're kind of missing the point here.
MVC is a pattern you'd use for designing an application. I think at the minimum you'd expect to be able to change the model, and see the change reflected in the view.
You'd typically have an object to represent the model, a different object to represent the "view" (which would probably mediate between the model and the HTML objects that you're using as the view) and a controller, which would take inputs from your HTML objects and update the model.
So you change an edit field, the edit field tells the controller, the controller updates the model, the model fires events that the controller uses to update any other view components that depend on this data.
It'd be a few more lines to implement a "hello world" version, but I think this is what I'd be looking for from an interview question like this.
So I made a simple example MVC with data output at console.log().
let model = {
data: {
text: "Hello World",
},
};
let view = {
init: function () {
this.render();
},
render: function () {
console.log(model.data.text);
},
};
let controller = {
init: function () {
view.init();
},
};
controller.init();

Categories

Resources