I am trying to implement a simple favorites system. On the page load posts are listed on the home page and any previously favorited posts called nubs will show up with the FAVED tag underneath them.
<div class="list-group" ng-repeat="nub in nubs">
<a href="#" class="list-group-item active">
<h4 class="list-group-item-heading">{{nub.title}}</h4>
<p class="list-group-item-text">{{nub.description}}</p>
<p class="list-group-item-text">{{nub.synopsis}}</p>
<li ng-repeat="url in nub.attachmentsUrls">
<p class="list-group-item-image">
<img ng-src={{url}} />
</p>
</li>
</a>
<button ng-click="toggleFav(nub)">favorite</button>
<p ng-show="getFaved(nub.$id)">FAVED</p>
</div>
This is working but when I add something to my favorites the page doesn't update to reflect the newly favorited post. I would like to make my page respond actively to the toggleFav function.
Here is my controller
var ref = new Firebase("https://xxxxx.firebaseio.com");
var auth = ref.getAuth();
var nubRef = new Firebase("https://xxxxx.firebaseio.com/Nubs");
var nubs = $firebaseArray(nubRef);
$scope.nubs = nubs;
var userRef = new Firebase("https://xxxxx.firebaseio.com/users");
var users = $firebaseArray(userRef);
$scope.users = users;
// Array of booleans for favorites
$scope.favedArray = [];
// Array of user ids for
$scope.userIdArray = [];
var userFavs = $firebaseArray(userRef.child(auth.uid).child("favorites"));
$scope.userFavs = userFavs;
userFavs.$loaded()
.then
(
function()
{
nubs.$loaded()
.then
(
function()
{
$scope.tempFaved = [];
$scope.tempId = [];
console.log(userFavs);
angular.forEach
(
nubs,
function(nub)
{
$scope.tempFaved.push(false);
$scope.tempId.push(nub.$id);
console.log($scope.tempId);
angular.forEach
(
userFavs,
function(favs)
{
console.log($scope.tempFaved);
if(favs.nub == nub.$id)
{
$scope.tempFaved.pop();
$scope.tempFaved.push(true);
console.log($scope.tempFaved);
}
}
);
}
);
while($scope.tempFaved.length > 0)
{
$scope.favedArray.push($scope.tempFaved.pop());
$scope.userIdArray.push($scope.tempId.pop());
}
$scope.getFaved = function(nubId)
{
console.log($scope.favedArray[$scope.userIdArray.indexOf(nubId)]);
$scope.faved = $scope.favedArray[$scope.userIdArray.indexOf(nubId)];
return $scope.faved;
}
$scope.toggleFav = function(nub)
{
var nubFavRef = nubRef.child(nub.$id).child("favorites");
var nubFavs = $firebaseArray(nubFavRef);
var faved = $scope.getFaved(nub.$id)
console.log(faved);
if (faved == false)
{
nubFavs.$add
(
{
user: auth.uid
}
);
userFavs.$add
(
{
nub: nub.$id
}
)
console.log("favorited");
}
else
{
nubFavs.$remove(auth.uid);
userFavs.$remove(nub.$id);
console.log("unfavorited");
}
};
}
)
}
);
Essentially it is looping through the nubs or posts displayed on the page and checking them against the nubs the user has favorited to display the FAVED tag and toggle the functionality of the favorite button. If the user doesn't have the nub favorited the button will add the nub to their list of favorites as well as adding them to the list of users that have the nub favorited and if the user does have the post favorited it will remove them.
The unfavorite functionality of the toggleFav doesn't work either so help with that would also be appreciated, but that's a matter of being able to access the right child of the faved arrays which I'm not sure how to do.
What I think needs to happen for the page to update with the right information when something is favorited is some kind of $on listener, but I'm not sure how to implement it.
/* How store data in fire base:
{
"home" : {
"room1" : {
"status" : "true",
"switch_name" : "light 2",
"user_id" : "-Kvbk-XHqluR-hB8l2Hh"
}
}
}
*/
//select element in which you want real time data.
const preObject = document.getElementById('tbl_switch_list');
//select your root table name
const dbRefObject = firebase.database().ref().child('home');
//Change Value in Firebase and view in your console.
dbRefObject.on('value',snap => console.log('Response : ',snap.val());
<h3>Switch List</h3>
<table id="tbl_switch_list" border="1">
<thead>
<tr>
<td>#ID</td>
<td>#switchName</td>
<td>#status</td>
</tr>
<thead>
<tbody id="list"></tbody>
</table>
Related
Please help a little bit.
I have a list of 7 events displayed already with Angularjs. I'd like when I click on the <h2> (the event name) of some event, to open an ovelay that displays the same data from the database but only for this event which is clicked.
I'm sure that 'filter' will do the work but it seems I'm doing something wrong.
Here is my code. The ng-app and ng-controller are in the <main> tag.
Angularjs version: 1.7.9
My Html:
<main ng-app="eventsApp" ng-controller="eventsCtrl">
<!-- Overlay that holds and displays a single event -->
<div>
<div ng-repeat="x in singlePageEvent | filter:hasName(x.eventName)">
<div>
<img ng-src="{{x.eventImgSrc}}" alt="{{x.eventImgName}}"/>
<h2 class="event-name">{{x.eventName}}</h2>
<p>{{x.eventTime}}</p>
<p>{{x.eventPlace}}</p>
</div>
</div>
</div>
<!-- A list with all the events -->
<div ng-repeat="x in events">
<div>
<img ng-src="{{x.eventImgSrc}}" alt="{{x.eventImgName}}"/>
<h2 ng-click="singleEventOpen(x)" class="event-name">{{x.eventName}}</h2>
<p>{{x.eventTime}}</p>
<p>{{x.eventPlace}}</p>
</div>
</div>
</main>
My script:
let eventsApp = angular.module('eventsApp', []);
this filter below is not working at all. It continues to show all the events.
eventsApp.filter('hasName', function() {
return function(events, evName) {
var filtered = [];
angular.forEach(events, function(ev) {
if (ev.eventName && ev.eventName.indexOf(evName) >-1) {
filtered.push(ev);
}
});
return filtered;
}
});
eventsApp.controller('eventsCtrl', function($scope, $http) {
let x = window.matchMedia("(max-width: 450px)");
let singleEventOverlay = angular.element(document.querySelector('div.single-event.overlay'));
let singleEvent = singleEventOverlay;
function responsiveEventImages(x) { //this displays the list with events
if (x.matches) {
$http.get('./includes/events_res.inc.php').then(function(response) {
$scope.events = response.data.events_data;
});
} else {
$http.get('./includes/events.inc.php').then(function(response) {
$scope.events = response.data.events_data;
});
}
}
...and then by invoking singleEventOpen() the overlay appears, but it displays all the data, not just the clicked event
$scope.singleEventOpen = function(singleEvent) {
let clickedEvent = singleEvent.eventName; //I got the value of each h2 click thanx to #georgeawg but now what?
console.log("Fetching info for ", singleEvent.eventName);
$http.get('./includes/single_event.inc.php').then(function(response) {
$scope.singlePageEvent = response.data.events_data;
});
singleEventOverlay.removeClass('single-event-close').addClass('single-event-open');
}
});
The php file with the database extraction is working fine so I won't display it here.
What should I do to make the overlay display only the event which <h2> is clicked?
Here is a pic of the list with events
Here is a pic of the overlay
Thanx in advance.
EDITED
I got the value of each h2 click thanx to #georgeawg but now what?
UPDATE
Hey, thanx a lot #georgeawg . After many attempts I finally did this:
$scope.singleEventOpen = function(singleEvent) {
$http.get('./includes/single_event.inc.php').then(function(response) {
let allEvents = response.data.events_data;
for (var i = 0; i < allEvents.length; i++) {
singleEvent = allEvents[i];
}
});
console.log('Fetching data for', singleEvent);
$scope.ex = singleEvent;
});
And it works well.
Change the ng-click to pass an argument to the singleEventOpen function:
<div ng-repeat="x in events">
<div>
<img ng-src="{{x.eventImgSrc}}" alt="{{x.eventImgName}}"/>
<h2 ng-click="singleEventOpen(x)" class="event-name">{{x.eventName}}</h2>
<p>{{x.eventTime}}</p>
<p>{{x.eventPlace}}</p>
</div>
</div>
Then use that argument:
$scope.singleEventOpen = function(singleEvent) {
console.log("Fetching info for ", singleEvent.eventName);
//...
//Fetch and filter the data
$scope.ex = "single item data";
}
Adding an argument is the key to knowing which <h2> element was clicked.
Update
Don't use ng-repeat in the overlay, just display the single item:
<!-- Overlay that holds and displays a single event -->
̶<̶d̶i̶v̶ ̶n̶g̶-̶r̶e̶p̶e̶a̶t̶=̶"̶x̶ ̶i̶n̶ ̶s̶i̶n̶g̶l̶e̶P̶a̶g̶e̶E̶v̶e̶n̶t̶ ̶|̶ ̶f̶i̶l̶t̶e̶r̶:̶h̶a̶s̶N̶a̶m̶e̶(̶x̶.̶e̶v̶e̶n̶t̶N̶a̶m̶e̶)̶"̶>̶
<div ng-if="ex"">
<div>
<img ng-src="{{ex.eventImgSrc}}" alt="{{ex.eventImgName}}"/>
<h2 class="event-name">{{ex.eventName}}</h2>
<p>{{ex.eventTime}}</p>
<p>{{ex.eventPlace}}</p>
</div>
</div>
I am trying to filter through an array and display data based on the click event. There are three buttons that control different actions.
1.Button rollers display all the individuals who are rolling
2.Buttons non-rollers display lazy peeps who don't roll.
3.Buttons All display all the rollers and non-rollers.
I am trying to figure out if there is any way to display all rollers with the same filterRollers() function. I understand just resetting the state of rollingData back to the original value in a different function would do the trick but I am trying to limit to using one function. I will appreciate Any suggestion regarding the best practices . Thank you
var data =[
{ name="adam", task="roll" ,isrolling=true},
{ name="jack", task="roll" ,isrolling=false},
{ name="r2d2", task="roll" ,isrolling=true},
{ name="spidy", task="roll" ,isrolling=false}
]
this.state={
rollingState=data,
rollingData=data
}
filterRollers(status) {
let list = this.state.rollingState.filter(roll => {
return roll.isrolling === status
})
this.setState({
rollingData:list
})
}
render(){
return(
<div>
<button onClick={this.filterRollers(true)}>rollers </button>
<button onClick={this.filterRollers(false)}>non-rollers </button>
<button onClick={this.filterRollers(something)}> All </button>
{this.state.rollingData.map(roll=>{
return <Something roll={roll}/>
}
)}
</div>
)
}
When you want to show all the rollers, you can just call the function without any parameters. And then at the beginning of filterRollers function you can check if the parameter is undefined. If it is, just return all the data. If not, filter:
filterRollers(status) {
let list;
if(typeof status !== 'undefined'){
list = this.state.rollingState.filter(roll => {
return roll.isrolling === status
})
} else{
list = data.slice();
}
this.setState({
rollingData:list
})
}
Call the function like this, when you want to show all the rollers:
<button onClick={this.filterRollers()}> All </button>
I just deployed a website to test in "production" but when i try to go to the website some of my computer won't see one the results of my ng-repeat and some will see. If I go to the website when there's nothing displayed I look at the source code and I see the ng-repeat with each object of my array, but no html output on the screen. Here some of my code when I load my controller:
/**
* Function that send a request to get a list of posts.
* #return {Function} A promise.
*/
function retrievePosts() {
var defered = $q.defer();
// If the user is logged in we do a search by country, otherwise we get all the posts.
if($rootScope.user !== null && $rootScope.user !== undefined) {
PostService.searchPost({ countries: [$rootScope.user.country] }, function(err, posts) {
if(err) {
defered.reject(err);
}
else if(posts && posts.length > 0) {
defered.resolve(posts);
}
// If the previous condition is not true, we try to get all the posts, since the search by country didn't work.
else {
PostService.getAllPosts(function(err, posts2) {
if(err) {
defered.reject(err);
} else {
defered.resolve(posts2);
}
});
}
});
} else {
PostService.getAllPosts(function(err, posts) {
if(err) {
defered.reject(err);
}
else {
defered.resolve(posts);
}
});
}
return defered.promise;
}
This function is used to get an array of JSON posts object. Then I do a q.all like this:
$q.all([retrieveManufacturer(), retrieveCategories(), retrievePosts(), getTotalPosts(), retrieveGalleryPosts()]).then(function(results) {
$scope.manufacturers = results[0];
$scope.categories = results[1];
// Here we must cache the result and slice it, so that angular doesn't render
// a tone of post but 10 at a time.
postCache = results[2];
$scope.numberOfPostsToShow = 10;
$scope.posts = postCache.slice(0, $scope.numberOfPostsToShow);
// Some code to display the proper amount of post for each category.
var i = -1;
var max = results[3].length;
var groupedPostsCount = { };
var group;
while(++i < max) {
group = results[3][i];
// "_id" contains the name of the category.
groupedPostsCount[group._id] = group.count;
}
if(Object.keys(groupedPostsCount).length > 0){
$scope.categoriesPostCount = groupedPostsCount;
}
$scope.galleryPosts = results[4];
// Prepare the $scope.galleryPosts to be bound with posts.
buildGallery($scope.galleryPosts);
}, function(err) {
console.log(err);
});
Every task in $q.all gets executed and they all get resolved. I see them in my HTML like the categories, manufacturers, etc... Results[2] which are the array of posts are not null they really do have 500 posts in them. I try to call $scope.$apply() after buildGallery() method call, but nothing work. If I print {{ posts }} anywhere in my html i see the array of posts. But when they are in that ng-repeat:
<div class="ad-container" ng-repeat="post in posts" ng-click="viewPostDetails(post)">
<div class="ad-picture">
<table class="wrapper">
<tr>
<td><img ng-src="img/175/{{ post.mainImageName || post.imgUrls[0] }}" alt="No image provided"/></td>
</tr>
</table>
</div>
<div class="ad-info">
<span class="ad-info-title">{{ post.title }}</span>
<span class="ad-info-price">{{ post.country == 'Canada' ? (post.price | currency : "CA$") : (post.price | currency : "US$") }}</span>
<br />
<span>{{ post.country }}, {{ post.province }}, {{ post.createdAt | date }}</span>
<p>{{ post.description }}</p>
</div>
</div>
Of course this code is inside a div that has a controller bound to it.Like I said, it's really weird. On my development computer everything works perfectly, but some of the computers of my friend did work and others didn't. Here's the link to the website www.firearmsbin.com maybe the problem will occur on your computer. I tried on firefox, firefox for dev, edge, chrome and IE11.
Thanks.
I found out that it was adblock who was not displaying my div which as the class "ad-container". So every class in css that contains "ad" word get block.
I have a view (cshtml) that has a tab strip on it. The contents of each tab is of course different. The individual tabs have the correct data/information on them. There is some javascript that is intended to fire when a selection is made from the control on the individual tab. As it stands right now the first tab rendered the javascript fires. All other tabs do not fire. Further on the tab that does fire (first one) it obtains the correct value but then when trying to find the matching item in the model it doesn't find a match. Debugging shows that only the data for the last tab is available in the model. Well that explains why no match but begs the question of where did the data the first page was populated with go?
I have snipped the code for brevity. If, in my ignorance I left something out just say so and I'll post whatever is needed.
So to start here is the parent cshtml:
foreach (var extbrd in Model.ExternalBoards)
{
tabstrip.Add()
.Text(extbrd.ExtForumName)
.ImageUrl("~/.../ForumTabIcon.png")
.Content(#<text>
<div>
#Html.Action("ActionName", "Controller", new { id = extbrd.BoardId });
</div>
</text>);
}
Well as you can see above as we loop we call an action in the controller for each tab. Here is that action:
public ActionResult ActionName(int extforumid)
{
//get url for selected forum (tab) and pull feed
ExternalForums ExtFrm = _forumService.GetExternalForumById(extforumid);
reader.Url = ExtFrm.ForumUrl;
return View(reader.GetFeed());
}
That's actually it. As above I can post the reader code but I don't think it is the source of the trouble.
Well this action of course has a view and this is where I think things get wacky:
#model ExternalThreadsModel
<script type="text/javascript">
var model = #Html.Raw(Json.Encode(Model.RssThreads))
</script>
<script type="text/javascript">
$(function() {
$("##Html.FieldIdFor(model => model.ExtForumIds)").click(function () {
var selectedItem = $(this).val();
var matchingObj = getObjects(model, 'ThreadValue', selectedItem);
if(matchingObj > 0)
{
var $iframe = $('#ForumFrame');
if ( $iframe.length ) {
$iframe.attr('src', matchingObj[0].Link);
}
var $prevfram = $('#ForumPreview');
if ( $prevfram.length ) {
$prevfram.val(matchingObj[0].Description);
}
}
});
});
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (i == key && obj[key] == val) {
objects.push(obj);
}
}
return objects;
}
</script>
<div>
<table>
<tr>
<td>
#Html.DropDownListFor(model => model.ExtForumIds, Model.SelectThreads, new {style = "...", #size = 30})
</td>
<td style="width:25px;"> </td>
<td>
#{ Html.Telerik().TabStrip()
.Name("ForumView")
.Items(tabstrip =>
{
tabstrip.Add()
.Text("Preview")
.Content(#<text>
<div>
<textarea style="background-color:#979797; text-decoration: none;" id="ForumPreview" name="ForumPreview" rows="26" cols="200" readonly></textarea>
</div>
</text>);
tabstrip.Add()
.Text("Interactive")
.Content(#<text>
<div>
<iframe id="ForumFrame" name="ForumFrame" src="" style="width:800px;height:350px;"></iframe>
</div>
</text>);
})
.SelectedIndex(0)
.Render();
}
</td>
</tr>
</table>
</div>
So as I mentioned each tab does have the correct data / information on it. The problem comes when a user selects an item from the drop down list.
The click handler only fires on the first tab. It doesn't fire for any other tabs???
Further on the first tab the click handler does fire and it pulls the correct selectedItem but when it runs through the helper function getobjects it doesn't find a match.
When I break and examine "model" as it is being passed into getObjects it only contains data for the last tab...so yeah nothing is going to be matched.
What is even stranger for me to understand is the line:
<script type="text/javascript">
var model = #Html.Raw(Json.Encode(Model.RssThreads))
</script>
In HTML it does render a json object with ALL the data from ALL the tabs...so...somewhere I must be running into variable scope pollution????
Your support and assistance is..as always..greatly appreciated.
I am using knockout.js to display a list of employees. I have a single hidden modal markup on the page. When the "details" button for a single employees is clicked, I want to data-bind that employee to the modal popup. I am using the ko.applyBindings(employee, element) but the problem is when the page loads, it is expecting the modal to start off as bound to something.
So I'm wondering, is there a trick/strategy to do a late/deferred databinding? I looked into virtual bindings but the documentation was not helpful enough.
Thanks!
I would like to propose a different way to work with modals in MVVVM. In MVVM, the ViewModel is data for the View, and the View is responsible for the UI. If we examine this proposal:
this.detailedEmployee = ko.observable({}),
var self = this;
this.showDetails = function(employee){
self.detailedEmployee(employee);
$("#dialog").dialog("show"); //or however your dialog works
}
I strongly agree with this.detailedEmployee = ko.observable({}), but I am in strong disagreement with this line: $("#dialog").dialog("show");. This code is placed in the ViewModel and shows the modal window, wherein fact it is View's responsibility, so we screw-up the MVVM approach. I would say this piece of code will solve your current task but it could cause lots of problems in future.
When closing the popup, you should set detailedEmployee to undefined to have your main ViewModel in a consistent state.
When closing the popup, you might want to have validation and the possibility to discard the close operation when you want to use another modal's component in the application
As for me, these points are very critical, so I would like to propose a different way. If we "forget" that you need to display data in popup, binding with could solve your issue.
this.detailedEmployee = ko.observable(undefined);
var self = this;
this.showDetails = function(employee){
self.detailedEmployee(employee);
}
<div data-bind="with: detailedEmployee">
Data to show
</div>
As you can see, your ViewModel don't know anything about how data should be shown. It knows only about data that should be shown. The with binding will display content only when detailedEmployee is defined. Next, we should find a binding similar to with but one that will display content in the popup. Let's give it the name modal. Its code is like this:
ko.bindingHandlers['modal'] = {
init: function(element) {
$(element).modal('init');
return ko.bindingHandlers['with'].init.apply(this, arguments);
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
var returnValue = ko.bindingHandlers['with'].update.apply(this, arguments);
if (value) {
$(element).modal('show');
} else {
$(element).modal('hide');
}
return returnValue;
}
};
As you can see, it uses the with plugin internally, and shows or hide a popup depending on value passed to binding. If it is defined - 'show'. If not - 'hide'. Its usage will be the as with:
<div data-bind="modal: detailedEmployee">
Data to show
</div>
The only thing you need to do is to use your favorite modals plugin. I prepared an example with the Twitter Bootstrap popup component: http://jsfiddle.net/euvNr/embedded/result/
In this example, custom binding is a bit more powerful; you could subscribe the onBeforeClose event and cancel this event if needed. Hope this helps.
The JSFiddle linked to in the answer provided by #Romanych didn't seem to work anymore.
So, I built my own example (based upon his original fiddle) with full CRUD support and basic validation using Bootstrap 3 and the Bootstrap Modal library: https://jsfiddle.net/BitWiseGuy/4u5egybp/
Custom Binding Handlers
ko.bindingHandlers['modal'] = {
init: function(element, valueAccessor, allBindingsAccessor) {
var allBindings = allBindingsAccessor();
var $element = $(element);
$element.addClass('hide modal');
if (allBindings.modalOptions && allBindings.modalOptions.beforeClose) {
$element.on('hide', function() {
var value = ko.utils.unwrapObservable(valueAccessor());
return allBindings.modalOptions.beforeClose(value);
});
}
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).removeClass('hide').modal('show');
} else {
$(element).modal('hide');
}
}
};
Example Usage
The View
<div data-bind="modal: UserBeingEdited" class="fade" role="dialog" tabindex="-1">
<form data-bind="submit: $root.SaveUser">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>User Details</h3>
</div>
<div class="modal-body">
<div class="form-group">
<label for="NameInput">Name</label>
<input type="text" class="form-control" id="NameInput" placeholder="User's name"
data-bind="value: UserBeingEdited() && UserBeingEdited().Name, valueUpdate: 'afterkeydown'">
</div>
<div class="form-group">
<label for="AgeInput">Age</label>
<input type="text" class="form-control" id="AgeInput" placeholder="User's age"
data-bind="value: UserBeingEdited() && UserBeingEdited().Age, valueUpdate: 'afterkeydown'">
</div>
<!-- ko if: ValidationErrors() && ValidationErrors().length > 0 -->
<div class="alert alert-danger" style="margin: 20px 0 0">
Please correct the following errors:
<ul data-bind="foreach: { data: ValidationErrors, as: 'errorMessage' }">
<li data-bind="text: errorMessage"></li>
</ul>
</div>
<!-- /ko -->
</div>
<div class="modal-footer">
<button type="button" data-dismiss="modal" class="btn btn-default">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
The ViewModel
/* ViewModel for the individual records in our collection. */
var User = function(name, age) {
var self = this;
self.Name = ko.observable(ko.utils.unwrapObservable(name));
self.Age = ko.observable(ko.utils.unwrapObservable(age));
}
/* The page's main ViewModel. */
var ViewModel = function() {
var self = this;
self.Users = ko.observableArray();
self.ValidationErrors = ko.observableArray([]);
// Logic to ensure that user being edited is in a valid state
self.ValidateUser = function(user) {
if (!user) {
return false;
}
var currentUser = ko.utils.unwrapObservable(user);
var currentName = ko.utils.unwrapObservable(currentUser.Name);
var currentAge = ko.utils.unwrapObservable(currentUser.Age);
self.ValidationErrors.removeAll(); // Clear out any previous errors
if (!currentName)
self.ValidationErrors.push("The user's name is required.");
if (!currentAge) {
self.ValidationErrors.push("Please enter the user's age.");
} else { // Just some arbitrary checks here...
if (Number(currentAge) == currentAge && currentAge % 1 === 0) { // is a whole number
if (currentAge < 2) {
self.ValidationErrors.push("The user's age must be 2 or greater.");
} else if (currentAge > 99) {
self.ValidationErrors.push("The user's age must be 99 or less.");
}
} else {
self.ValidationErrors.push("Please enter a valid whole number for the user's age.");
}
}
return self.ValidationErrors().length <= 0;
};
// The instance of the user currently being edited.
self.UserBeingEdited = ko.observable();
// Used to keep a reference back to the original user record being edited
self.OriginalUserInstance = ko.observable();
self.AddNewUser = function() {
// Load up a new user instance to be edited
self.UserBeingEdited(new User());
self.OriginalUserInstance(undefined);
};
self.EditUser = function(user) {
// Keep a copy of the original instance so we don't modify it's values in the editor
self.OriginalUserInstance(user);
// Copy the user data into a new instance for editing
self.UserBeingEdited(new User(user.Name, user.Age));
};
// Save the changes back to the original instance in the collection.
self.SaveUser = function() {
var updatedUser = ko.utils.unwrapObservable(self.UserBeingEdited);
if (!self.ValidateUser(updatedUser)) {
// Don't allow users to save users that aren't valid
return false;
}
var userName = ko.utils.unwrapObservable(updatedUser.Name);
var userAge = ko.utils.unwrapObservable(updatedUser.Age);
if (self.OriginalUserInstance() === undefined) {
// Adding a new user
self.Users.push(new User(userName, userAge));
} else {
// Updating an existing user
self.OriginalUserInstance().Name(userName);
self.OriginalUserInstance().Age(userAge);
}
// Clear out any reference to a user being edited
self.UserBeingEdited(undefined);
self.OriginalUserInstance(undefined);
}
// Remove the selected user from the collection
self.DeleteUser = function(user) {
if (!user) {
return falase;
}
var userName = ko.utils.unwrapObservable(ko.utils.unwrapObservable(user).Name);
// We could use another modal here to display a prettier dialog, but for the
// sake of simplicity, we're just using the browser's built-in functionality.
if (confirm('Are you sure that you want to delete ' + userName + '?')) {
// Find the index of the current user and remove them from the array
var index = self.Users.indexOf(user);
if (index > -1) {
self.Users.splice(index, 1);
}
}
};
}
Initializing Knockout with the View and the ViewModel
var viewModel = new ViewModel();
// Populate the ViewModel with some dummy data
for (var i = 1; i <= 10; i++) {
var letter = String.fromCharCode(i + 64);
var userName = 'User ' + letter;
var userAge = i * 2;
viewModel.Users.push(new User(userName, userAge));
}
// Let Knockout do its magic!
ko.applyBindings(viewModel);
I would create another observable that wraps the employee.
this.detailedEmployee = ko.observable({}),
var self = this;
this.showDetails = function(employee){
self.detailedEmployee(employee);
$("#dialog").dialog("show"); //or however your dialog works
}
Attach the click to showDetails. Then you can just call applyBindings on page load.