Adding new form fields dynamically in ngView - Angularjs - angularjs

I use routing to load different templates into a ngView. One of the templates has a simple controller which contains an array of contacts. What I'm trying to do here is as simple as by clicking a link (ngclick) call a function and add a new object into the array which I expect will be reflected in my UI.
It's something like this:
$scope.contacts = [{name='', email=''}];
<li ng-repeat="con in contacts">
<input type="text" ng-model="con.name"/>
<input type="email" ng-model="con.email"/>
</li>
<li>
<a href ng-click="addContact()">add</a>
</li>
$scope.addContact = function() {
$scope.contacts.push( {name='', email=''} ); //-- i can use either $scope or this to reference the array and works.
}
So, the UI renders well with the initial value, the addContact function is invoked on click and I see the value is pushed (length = 2) but then the function ends the array seems to be reset to one element (lenght = 1) after angularjs evaluation.
I'm not sure if this is occurring because I use ngView. I mean, I reviewed this example (http://code.angularjs.org/1.0.3/docs/api/ng.directive:ngController) and I don't see much differences of what I'm trying to do here, the diff is that I use routing with ngView.
fiddle: http://jsfiddle.net/fdDph/
Help is much appreciated.

In your Fiddle, you are resetting the array length to 1 in the ng-show:
<span ng-hide="contacts.length = 1">
Do this and it will work:
<span ng-hide="contacts.length == 1">

{name='', email=''} is wrong syntax btw, it should be {name:'', email:''}

Related

How to create an independent indexer with default value inside ngRepeat loop

I have a list of posts with comments. I need to traverse through array of comments inside each of the posts on my page. I'm trying to create variable inside ngRepeat for posts which I can use like an indexer to display exact comment for each post. Due to ngRepeat creating nested scope, this variable must be unique for each iteration. But when I'm trying to change it with ng-click, it doesn't change.
My ngRepeat:
<div class="question_block col-xs-12"
ng-repeat="answer in question.content.answer track by answer.id">
is followed by <span style="display:none">{{counter=0}}</span>. And then I'm showing some items like <span>{{answer.comments[counter].user.organization.title}}</span>. When I'm trying to do something like <a href ng-click="counter++">Increment</a> nothing happens. What's the matter ?
Use ng-init="counter = 0" and attach your ng-click to a function in your controller:
<div ng-repeat="item in items track by $index" ng-init="counter=0">
{{item}} ({{counter}})
<button ng-click="clickHandler()">Increment</button>
<hr />
</div>
Then, to increment counter in the context of the event, use this.counter
$scope.clickHandler = function() {
this.counter++;
};
Plunker demo : http://plnkr.co/edit/J4d0JlJSKD7i4OfMT2r4?p=preview

Removing items from an array with Angular

