expression syntax within ng-click - javascript

I have 2 variables wkidx and dyidx
I am trying to create multiple collapseable elements on the same page using the angular ui bootstrap directive and I have the following element
<a
href=""
class=""
ng-click="isCollapsed{{wkidx}}{{dyidx}} = !isCollapsed{{wkidx}}{{dyidx}}">
Blah
</a>
now this outputs what I want it to to the dom (ie it appends the variable values to iscollapsed) but I get a syntax error like so
Syntax Error
error in component $parse
Syntax Error: Token 'wkidx' is at column {2} of the expression [{3}] starting at [{4}].
I tried reading the page that the error page links to but it didn't really make things clearer for me. I have tried numerous variations and they either dont give an error but dont append the values or they append the values but throw a syntax error
thankyou for the help here is were I am at but I am getting errors for probably obvious reasons
html
<div
ng-repeat="session in day.sessions"
ng-init="ssnidx = $index">
<a
href=""
class=""
ng-click="setCollapsed(!isCollapsed(wkidx, dyidx), wkidx, dyidx)">
</a>
<div
collapse="I DONT KNOW WHAT SHOULD GO HERE">
<p>hello from the collapsed div</p>
</div>
</div>
app.js
$scope.setCollapsed = function(value, wkidx, dyidx) {
};
$scope.isCollapsed = function(wkidx, dyidx) {
if (isCollapsed + wkidx + dyidx) {
return true;
} else {
return false;
}
};

This is not a valid expression for ng-click. You'd better to create to methods in your $scope:
setCollapsed(value, wkidx, dyidx) and isCollapsed(wkidx, dyidx)
function MyController($scope) {
$scope.setCollapsed = function(value, wkidx, dyidx) {
//...
};
$scope.isCollapsed = function(wkidx, dyidx) {
//...
};
}
<a
href=""
class=""
ng-click="setCollapsed(!isCollapsed(wkidx, dyidx), wkidx, dyidx)>
Blah
</a>
Assuming, that wkidx and dyidx are atached to $scope
Answering to your updated question:
<div
ng-repeat="session in day.sessions"
ng-init="ssnidx = $index">
<a
href=""
class=""
ng-click="setCollapsed(!isCollapsed(wkidx, dyidx), wkidx, dyidx)">
</a>
<div
ng-show="isCollaspsed(wkidx, dyidx)">
<p>hello from the collapsed div</p>
</div>
</div>

Related

Passing parameter to ng-click function with ng-repeat

Please see code below:
<div>
<div class="list-group">
<a class="list-group-item" ng-click="c.selectItem(note.sys_id)"
ng-repeat="note in data.notes">
<h4 class="list-group-item-heading">
{{note.title}}
</h4>
<p class="list-group-item-text">
{{note.sys_id}}
</p>
</a>
</div>
</div>
in the output I can see that note.sys_id is getting printed. However, I need to pass this to the ng-click function on top. I tried below code, but no result:
ng-click="c.selectItem(note.sys_id)"
ng-click="c.selectItem({{note.sys_id}})"
https://jsfiddle.net/1q8mdb3z/
ng-click="c.selectItem(note.sys_id)"
should work for you. Make sure your function is correct. I'd assume your scope is something to the effect
$scope.c = {
selectItem = function(id) {
// do something with id
}
}

Why can't I access the new DOM element created by Angular?

