Show list without using a button, with knockout - javascript

I have this code to show a list when a button is clicked. How can I make the list show without clicking the button?
<button class="btn" type="submit" data-bind="click: getList">Get List</button>
<ul data-bind="foreach: pList">
<li>
<b data-bind="text: $data"></b>
</li>
</ul>
define(['sublime/aadConfig','knockout'], function (aconfig,ko) {
var plugins = ko.observableArray([]);
var getPlugins = function () {
plugins(aconfig.getPluginList());
};
return {
pList: plugins,
getList: getPlugins
};
});

If you want the list to load without having to click the button you just need to call getPlugins;
define(['sublime/aadConfig','knockout'], function (aconfig,ko) {
var plugins = ko.observableArray([]);
var getPlugins = function () {
plugins(aconfig.getPluginList());
};
getPlugins(); //<-- This will go populate your plugins observable, which should auto populate your list.
return {
pList: plugins,
getList: getPlugins
};
});
EDIT
If you have objects in your array then that would happen as you specified to show $data in your markup.
[{text:"someText"},{text:"someText"},{text:"someText"}] <-- this would output [object Object]
["sometext","sometext","sometext"] <-- would output "sometext"
So its sound like you need to say $data.myDesiredValue or just myDesiredValue in your markup rather than $data

a quick solution if you have jQuery but not so pretty :
$(function(){
$('button.btn').click();
});

Related

How to get element by id which is dynamically created by ng-repeat in angularjs

I am only posting the necessary code and solving this much will clear rest of my doubts. I am new to angularjs, so kindly forgive if I am asking something stupid.
I am using ng-repeat to generate a list which uses an array defined in the controller scope. When I click on 'Add Another' button, a new element is created. I want to get access of this element to add a class to it. But when I use 'getElementById' function in the same function 'addNewForm' I get 'null'.
However, when I call function 'fn' by hitting 'Find Untitled' button, I get the correct element. Could anybody explain and solve this? Any help is appreciated. Thanks in advance!
I am posting the code below:
HTML:
<div ng-controller="myctrl3">
<ul id ="menu_Ul">
<li ng-repeat="x in list">
<button id="{{ 'navAppsButtonID-' + $index }}">{{x}}</button>
<br>
</li>
<li>
<button ng-click="addNewForm()">Add another</button>
</li>
</ul>
<button ng-click="fn()">Find Untitled</button>
</div>
JS:
.controller("myctrl3", function($scope) {
var list = ['abcd', 'efgh', 'ijkl', 'mnop'];
$scope.list = list;
$scope.abc = function () {
var listPush = function () {
$scope.list.push("Untitled Menu");
for(var i = 0;i<$scope.list.length-1;i++) {
var element = document.getElementById('navAppsButtonID-'+i);
element.classList.remove('current');
}
};
var listLen = $scope.list.length;
if($scope.list[listLen-1] === undefined) {
listPush();
}
else if ($scope.list[listLen-1] == "Untitled Menu") {
alert("Cannot open more than one Untitled Menu at the same time.");
}
else {
listPush();
}
};
$scope.addNewForm = function() {
$scope.abc();
console.log("Element is: ", document.getElementById('navAppsButtonID-'+($scope.list.length-1)));
};
$scope.fn = function () {
console.log("Element is: ", document.getElementById('navAppsButtonID-'+($scope.list.length-1)));
};
})
You're thinking too much jQuery and too little angular. If the goal is to add a class to the last element of ng-repeat, this is how you do that:
<li ng-repeat="x in list">
<button ng-class="{ current: $last }">{{ x }}</button>
</li>
$last is a variable available inside ng-repeat, and if it's true, ng-class will set the class current on the element.
You don't assign unique ids to elements to getElementById from somewhere else when working in angular.

$scope.apply not working in Angular

im trying to learn Angular.
Here is what im trying to do:
I am building an App that shows me citys. When i click on a city i want to see a list of all my favourite citys.
Using an "Show-List" Button with ng-click works but requires the button the be pushed.
Here is my approach for getting it done automatic:
I want a list in my DOM automatically updated on change of the list.
$scope.$watch('updatedList', function() {
// CHECK IF WORKS
console.log($scope.updatedList);
// APPLY TO DOM
$timeout(function(){
$scope.$apply(function () {
$scope.watchList = $scope.updatedList;
});
}, 1000)
});
The Console shows no error and gives out the correc values:
Object {city.3: "Herat", city.7: "Haag", city.10: "Tilburg" ......}
In my div is the following:
<ul>
<li ng-repeat="y in updatedList">{{ y }}</li>
</ul>
<ul>
<li ng-repeat="a in watchList">{{ a }}</li>
</ul>
First for the NG-Click-Version(which works on click) second for the $scope.$watch
Sorry for lots of questions but im really struggling with the Angular-Docs.
EDIT:
Function that Adds Citys to the List:
$scope.addToList = function(name,id) {
var cityToAdd = name;
var cityToAddID = id;
// ADD A CITY TO THE COOKIE -> WORKS
$cookies.put('city.' + cityToAddID, cityToAdd);
$scope.newList = $cookies.getAll();
$scope.addToListMessage = cityToAdd + " wurde hinzugefĆ¼gt";
// Show short INFONOTICE
window.setTimeout(function() {
$scope.$apply(function() {
$scope.addToListMessage = "";
});
}, 1000);
// Update the List
$scope.updateList();
};
Second Functions -> gets Values from Cookies and puts them to an Array:
$scope.updateList = function() {
var allCitys = $cookies.getAll();
// PUT ALL INTO AN ARRAY -> WORKS
var favouritesFromCookie = [];
$.each(allCitys, function(index, value) {
if (index.indexOf('city.') == 0) { favouritesFromCookie.push(value) }
});
// PUT THE ARRAY OF CITYS INTO A SCOPE_VARIABLE
$scope.updatedList = favouritesFromCookie;
};
Your $scope.updatedList needs to be an array to be used in ng-repeat.
You shouldn't directly write a list in expression. Try this
<ul>
<li ng-repeat="y in watchList">{{ y.city }}</li>
<li ng-repeat="y in watchList">{{ y.yourListItem}}</li>
</ul>