Yes, it has been asked before, and I've read all the answers but nothing seems to work. So I'm asking for an extra pair of eyes to see if you can find any singularity in my code that is making it not work as it should. (I tried this code and logic somewhere else and it seems to work fine). No errors in the console by the way
I am simply trying to remove an item from the view when someone clicks the x on the picture.
Here is the controller
app.controller('galleryController', ['$scope', '$http', function($scope, $http) {
$http.get('data/galleries.json').success(function(data){
$scope.galleries = data;
}).error(function(error) {
console.log(error);
});
$scope.removeGalleryItem=function(gallery){
var removedGallery = $scope.galleries.indexOf(gallery);
$scope.galleries.splice(removedGallery, 1);
};
}]);
and my view
<div class="col-xs-12 col-md-3" ng-repeat="gallery in galleries" >
<a class="gallery-item" ng-href="{{gallery.img}}" ng-class="{true:'active',false:''}[checked]"
title="Nature Image 1" >
<div class="image">
<img ng-src="{{gallery.img}}" alt="Nature Image 1"/>
</div>
<div class="meta">
<strong>{{gallery.title}}</strong>
<span>{{gallery.desc}}</span>
</div>
</a>
<ul class="gallery-item-controls">
<li><label class="check"><input type="checkbox" class="icheckbox" ng-model="checked" /></label></li>
<li><span class="gallery-item-remove"><i class="fa fa-times" ng-click="removeGalleryItem(gallery)"></i></span></li>
</ul>
</div>
Angular 1.5.8
Thanks
You can pass an $index in your click function like this.
<i class="fa fa-times" ng-click="removeGalleryItem(galleryItem, $event , $index)">
and use $scope.galleries.splice(index, 1); inside your click function removeGalleryItem, make sure you have index parameter too like this.
$scope.removeGalleryItem = function(gallery , event, index){
$scope.galleries.splice(index, 1);
};
Hope this helps..
After doing some research I think the problem is that galleryController is defined somewhere in your markup but the elements in the gallery are not inside of where that controller is defined.
Referring to http://joli.sitedev.online/#/gallery. In file slidesController.js where galleryController is defined I put a break here and the code pauses:
I also put a break point here but the code does not pause when clicking on a delete button:
Looking at the markup I can't see any sign of ng-controller="galleryController" so I can't see how galleries in the ng-repeat is populated:
Maybe it is through this:
If it is through that then it would explain things as that directive has its own controller. Any further information would help and I'm sure we can clear this up.
If I understood correctly your question, if you want to delete a particular element both from DOM and Array containing these particular elements you can do the following:
<!-- Intercept that particular Element with $event-->
<i class="fa fa-times" ng-click="removeGalleryItem(galleryItem, $event)">
Lets supposing you are repeating some galleryItems and they have a name property.
And on your controller:
$scope.removeGalleryItem(galleryItem, $event){
//Save galleryItem Name
var itemName = $($event.currentTarget).name(); // if it has it on html
var itemName = galleryItem.name; // if it has a property
//Get the target and remove it
$($event.currentTarget).remove();
//Using lodash, loop through your array and remove that exact object from it.
//Ofc you can do a normal loop through it.
$scope.galleries = _.remove($scope.galleries, function(n) {
return n != itemName;
});
//Then, if the change does not happen in your DOM
$scope.$apply();
}
Hope I've been helpful.
I have made some changes to fix this issue and you can check it at this link.
The problem here was that there was a typo in the html snippet that was calling the removeGalleryItem(galleryItem, $event). The parameter name should have been gallery, not galleryItem, since there is no such object by the name galleryItem, hence inside this method, the parameter value was undefined. Once I fixed it, I was able to get the gallery object within the removeGalleryItem method and the following code worked absolutely fine:
$scope.removeGalleryItem=function(gallery){
var selectedGallery = $scope.galleries.indexOf(gallery);
$scope.galleries.splice(selectedGallery, 1);
};
Also note that, I have removed the $event attribute from the method declaration and from the html method call as we didn't need it in the above mentioned approach.
<i class="fa fa-times" ng-click="removeGalleryItem(gallery)"></i>

Trigger a click on first item of ng-repeat, initially and each time new data is loaded

I'm very very new to Angular JS and have the following requirement:
I have an ng-repeat as shown below:
<div class="panel" ng-repeat="(appname, value) in chart.accordionData" style="margin-top: 0;">
<div class="accordion collapsed" data-parent="#accordion1"
data-toggle="collapse"
data-target="#{{appname.replace(' ','')}}">
<div class="accordion-head" initial-select index="{{$index}}">
<div class="arrow chevron"></div>
<h4><i></i>{{appname}}</h4>
</div>
</div>
<div class="accordion-body collapse" id="{{appname.replace(' ','')}}">
<p class="highlightable" >
Some data
</p>
</div>
</div>
The data in this ng-repeat comes from the server.
So the problem statement I have is to perform a click trigger on the first element under ng-repeat, .accordion-head so that the first item in the list is always open. I tried various approaches of putting $watch etc. but when the number of items in the list are same, then the trigger doesn't fire.
(the first item in list has to be clicked even when new data is loaded)
I thought of writing a directive initial-select and perform click based on index but that happens only once. I really need an experts advice.
Any solutions?
I think the simplest option is to use data-ng-init="collapse = !$first;".
Another option:
Have you used Angular UI Bootstrap? Also, are you using $http or $resource to get data from the server?
If you use $resource, you can set a variable on the success callback (interceptor response) to open=true. Then, you can either bind this variable to Angular UI Bootstrap is-open attribute or to your data-toggle. Also, with $resource you can set data-ng-if="chart.accordionData.$resolved" (or chart.$resolved depending on your setup), which is nice.
You can use a watch to watch the array contents.
Check out the following fiddle:
http://jsfiddle.net/akonchady/w0qdsen7/3/
I have used a variable to keep track when the array content changes since directly watching the array will be more memory intensive.
Here is the main code that watches the variable:
$scope.$watch('arrModified', function(newValue, oldValue) {
if(newValue) { //array is modified
//Trigger ng-repeat div click
setTimeout(function() {
$('.item:first').trigger('click');
$scope.arrModified = false;
$scope.$digest();
}, 0);
}
});