HTML:
<div class="list-group link-list" ng-show="linksForPerson">
<a href="" class="list-group-item" ng-repeat="link in linksForPerson" ng-click="showLinkDetail(link)" ng-class="{active: isSelectedLink(link)}">
<h4 class="list-group-item-heading">[[ link.engine.name ]]</h4>
<p class="list-group-item-text">[[ link.engine.base_url ]]</p>
<p class="list-group-item-text" ng-show="link.user_sync_id">[[ link.user_sync_id ]]</p>
<p class="list-group-item-text" ng-show="link.group_sync_id">[[ link.group_sync_id ]]</p>
</a>
<span class="glyphicon glyphicon-plus"></span> Add a new link
</div>
Controller:
appModuleLightDashboard.controller('ManageLinksController',
function($scope, $http, $timeout) {
$scope.addLink = function(event) {
$scope.linksForPerson.push({});
// Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.0-rc.1/$rootScope/inprog?p0=%24apply
$('.link-list .list-group-item').eq(-2).trigger('click');
// But this works ---- why?
// $timeout( function(){$('.link-list .list-group-item').eq(-2).trigger('click')} , 0);
}
});
I have changed the interpolate symbol to [[]] as it conflicts with Django
The problem:
A new list item will be created when the user clicks on the "Add a new link". I wanted to select this new list item automatically.
But it looks like I couldn't select that new DOM element created by Angular ( i.e. $('.link-list .list-group-item') doesn't return the new one ), unless I wrap the code with $timeout. Anyone knows why?
Also, please advise if there is a more Angular way to achieve it:)
Your question is "why". The answer is because at the moment you are trying to use jQuery to find the element, it hasn't yet been added to the DOM. That doesn't happen until the digest cycle runs.
$timeout works because the function call is now deferred until after the next digest cycle. The problem with that solution is that there are cases where the DOM still won't yet have been modified.
Looking in more detail, this will have several failure modes. The error you are showing is sent because you are actually triggering a click in the second to last element already added, and you are doing it from inside of a digest cycle. If you already have two or more items added to the collection, this triggers angular's ng-click on the second to last one (which happens to not be the one you think), which assumes it is called outside of a digest cycle and calls $apply, which fails with the error you see because it's actually inside of a digest cycle.
The "angular way" to achieve what you want is to use a directive.
.directive('triggerClick', function($parse) {
return {
restrict: 'A',
link: function(scope, elem, attr) {
var fn = $parse(attr['triggerClick']);
if(scope.$last) { //or some other logic
fn(scope);
}
}
}
})
div class="list-group link-list" ng-show="linksForPerson">
<a href="" class="list-group-item" ng-repeat="link in linksForPerson" ng-click="showLinkDetail(link)" ng-class="{active: isSelectedLink(link)}" trigger-click="showLinkDetail(link)">
<h4 class="list-group-item-heading">[[ link.engine.name ]]</h4>
<p class="list-group-item-text">[[ link.engine.base_url ]]</p>
<p class="list-group-item-text" ng-show="link.user_sync_id">[[ link.user_sync_id ]]</p>
<p class="list-group-item-text" ng-show="link.group_sync_id">[[ link.group_sync_id ]]</p>
</a>
<span class="glyphicon glyphicon-plus"></span> Add a new link
</div>
This works because the link function of the directive will be called after the node has been constructed and added to the DOM. Note the addition of "trigger-click" to your ng-repeat element.
elem in the directive is a jQuery object wrapped around the instance of the ng-repeat item. Angular will call the link function for every instance of the directive, which in this case is every instance of the ng-repeat.
Even more "angular" would be to not use a click event at all. You don't include the implementation of showLinkDetail, but rather than trigger a click, just call it in your controller.
As a general "angular" rule, anything that looks like jQuery should only happen in a directive.
EDIT: With more info on what you need, you can do this without need to do any DOM manipulation at all (no directives).
appModuleLightDashboard.controller('ManageLinksController',
function($scope, $http, $timeout) {
$scope.activeLink = undefined;
$scope.addLink = function(event) {
$scope.activeLink = {};
$scope.linksForPerson.push($scope.activeLink);
}
$scope.showLinkDetail = function(link){
$scope.activeLink = link
}
$scope.isSelectedLink = function(link){
return $scope.activeLink === link;
}
});
<div class="list-group link-list" ng-show="linksForPerson">
<a href="" class="list-group-item" ng-repeat="link in linksForPerson" ng-click="showLinkDetail(link)" ng-class="{active: isSelectedLink(link)}">
<h4 class="list-group-item-heading">[[ link.engine.name ]]</h4>
<p class="list-group-item-text">[[ link.engine.base_url ]]</p>
<p class="list-group-item-text" ng-show="link.user_sync_id">[[ link.user_sync_id ]]</p>
<p class="list-group-item-text" ng-show="link.group_sync_id">[[ link.group_sync_id ]]</p>
</a>
<span class="glyphicon glyphicon-plus"></span> Add a new link
</div>
you should not put your "add new link" inside the div with ngShow because when the linksForPerson array is empty, you will not be able to add a new link . Also, putting it outside the div will ease up every other manipulation (based on what you want to achieve"
linksForPerson is an array, use ng-show="linksForPerson.length" instead
you should initialize your arrays before pushing anything into it $scope.linksForPerson=[]
use of ng-bind is a better alternative to {{}} or [[]]
I refactored your code.
// ---- controller
appModuleLightDashboard.controller('ManageLinksController',
function($scope, $http, $timeout) {
var activeLink;
// you should initiate your array
$scope.linksForPerson = [];
$scope.isSelectedLink = function (link) {
return activeLink === link;
};
$scope.addLink = function(event) {
activeLink = {
engine: {
name : "engine" + ($scope.linksForPerson.length + 1),
base_url : " someUrl"
}
};
$scope.linksForPerson.push(activeLink);
};
});
and html (note use of ng-bind)
<div ng-controller="ManageLinksController">
<div class="list-group link-list" ng-show="linksForPerson.length">
<a href="#" class="list-group-item" ng-repeat="link in linksForPerson" ng-click="showLinkDetail(link)" ng-class="{active: isSelectedLink(link)}">
<h4 class="list-group-item-heading" ng-bind="link.engine.name"></h4>
<p class="list-group-item-text" ng-bind="link.engine.base_url"></p>
<p class="list-group-item-text" ng-show="link.user_sync_id" ng-bind="link.user_sync_id"></p>
<p class="list-group-item-text" ng-show="link.group_sync_id" ng-bind="link.group_sync_id"></p>
</a>
</div>
<span class="glyphicon glyphicon-plus"></span> Add a new link
</div>
here's jsfiddle for you to play with

KnockoutJS - foreach not working with a single entry in viewmodel

I have the following code that, on a successful AJAX return, displays a popup window with a list of addresses. The knockout version is 2.3.0.
If there is more than 1 address then the html correctly renders with a 'display' string.
The problem is that if there is ONLY 1 address the html list renders but without any text in the span.
In both cases the view model is correctly being populated with data so it looks to me like a problem updating the html.
I have tried pushing the data again and although I can use jQuery to update the html but this doesn't help me understand the problem.
HTML
<div id="reverseGeocodingResults">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Local Addresses</h4>
</div>
<div>
<ul data-bind="foreach: vm.reverseGeocodingViewModel.AddressList" class="locationList">
<li class="locationListItem" data-bind="click: SubmitAddressRequest">
<div>
<span data-bind="text: Display"></span>
</div>
</li>
</ul>
</div>
</div>
</div>
Javascript (from a separate file so only taken what I think is relevant)
var vm;
var masterViewModel = function () {
this.viewModel = { LocationList: ko.observableArray([]), SubQuery: ko.observable() };
this.reverseGeocodingViewModel = { AddressList: ko.observableArray([]) };
};
function SubmitReverseGeocodingRequest(easting, northing, projectId, mouseLocation) {
$.post('url?action=ReverseGeocodingLookup', {
easting: easting,
northing: northing,
pid: projectId
})
.done(function (data) {
spinner.stop();
if (parseInt(data.NumberOfAddressesFound) > 0) {
if (data.AddressList.length == 1) {
alert('just 1 address');
}
// remove all array items before adding new
// Not pretty but gets around an issue the UI seems to have displaying the updated list
if (vm.reverseGeocodingViewModel.AddressList().length > 0) {
vm.reverseGeocodingViewModel.AddressList.splice(0, vm.reverseGeocodingViewModel.AddressList().length);
}
vm.reverseGeocodingViewModel.AddressList(data.AddressList);
}
});
)
$(document).ready(function () {
vm = new masterViewModel();
ko.applyBindings(vm);
})
HTML Result - Multiple Results
<ul class="locationList" data-bind="foreach: vm.reverseGeocodingViewModel.AddressList" style="height: 265px;">
<li data-bind="click: SubmitAddressRequest" class="locationListItem">
<div>
<span data-bind="text: Display">Yates Wine Lodge, SWINDON</span>
</div>
</li>
<li data-bind="click: SubmitAddressRequest" class="locationListItem">
<div>
<span data-bind="text: Display">The Brunel Centre, SWINDON</span>
</div>
</li>
</ul>
HTML Result - Single Result
<ul class="locationList" data-bind="foreach: vm.reverseGeocodingViewModel.AddressList" style="height: 265px;">
<li class="locationListItem" data-bind="click: SubmitAddressRequest">
<div>
<span data-bind="text: Display"></span>
</div>
</li>
</ul>
I have looked at various previous questions on this topic and couldn't come up with an answer hence the question, but please point me to another post if I have missed one.
to remove all before add new use reverseGeocodingViewModel.AddressList.removeAll()
Maybe didn't find it but you have to parse data, $.parseJSON I use for this.
Try to use
$.each($.parsejSON(data), function(i, el){
reverseGeocodingViewModel.AddressList.push(el);
})
I hope it will help you
I've noticed following issues:
function SubmitReverseGeocodingRequest should be closed with } not )
foreach: vm.reverseGeocodingViewModel.AddressList should be foreach: reverseGeocodingViewModel.AddressList, without vm because vm is already binded
vm = new masterViewModel();
ko.applyBindings(vm);
To clean AddressList use vm.reverseGeocodingViewModel.AddressList([]) instead vm.reverseGeocodingViewModel.AddressList.splice(0, vm.reverseGeocodingViewModel.AddressList().length);
Note: Here's possible mistake
<li class="locationListItem" data-bind="click: SubmitAddressRequest">
so AddressList item have to have the SubmitAddressRequest function. I don't think that you get data with the function
I've created test sample based on your code, take a look here