binding knockout observable arrays to multiple <ul> elements

For a navigation menu, I have two groups of links, each group and link showing up or not dependent on a user's role. The roles are looked up when the link structure is being built and the list of links is built accordingly. The returned JSON gets parsed, put into observable arrays with no problem, but when I actually try and apply the bindings, the binding fails because the observables are blank. Here is the HTML...
<ul id="user-menu" class="menu" data-bind="foreach: areas">
<li>
<a data-bind="attr: { href: areaLink }">
<img data-bind="attr: { src: iconUri }" />
<span data-bind="text: areaName"></span>
</a>
</li>
</ul>
<ul id="admin-menu" class="menu" data-bind="foreach: adminAreas">
<li>
<a data-bind="attr: { href: areaLink }">
<img data-bind="attr: { src: iconUri }" />
<span data-bind="text: areaName"></span>
</a>
</li>
</ul>
Knockout view model in the background...
var navigation = (function() {
function Area() {
var self = this;
self.areaName = ko.observable();
self.areaLink = ko.observable();
self.iconUri = ko.observable();
self.sequenceNo = ko.observable();
self.isAdmin = ko.observable();
self.loadFromVM = function (vm) {
self.areaName(vm.name || '');
self.areaLink(vm.link || '');
self.iconUri(vm.iconUri || '');
self.sequenceNo(vm.sequenceNo || '');
self.isAdmin(vm.isAdmin);
}
}
function viewModel() {
var self = this;
self.areas = ko.observableArray([]);
self.adminAreas = ko.observableArray([]);
self.setup = function () {
var data = {}; // population with basic session data
$.getJSON('....php', { JSON.stringify(data) }, function (results) {
for (var i = 0; i < results.length; i++) {
var area = new Area();
area.loadFromVM(results[i]);
if (area.isAdmin()) {
self.adminAreas().push(area);
} else {
self.areas().push(area);
}
}
});
};
}
var vmInstance;
return {
setup: function () {
vmInstance = new viewModel();
vmInstance.setup();
ko.applyBindings(vmInstance, $('#user-menu')[0]);
ko.applyBindings(vmInstance, $('#admin-menu')[0]);
}
};
})();
And then I bring it together with this in the navigation view file...
navigation.setup();
So after I get my JSON back, parse it, and organize it when I loop through the success function of the $.getJSON method, putting a watch on self.areas() and self.adminAreas() does show that the arrays have the exact information I want them to. But by the time they have to be applied, calling vmInstance.areas().length or vmInstance.adminAreas().length returns zero. Even more oddly, putting in an alert with the length of the arrays right after the $.getJSON call but within the setup() function will cause the alert to fire first, show zeroes, then goes through the get, populates the array, then fires zeroes again.
Not exactly sure what's going on here, but I can't remember seeing this kind of behavior in another project so I'm not quite sure what I'm doing wrong here. Any ideas or advice would be greatly appreciated.
EDIT: Nevermind on the Fiddle. It doesn't really capture my actual error.
adminarea object is not initialized.you made the adminArea variable but instead of this you have used same area variable to set values.
var adminArea = new Area();
adminArea.areaName('test admin area');
adminArea.areaLink('#');
adminArea.iconUri('http://evernote.com/media/img/getting_started/skitch/windows8/win8-checkmark_icon.png');
adminArea.sequenceNo(1);
adminArea.isAdmin(true);
Fiddle Demo

Knockout foreach binding not working

