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>
Related
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.
I am working on a web app where non-profit organizations can create a profile and be easily searchable by various parameters. In the "create and organization" form, I have a nested array where the organization can add donations that they need. The array is storing ok (I can add multiple donations), however when I try to display it using ng-repeat, nothing renders. When I don't use the ng-repeat and just display via {{ ctrl.organization.donations }} the information shows up with brackets and quotation marks.
Here is the code that I use to add the donations (via the newOrganization controller):
function NewOrganizationController(OrganizationService, CategoryService, $stateParams, $state, $http, Auth){
var ctrl = this;
CategoryService.getCategories().then(function(resp) {
ctrl.categories = resp.data;
});
ctrl.donations = [{text: ''}];
Auth.currentUser().then(function(user) {
ctrl.user = user;
})
ctrl.addNewDonation = function() {
var newDonation = ctrl.donations.length+1;
ctrl.donations.push({text: ''});
};
ctrl.removeDonation = function() {
var lastItem = ctrl.donations.length-1;
ctrl.donations.splice(lastItem);
};
ctrl.addOrganization = function() {
var donations = this.donations;
var allDonations = [];
for (var key in donations) {
if (donations.hasOwnProperty(key)) {
var donation = donations[key].text;
allDonations.push(donation);
}
}
var data = {
name: ctrl.organization.name,
description: ctrl.organization.description,
address: ctrl.organization.address,
donations: allDonations.join("/r/n"),
category_id: this.category.id
};
OrganizationService.createOrganization(data);
$state.go('home.organizations');
};
}
angular
.module('app')
.controller('NewOrganizationController', NewOrganizationController);
Here is the code that I am using to display the array on my show page (this is what shows up with brackets, i.e. donations needed: ["food", "clothing"]):
<h5>{{ ctrl.organization.donations }}</h5>
This is the ng-repeat code that is not rendering anything to the page:
<li class="list-group-item" ng-repeat="donation in donations track by $index">
{{ donation }}
</li>
I've tried to use .join(', ') within the {{donation}} brackets, but this isn't recognized as a function.
edit: After taking AJ's suggestion here is a screenshot of what appears...anyone know how to fix this?
seems that my array is showing up in table form, with each row containing one character
Any help would be greatly appreciated. Here is a link to the github repo in case you want to look at anything else or get a bigger picture.
You need to use the same variable name that works in the h5
<li class="list-group-item" ng-repeat="donation in ctrl.organization.donations track by $index">
{{ donation }}
</li>
In angular, I am trying to keep my page realtime by polling a REST service(hosted locally) and update my array with the new retrieved content like this:
JS
angular.module("WIMT").controller('overviewController', function ($scope,$interval,$http){
var reg = this;
var promise;
reg.teacherInfoList = [];
reg.dayfilter = "";
$scope.start = function() {
$scope.stop();
promise = $interval( $scope.longPolling, 3000);
};
$scope.stop = function() {
$interval.cancel(promise);
};
$scope.longPolling = function(){
reg.teacherInfoList.length = 0;
$http({
method: 'GET',
url: 'api/schedules/' + "TPO01"
}).then(function onSuccessCallback(response) {
reg.teacherInfoList[0] = response.data;
console.log(reg.teacherInfoList[0]);
$scope.start();
}, function errorCallback(response) {
$scope.start();
});
}
$scope.start();
});
HTML
<div ng-controller="overviewController as oc">
<ul>
<li ng-repeat="teachInfo in oc.teacherInfoList ">
{{teachInfo.fullname}}
<div ng-repeat="day in teachInfo.days | filter: oc.dayfilter">
Today is: {{day.day}} {{day.date}}
<ul ng-repeat="roster in day.entries">
<li>
Name: {{roster.name}}
</li>
<li>
Start: {{roster.start}}
</li>
<li>
End: {{roster.end}}
</li>
<li>
Note: {{roster.note}}
</li>
</ul>
</div>
</li>
</ul>
The code used as above causes flickering:
reg.teacherInfoList[0] = response.data;
This code also causes flickering:
reg.teacherInfoList.splice(0,1);
reg.teacherInfoList.splice(0,0,response.data);
I have also tried to apply this to my ng-repeats:
ng-cloack
And applied this to my ng-repeats
track by $index
I have also read this:
How does the $resource `get` function work synchronously in AngularJS?
Now, I know that when I replace my array for a brief second the array is empty causing it to flicker, but I can't think of a solution to solve this problem. What would be the best way to solve this?
reg.teacherInfoList.length = 0;
Not sure if emptying the array is necessary here.
I believe the teacherInfoList array is empty for the entire duration of the request, causing it to render as blank.
You could try either removing (or commenting out) the line above or moving it into the top of the callback function of the GET request like
}).then(function onSuccessCallback(response) {
// applied here
reg.teacherInfoList.length = 0;
reg.teacherInfoList[0] = response.data;
console.log(reg.teacherInfoList[0]);
$scope.start();
}, function errorCallback(response) {
//and here
reg.teacherInfoList.length = 0;
$scope.start();
});
I am new to automated testing, Protractor, and angularJS. I have a list that I would like to count, copy to an array maybe, and verify the list text is present. For example The list shows Attractions, Capacity, and Content to the user so they know what privileges they have.
Below is the .html
<div class="home-info">
<div class="home-top home-section">
<h3>User Information</h3>
<div class="home-box">
<div class="property-group wide">
<span>
Change Phillips<br />
</span>
</div>
</div>
<div class="home-box">
<div class="property-group wide">
<div>Editors:</div>
<span>
<ul class="property-stack">
<li><span>Attractions</span>
</li>
<li><span>Capacity</span>
</li>
<li><span>Content</span>
</li>
<li><span>Media</span>
</li>
<li><span>Options</span>
</li>
<li></li>
<li></li>
<li><span>Upload CADs</span>
</li>
</ul>
</span>
</div>
</div>
</div>
Below is the code I have written. I can get the first item on the list however using .all isn't working for me.
var text = "";
browser.driver.findElement.all(By.xpath("//li/span")).count().then(function(count) {
initialCount = count;
console.log(initialCount);
});
browser.driver.findElement(By.xpath("//li/span")).getText().then(function(text) {
console.log(text);
});
I'm trying to avoid using xpath as I was told to try and avoid. To be honest Im lost. Thanks for the help in advance.
Code used for matching:
expect(myLists).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);
I am not sure what version of protractor you're using but you should be able to just call element without the browser or driver prefix. Using element.all should get you the array of of elements you're looking for.
If you want to access specific indexes within that array you can use the .get(index) suffix to the element.all
So below:
1. you get the array of the elements
2. you get the count of the array
3. we call a for loop to iterate through all the indexes of the array
4. each index of the array we call the getText() and print it to the console
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
for(int i = 0; i < count; i++){
myLists.get(i).getText().then(function(text) {
textList[j++] = text;
console.log(text);
});
}
});
EDIT:
In researching I actually found another way to iterate through the array of elements by using the .each() suffix to the element.all.
var j = 0; // using this since the i iterator in the for loop doesn't work within a then function
var textList = [];
var text = "";
var myLists = element.all(by.css("li span"));
myLists.count().then(function(count) {
console.log(count);
myLists.each(function(element, index) {
element.getText().then(function (text) {
textList[j++] = text;
console.log(index, text);
});
});
});
you should be able to use the textList array to match things.
expect(textList).toEqual(['Attractions', 'Capacity', 'Conent',
'Media', 'Options', 'Upload CADs'
]);
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