Running click functions on every instance of listing instead of current

I have a listing of articles here, and I can't figure out how to execute the ng-click function calls on every new article inside the ng-repeat. Right now it works for existing articles, but when new articles are added dynamically (via AJAX), I need those to have the same functionality too.
For example: the ng-click function calls on the "+" sign to reveal social buttons seem to not work once new articles are inserted via AJAX (ie: delete articles, and let list be populated again with new elements)
Does AngularJS provide any tools to do that?
<div>
<div>
<input type="text" ng-model="search">
<span>{{filtered.length}} article(s)</span>
</div>
<div article-listing ng-repeat="article in filtered = (wikiArticles | filter:search)">
<!--Individual article begin-->
<span>
{{article.title}}
</span>
<div>
<a ng-click="articles.removeArticle($index)" title="Delete">
<span>✖</span>
</a>
<a ng-click="articles.toggleShare(article)">
<span class="plus-sign" title="Share">✖</span>
<div social-share ng-show="article.socialShare">
<div ng-click="socialShare = !socialShare" class="addthis_toolbox addthis_default_style addthis_32x32_style"
addthis:title="{{article.title}}" addthis:description="{{article.extract}}" addthis:url="{{article.url}}">
<a class="addthis_button_facebook"></a>
<a class="addthis_button_twitter"></a>
<a class="addthis_button_google_plusone_share"></a>
<a class="addthis_button_reddit"></a>
<a class="addthis_button_hackernews"></a>
</div>
</div>
</a>
</div>
<div>{{article.extract}}</div>
<!--Individual article end-->
</div>
</div>
Code for ng-click calls that don't seem to work for new article insertions
$scope.articles = (function() {
return {
shuffleArticles : function() {
$scope.wikiArticles.reverse();
},
removeArticle : function(index) {
$scope.wikiArticles.splice(index, 1);
$scope.fireAPICalls();
},
toggleShare : function(currArticle) {
var previousState = currArticle.socialShare;
angular.forEach($scope.wikiArticles, function(article) {
article.socialShare = false;
});
currArticle.socialShare = previousState ? false : true;
}
}
})();
Your ng-click calls are actually working- you can watch the ng-show toggle in the debugger.
The problem is that there is nothing to display on the new items you add.
The articles you initially add all have their icons populated with the .addthis classes, for instance here's your Facebook icon element:
<a class="addthis_button_facebook at300b" title="Facebook" href="#">
<span class=" at300bs at15nc at15t_facebook">
<span class="at_a11y">Share on facebook</span>
</span>
</a>
at300bs includes the following css which displays the image:
background: url(widget058_32x32.gif) no-repeat left!important;
However as you add new items, you aren't including the needed .addthis classes to them. Their elements look like this:
<a class="addthis_button_facebook"></a>
So ng-show has nothing to display (it shows a 0x0 div).
Add the .addthis classes to your new elements as you add them and you'll be all set.