AngularJS's ng-model icm textarea

I'm trying to add some text to the last cursor place after clicking a button.
In the controller:
$scope.addEmoji = function(name){
var element = $("#chat-msg-input-field");
element.focus(); //ie
var selection = element.getSelection();
var textBefore = $scope.chatMsg.substr(0, selection.start);
var textAfter = $scope.chatMsg.substr(selection.end);
$scope.chatMsg = textBefore + name + textAfter;
}
$scope.updateChatMsg = function(chatMsg){
$scope.chatMsg = chatMsg;
}
$scope.sendChatMsg = function(){
var backend = $scope.convs[$scope.active.conv].backend.name;
$scope.view.addChatMsg($scope.active.conv, $scope.active.user, $scope.chatMsg,new Date().getTime() / 1000, backend);
Chat[backend].on.sendChatMsg($scope.active.conv, $scope.chatMsg);
$scope.chatMsg = '';
};
And then some HTML:
<div class="chat-msg-button" >
<button ng-click="view.toggle('emojiContainer')" ><img src="/apps/chat/img/emoji/smile.png"></button>
</div>
<form id="chat-msg-form" ng-submit="sendChatMsg()">
<div class="chat-msg-button" >
<button type="submit"><div class="icon-play"> </div></button>
</div>
<div id="chat-msg-input">
<textarea id="chat-msg-input-field" autocomplete="off" type="text" ng-model="chatMsg" ng-change="updateChatMsg(chatMsg)" placeholder="Chat message"></textarea>
<div>{{ chatMsg }}</div>
</div>
</form>
What I'm trying to achieve: a user types some text in the textarea => $scope.chatMsg gets the value of the textarea. Now the user press one of the button's => the name of the button is added to the latest cursor position. (it's no problem to find the latest cursor position)
The problem
There is a difference between the value of $scope.chatMsg, {{ chatMsg }} inside the div and the text in the textarea.
The contents of the textarea and the div stays always the same. But when pressing the button the name is added to $scope.chatMsg but the contents of the textarea isn't changed...
How can I solve this?
TIA
First of all, you're mixing jQuery with AngularJS, it doesn't look like you need jQuery here that much.
Also, your chat message is updated in 3 different functions, so you need some debugging to see which are fired.
In general:
To solve your issue, try some more debugging, do a
$scope.$watch($scope.chatMsg, function(){
console.log($scope.chatMsg);
});
this will watch all changes to chatMsg. Add console.log() to each of your functions and you can watch which is fired.
Also, rather than using {{ }} inside your div just use ng-bind since that text is the only item in your div, it's cleaner if your app crashes somewhere.
// change from
<div>{{ chatMsg }}</div>
// to
<div ng-bind="chatMsg "></div>
Update: after seeing your plunker, I modified it and came up with this: http://plnkr.co/edit/oNKGxRrcweiJafKCm9A5?p=preview
Your ng-repeat needs to be tracked by $index so that duplicates are displayed rather than crashing when someone creates the same message
I solved all problems. The plunkr form above works. So after investigating all scopes with the Angular chrome extension I saw that chatMsg was defined in another scope. Thus not in the scope I was trying to acces it from.
Via this question angularJS ng-model input type number to rootScope not updating I found a solution.
I added chatMsg to the fields object.

Getting the jQuery object that has been clicked on in angular via $event

I have a list like this. It is pre rendered and so cannot make use of anything attached to ng-repeat.
<li ng-class="{ 'active': 1 == selectedIndex }">
Item 1
</li>
I want to be able to toggle the class of the <li> when the a is clicked.
Looking at some other answers to similar questions on here, it seems as though there is a variable associated with ng-repeat which means you can use an $index variable to achieve this. As this list is pre-rendered this is not available and so I guess I have to do it the jQuery way.
I see that I have access to an $event object but event.target only gives me the DOM element, I would like to be able to convert it into a jQuery object. Is this possible?
You have to think about this in a different way than you would normally. You can't modify the DOM within angular like you do with plain old jquery. Here is what you should do:
<li ng-class="{ active: selectedItem == item }" ng-repeat="item in list">
{{ item.name }}
</li>
Then in your controller:
$scope.selectedItem = null; // if this is loaded from a service then you can set it after it loads.
$scope.itemSelected = function( item ) {
$scope.selectedItem = item;
}
No need to play around with indexes, jquery, or one off code.
Charlie

Resources