Click event doesn't bind in second foreach in knockout - javascript

Why does my click event not work in the second foreach?
My html:
<div class="row" id="menuBody" data-bind="foreach:categoryArray">
<div class="outer col-md-2" data-bind=" attr:{id:id},event:{mouseover :$parent.mouseOverKnockout,mouseout:$parent.mouseOutKnockout }">
<div class="inner col-md-12">
<label class="label" data-bind="text:name"> </label>
<div class="children" data-bind="css:{changeTopChildren: changeTopChildren}">
<ul data-bind="foreach:$parent.items1" class="childrenUl">
<li data-bind=" text: name,attr:{id:id},click: $parent.selectLi" class="childrenLi col-md-12"></li>
</ul>
</div>
</div>
</div>
</div>
My script:
var modelCategory = function (id, name) {
var self = this;
self.changeTopChildren = ko.observable(false);
self.name = ko.observable(name);
self.id = ko.observable(id);
}
var modelProduct = function (id, name) {
var _self = this;
_self.name = ko.observable(name);
_self.id = ko.observable(id);
_self.selectLi = function () {
alert("li");
console.log(" selectLi");
};
}
var viewModelMenuBody = function () {
var self = this;
self.selectLi = function (tag) {
alert("li");
console.log(" selectLi");
};
self.categoryArray = ko.observableArray();
self.items1 = ko.observableArray();
var temp = null;
self.mouseOverKnockout = function (arg, e) {
temp = arg;
for (var i = 0; i < self.categoryArray().length; i++) {
self.categoryArray()[i].changeTopChildren(false);
}
arg.changeTopChildren(true);
$.getJSON("/Home/getChildrenForMenu", { id: arg.id }, function (rowProduct) {
self.items1.removeAll();
for (var total = 0; total < rowProduct.length; total++) {
var temp = new modelProduct();
temp.id(rowProduct[total].id);
temp.name(rowProduct[total].name);
self.items1.push(temp);
}
});
}
self.mouseOutKnockout = function (arg) {
if (arg!=null)
arg.changeTopChildren(false);
//else
// temp.changeTopChildren(false);
};
(function () {
$.getJSON("/Home/getDataForMenu", null, function (rowCategory) {
for (var total = 0; total < rowCategory.length; total++) {
var temp = new modelCategory();
temp.id(rowCategory[total].id);
temp.name(rowCategory[total].name);
self.categoryArray.push(temp);
}
});
})();
};
var viewModel1 = new viewModelMenuBody();
ko.applyBindings(viewModel1, document.getElementById('menuBody'));

All methods are defined within root viewmodel object. So $parent.selectLi call from the nested foreach binding won't work because $parent context refers to current item of the outer foreach.
Use $root.selectLi instead.
More info: http://knockoutjs.com/documentation/binding-context.html

Related

Display current date when saving/editing span (angularjs)