angular ng-class does not react on click

I am trying to use ng-class and bind a class to an expression so, that I can unit test the expression binding. But, it seems that I am missing something.
The button:
<li><a class="" ng-click="onAddInterface()"><i class="icon-plus-sign"></i> add interface </a></li>
The panel that should collapse and expand:
<div class="collapse" ng-class="{in:showCreateNewInterfacePanel}"><div>
the function that is fired
$scope.onAddInterface=function(){
$scope.showCreateNewInterfacePanel=true;
}
anyway clicking the link nothing happens.
Am I missing something?
I'm not sure if this is how you are really defining your $scope.onAddInterface function or if it is just an example... Nevertheless you should be doing it like this:
$scope.onAddInterface = function() {
$scope.showCreateNewInterfacePanel = true;
}
update
Also make sure the link and the collapsible element are under the same $scope/Controller:
<div ng-controller="Ctrl">
...
<a ng-click="onAddInterface()">add interface</a>
...
<div class="collapse" ng-class="{in:showCreateNewInterfacePanel}"><div>
...
</div>
Controller:
function Ctrl($scope) {
$scope.onAddInterface = function() {
$scope.showCreateNewInterfacePanel = true;
}
}​
I had a very similar problem and was able to solve this problem by placing 'in' in quotes. It is a string, and not a variable.
...
<div class="collapse" ng-class="{'in':showCreateNewInterfacePanel}"><div>
...

Categories

Resources