I'm following John Papa's jumpstart course about SPA's and trying to display a list of customers loaded via ASP.NET Web API the knockout foreach binding is not working. The Web API is working fine, I've tested it on it's own and it is returning the correct JSON, because of that I won't post the code for it. The get method simply returns one array of objects, each with properties Name and Email. Although not a good practice, knockout is exposed globaly as ko by loading it before durandal.
I've coded the customers.js view model as follows
define(['services/dataservice'], function(ds) {
var initialized = false;
var customers = ko.observableArray();
var refresh = function() {
return dataservice.getCustomers(customers);
};
var activate = function() {
if (initialized) return;
initialized = true;
return refresh();
};
var customersVM = {
customers: customers,
activate: activate,
refresh: refresh
};
return customersVM;
});
The dataservice module I've coded as follows (I've not wrote bellow the function queryFailed because I know it's not being used)
define(['../model'], function (model) {
var getCustomers = function (customersObservable) {
customersObservable([]);
var options = {url: '/api/customers', type: 'GET', dataType: 'json'};
return $.ajax(options).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
var customers = [];
data.forEach(function (item) {
var c = new model.Customer(item);
customers.push(c);
});
customersObservable(customers);
}
};
return {
getCustomers: getCustomers
};
});
Finaly the model module was built as follows:
define(function () {
var Customer = function (dto) {
return mapToObservable(dto);
};
var model = {
Customer: Customer
};
return model;
function mapToObservable(dto) {
var mapped = {};
for (prop in dto)
{
if (dto.hasOwnProperty(prop))
{
mapped[prop] = ko.observable(dto[prop]);
}
}
return mapped;
}
});
The view is then simply a list, it is simply:
<ul data-bind="foreach: customers">
<li data-bind="text: Name"></li>
</ul>
But this doesn't work. Any other binding works, and I've looked on the console window, and it seems the observable array is being filled correctly. The only problem is that this piece of code doesn't show anything on screen. I've reviewed many times the files but I can't seem to find the problem. What's wrong with this?
You can use the knockout.js context debugger chrome extension to help you debug your issue
https://chrome.google.com/webstore/detail/knockoutjs-context-debugg/oddcpmchholgcjgjdnfjmildmlielhof
Well, I just spent a lot of time on an local issue to realize that the ko HTML comment format, if used, should be like this:
<!-- ko foreach: arrecadacoes -->
and NOT like this:
<!-- ko: foreach: arrecadacoes -->
: is NOT used after ko...
I know this question is a little old but I thought I'd add my response in case someone else runs into the same issue I did.
I was using Knockout JS version 2.1.0 and it seems the only way I can get the data to display in a foreach loop was to use:
$data.property
so in the case of your example it would be
$data.Name
Hope this helps
I don't see anywhere in your code that you've called ko.applyBindings on your ViewModel.
KO has a known issue while using foreach in a non-container element like the one above <ul> so you have to use containerless control flow syntax.
e.g.
<ul>
<!-- ko foreach: customers-->
<li data-bind="text: Name"></li>
<!-- /ko -->
</ul>
Ref: http://knockoutjs.com/documentation/foreach-binding.html

Refreshing list after ajax call with Knockout JS

I have a list of attachments on a page which is generated using a jQuery $.ajax call and Knockout JS.
My HTML looks like (this is stripped back):
<tbody data-bind="foreach: attachments">
<tr>
<td data-bind="text: Filename" />
</tr>
</tbody>
I have a function that gets the list of attachments which is returned as a JSON response:
$(function () {
getFormAttachments();
});
function getAttachments() {
var request = $.ajax({
type: "GET",
datatype: "json",
url: "/Attachment/GetAttachments"
});
request.done(function (response) {
ko.applyBindings(new vm(response));
});
}
My view model looks like:
function vm(response) {
this.attachments = ko.observableArray(response);
};
There is a refresh button that the use can click to refresh this list because over time attachments may have been added/removed:
$(function () {
$("#refresh").on("click", getAttachments);
});
The initial rendering of the list of attachments is fine, however when I call getAttachments again via the refresh button click the list is added to (in fact each item is duplicated several times).
I've created a jsFiddle to demonstrate this problem here:
http://jsfiddle.net/CpdbJ/137
What am I doing wrong?
Here is a fiddle that fixes your sample. Your biggest issue was that you were calling 'applyBindings' multiple times. In general you will call applyBindings on page load and then the page will interact with the View Model to cause Knockout to refresh portions of your page.
http://jsfiddle.net/CpdbJ/136
html
<table>
<thead>
<tr><th>File Name</th></tr>
</thead>
<tbody data-bind="foreach: attachments">
<tr><td data-bind="text: Filename" /></tr>
</tbody>
</table>
<button data-bind="click: refresh">Refresh</button>
javascript
$(function () {
var ViewModel = function() {
var self = this;
self.count = 0;
self.getAttachments = function() {
var data = [{ Filename: "f"+(self.count*2+1)+".doc" },
{ Filename: "f"+(self.count*2+2)+".doc"}];
self.count = self.count + 1;
return data;
}
self.attachments = ko.observableArray(self.getAttachments());
self.refresh = function() {
self.attachments(self.getAttachments());
}
};
ko.applyBindings(new ViewModel());
});
--
You may also want to look at the mapping plugin - http://knockoutjs.com/documentation/plugins-mapping.html. It can help you transform JSON into View Models. Additionally it is able to assign a property to be the "key" for an object... this will be used to determine old vs new objects on subsequent mappings.
Here is a fiddle I wrote a while back to demonstrate a similar idea:
http://jsfiddle.net/wgZ59/276
NOTE: I use 'update' as part of my mapping rules, but ONLY so I can log to the console. You would only need to add this if you wanted to customize how the mapping plugin updated objects.

Categories

Resources