I have a fairly simple task list using angularjs and I would like to save the time when each task was created and update that time if the task is edited.
I'm thinking I could use something like this to display the current time, but I'm not sure how to do it on save/edit.
HTML:
<div ng-controller="TodoListController">
<form ng-submit="addTodo()" name="form">
<input type="text" ng-model="todoText" size="30" placeholder="Add New Entry" required id="textField" ng-model="myVar">
<input class="btn-primary" type="submit" value="Save">
</form>
<ul class="unstyled">
<li ng-repeat="todo in todos | orderBy : $index:true">
<button type="button" class="close" aria-label="Close" ng-click="remove(todo)">
<span aria-hidden="true">×</span>
</button>
<span class="done-{{todo.done}}" ng-style="todo.customStyle" ng-hide="todo.editing" ng-click="updateVar($event)">{{todo.text}}</span>
<input type="text" ng-show="todo.editing" ng-model="todo.text">
<button type="submit" ng-hide="todo.editing" ng-click="change(todo); todo.editing === true">Edit</button>
<button type="submit" ng-show="todo.editing" ng-click="save($index); todo.editing === false">Save</button>
<button type="submit" ng-show="todo.editing" ng-click="cancel($index); todo.editing === false">Cancel</button>
</li>
</ul>
</div>
JS:
var app = angular.module('todoApp', []);
app.controller('TodoListController', ['$scope', function ($scope) {
$scope.todos = [];
$scope.newField = [];
$scope.customStyle = {};
$scope.addTodo = function () {
$scope.todos.push({text: $scope.todoText, done: false, editing: false});
$scope.todoText = '';
};
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.delete = function () {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function (todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
$scope.remove = function () {
$scope.todos.splice(this.$index, 1);
};
$scope.change = function (field) {
var todoIndex = $scope.todos.indexOf(field);
$scope.newField[todoIndex] = angular.copy(field);
$scope.todos[todoIndex].editing = true;
};
$scope.save = function (index) {
$scope.todos[index].editing = false;
};
$scope.cancel = function (index) {
$scope.todos[index] = $scope.newField[index];
};
$scope.updateVar = function (event) {
$scope.myVar = angular.element(event.target).text();
};
$scope.editKeyword = function (name, index) {
$scope.mode[index] = 'edit';
console.log(name);
};
}]);
Just add a todoDate attribute in the todo model like
$scope.todos.push({text: $scope.todoText, done: false, editing: false, todoDate: new Date()});
and update it when the user updates the todo object ie save()
$scope.save = function (index) {
$scope.todos[index].editing = false;
$scope.todos[index].todoDate = new Date();
};
Hope it helps.
When you call save function to add new todos simply add the createdOn field.
$scope.save = function (index) {
$scope.todos[index].editing = false;
$scope.todos[index].createdOn = new Date().getTime();
};
Now when user edits the todo and change function is called, do as below
$scope.change = function (field) {
var todoIndex = $scope.todos.indexOf(field);
$scope.newField[todoIndex] = angular.copy(field);
$scope.todos[todoIndex].editing = true;
$scope.todos[todoIndex].LastModifyOn = new Date().getTime();
};
Now if this updated todo is again edited by the user we need to simply call change function and it will update the LastModifyOn filed.
So by doing this, we can keep both data like when was the todo created and when was it last gets updated.
var app = angular.module('todoApp', []);
app.controller('TodoListController', ['$scope', function ($scope) {
$scope.todos = [];
$scope.newField = [];
$scope.customStyle = {};
$scope.addTodo = function () {
$scope.todos.push({text: $scope.todoText, done: false, editing: false, created: new Date()});
$scope.todoText = '';
};
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.delete = function () {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function (todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
$scope.remove = function () {
$scope.todos.splice(this.$index, 1);
};
$scope.change = function (field) {
var todoIndex = $scope.todos.indexOf(field);
$scope.newField[todoIndex] = angular.copy(field);
$scope.todos[todoIndex].editing = true;
$scope.todos[todoIndex].created = new Date();
};
$scope.save = function (index) {
$scope.todos[index].editing = false;
$scope.todos[index].created = new Date();
};
$scope.cancel = function (index) {
$scope.todos[index] = $scope.newField[index];
};
$scope.updateVar = function (event) {
$scope.myVar = angular.element(event.target).text();
};
$scope.editKeyword = function (name, index) {
$scope.mode[index] = 'edit';
console.log(name);
};
}]);

How to access viewModel in Knockout component?

I am trying to preopulate a knockout component on document ready.
I've written the following code:
function Finding(id, trigger) {
var self = this;
self.id = ko.observable(id);
self.trigger = ko.observable(trigger);
}
function FindingViewModel() {
let self = this;
self.findings = ko.observableArray();
self.addFinding = function () {
self.findings.push(new Finding(self.findings().length + 1, ""));
};
self.removeFinding = function (finding) {
self.findings.remove(finding);
ko.utils.arrayForEach(self.findings(), function (value, i) {
self.findings.replace(value, new Finding(i + 1, value.trigger()));
});
};
self.update = function (data) {
var findings = data.findings;
for (var index = 0; index < findings.length; ++index) {
var finding = findings[index];
self.findings.push(new Finding(self.findings().length + 1, finding.trigger));
}
};
}
ko.components.register('finding', {
template: `<table>
<tbody data-bind="foreach: findings">
<tr>
<td><span data-bind="text: id"/></td>
<td><input data-bind="value: trigger"/></td>
<td>Remove</td>
</tr></tbody></table>
<button data-bind="click: addFinding">Add a Finding</button>`,
viewModel: FindingViewModel
});
$(function () {
ko.applyBindings();
$.getJSON("/_get_findings", function (data) {
//findingModel.update(data);
})
});
How can I access the underlying Viewmodel from the finding component to set data from inside the getJSON function?
thx to Jeroen the solution looks like this:
function FindingViewModel() {
let self = this;
self.findings = ko.observableArray();
self.addFinding = function () {
self.findings.push(new Finding(self.findings().length + 1, ""));
};
self.removeFinding = function (finding) {
self.findings.remove(finding);
ko.utils.arrayForEach(self.findings(), function (value, i) {
self.findings.replace(value, new Finding(i + 1, value.trigger()));
});
};
self.update = function (data) {
let findings = data.findings;
for (let index = 0; index < findings.length; ++index) {
let finding = findings[index];
self.findings.push(new Finding(self.findings().length + 1, finding.trigger));
}
};
$.getJSON("/_get_findings", function (data) {
self.update(data);
});
}

Doing a knockout foreach on an observable's sub property

This is my div.
<div id="storyBody" data-bind="foreach: CurrentStory.Paragraphs">
<p data-bind="text: $data">
</p>
</div>
Here is my model:
$(function ()
{
var currentUser = "";
nm.Story = function()
{
this.Paragraphs = ko.observableArray([]);
this.CurrentEditorId = ko.observable();
this.LastEditorId = ko.observable();
};
nm.StoryPreview = function()
{
this.Preview = "";
this.documentId = 0;
};
nm.vm = function ()
{
var UserStoryList = ko.observableArray([]),
CurrentStory = ko.observable(),
GetCurrentStory = function (data) {
nm.getStory(data.documentId, GetCurrentStoryCallback, GetCurrentStoryErrorCallback)
},
GetCurrentStoryCallback = function (data) {
var story = new nm.Story().CurrentEditorId(data.CurrentEditorId)
.LastEditorId(data.LastEditorId);
story.Paragraphs(data.Paragraphs);
CurrentStory(story);
},
GetCurrentStoryErrorCallback = function (data) {
},
LoadUserStoriesList = function() {
nm.getStories(LoadUserStoriesListCallback, LoadUserStoriesListErrorCallback);
},
LoadUserStoriesListCallback = function(data)
{
$.each(data, function (index, value) {
var storyPreview = new nm.StoryPreview();
storyPreview.Preview = value.Preview;
storyPreview.documentId = value.DocumentId;
UserStoryList.push(storyPreview);
});
},
LoadUserStoriesListErrorCallback = function (data)
{
};
return {
UserStoryList: UserStoryList,
CurrentStory: CurrentStory,
LoadUserStoriesList : LoadUserStoriesList,
LoadUserStoriesListCallback: LoadUserStoriesListCallback,
GetCurrentStory: GetCurrentStory
}
}();
nm.vm.LoadUserStoriesList();
ko.applyBindings(nm.vm);
});
Seems like this should work, but it doesn't. Any ideas?
You are missing some parenthesis.
Could you try with:
<div id="storyBody" data-bind="foreach: CurrentStory().Paragraphs">
<p data-bind="text: $data">
</p>
</div>

knockout.js pushing items into observablearray

JSFiddle.
I am trying to push objects for an existing array into a knockout observable array but am failing terrible. How do I get this code to work, and since this code sucks what is the 'proper' way of doing this. Thank you so much
<div data-bind="foreach: Bob">
<div data-bind="text: $data">
</div>
</div>
var Test = function(){
var self= this;
self.Init = function(name){
self.Name = ko.observable(name);
};
};
var ArrayOfTests = function() {
var self = this;
self.Init = function(tests){
self.AllTests = ko.observableArray([]);
for(var i = 0; i <=tests.length-1;i++)
{
self.AllTests.push(tests[i]);
};
};
};
var TestViewModule = function(){
self.Bob = ko.observable();
var temp = new ArrayOfTests();
var tempTest = new Test();
tempTest.Init('one2');
temp.Init([tempTest,tempTest]);
self.Bob= temp;
};
ko.applyBindings(new TestViewModule());
console.log(new TestViewModule());
The answer ended up being, I was adding a null item to the array making the array not work.
You pass 'one2' string in Test.Init, but this method don't accept parameters:
var Test = function(){
var self= this;
self.Name = ko.observable();
self.Init = function(data){
self.Name(data);
};
};
Edit
I completly refactored your viewModel, because current implementation is not correct:
Html:
<div data-bind="with: Bob">
<div data-bind="foreach: AllTests">
<div data-bind="text: $data">
</div>
</div>
</div>
ViewModel:
var Test = function(){
var self= this;
self.Name = ko.observable(name);
self.Init = function(name){
self.Name(name);
};
};
var ArrayOfTests = function() {
var self = this;
self.AllTests = ko.observableArray();
self.Init = function(tests) {
for(var i = 0; i < tests.length; i++) {
self.AllTests.push(tests[i].Name());
};
};
};
var TestViewModule = function(){
var temp = new ArrayOfTests();
var tempTest = new Test();
tempTest.Init('one2');
temp.Init([tempTest,tempTest]);
self.Bob = ko.observable(temp);
};
ko.applyBindings(new TestViewModule());
console.log(new TestViewModule());

KnockoutJs...ObservableArray and Two Dropdown Lists

Let's say I have three objects like this:
var registryEpisode = function() {
var self = this;
self.registry = ko.observable(new registry());
self.episodeType = ko.observable(new episodeType());
};
var episodeType = function() {
var self = this;
self.id = ko.observable(""),
self.name = ko.observable("");
};
var registry = function() {
var self = this;
self.id = ko.observable(""),
self.name = ko.observable("");
};
Then I have a view model like this:
var vm = function() {
var self = this;
self.registryEpisodeTypes = ko.observableArray([new registryEpisode()]);
self.addRegistryEpisodeType = function (episodeType, registry) {
var regEpisode = new registryEpisode();
regEpisode.registry = registry;
regEpisode.episodeType = episodeType;
self.registryEpisodeTypes.push(regEpisode);
} .bind(this);
}
I'm trying to bind the view model to a table of dropdown lists and have the view model updated each time a registry and episode type is selected, but I need to maintain the relationship between episodesTypes and registries. Thoughts?
Here is a simple implemantion of a chain dropDownLists: jsFiddle
var registryEpisode = function() {
var self = this;
self.registry = ko.observable(new registry());
self.episodeType = ko.observable(new episodeType());
};
var episodeType = function(id, name) {
var self = this;
self.id = ko.observable(id), self.name = ko.observable(name);
};
var registry = function() {
var self = this;
self.id = ko.observable(""), self.name = ko.observable("");
};
var vm = function() {
var self = this;
self.selectedRegistry = ko.observable("");
self.registryEpisodeTypes = ko.observableArray([]);
self.addRegistryEpisodeType = function(episodeType, registry) {
var regEpisode = new registryEpisode();
regEpisode.registry = registry;
regEpisode.episodeType = episodeType;
self.registryEpisodeTypes.push(regEpisode);
}.bind(this);
self.getRegistries = function() {
var hashCheck = {};
var result = ko.observableArray([]);
ko.utils.arrayMap(self.registryEpisodeTypes(), function(item) {
if (hashCheck["" + item.registry.id()] === null || hashCheck["" + item.registry.id()] === undefined) {
hashCheck["" + item.registry.id()] = item.registry.name();
result.push({
"name": item.registry.name(),
"value": item.registry.id()
});
}
});
return result;
}
self.getEpisodes = ko.dependentObservable(function() {
var ret = self.registryEpisodeTypes();
var selectedRegistryItem = self.selectedRegistry();
if (selectedRegistryItem === null || selectedRegistryItem === undefined || selectedRegistryItem === "")
return ;
var result = ko.observableArray([]);
ko.utils.arrayMap(ret, function(item) {
if (item.registry.id() == selectedRegistryItem) result.push({
"name": item.episodeType.name(),
"value": item.episodeType.id()
});
});
console.log(ko.toJSON(result));
return result;
});
}
var viewModel = new vm();
var breakingBad = new registry();
breakingBad.id("1000");
breakingBad.name("Breaking Bad");
viewModel.addRegistryEpisodeType(new episodeType("1", "E1-breakingBad"), breakingBad);
viewModel.addRegistryEpisodeType(new episodeType("2", "E2-breakingBad"), breakingBad);
viewModel.addRegistryEpisodeType(new episodeType("3", "E3-breakingBad"), breakingBad);
var trueBlood = new registry();
trueBlood.id("1001");
trueBlood.name("True Blood");
viewModel.addRegistryEpisodeType(new episodeType("1", "E1-trueBlood"), trueBlood);
viewModel.addRegistryEpisodeType(new episodeType("2", "E2-trueBlood"), trueBlood);
viewModel.addRegistryEpisodeType(new episodeType("3", "E3-trueBlood"), trueBlood);
ko.applyBindings(viewModel);
<div>
<span data-bind="text:selectedRegistry"></span>
</div>
<div>
<select data-bind="options:getRegistries(),optionsText:'name',optionsValue:'value',value:selectedRegistry"></select>
</div>
<div>
<select data-bind="options:getEpisodes(),optionsText:'name',optionsValue:'value'"></select>
</div>
I create a dependentObservable called getEpisodes. Whenever selectedRegistry changes this episodeList calculated again.

Categories

Resources