access an element inside ng-repeat when dynamic html loaded - angularjs

I am trying to access an DOM element inside a dymanic loaded HTML with http get request. The code that I am using inside controller is
$http.get("/ProjectManager/Edit?id=" + requestId).success(function (data, status, headers, config) {
data = data.trim();
var newDirective = angular.element(data);
angular.element(document.getElementById("editDiv")).html(newDirective);
$compile(newDirective)($scope);
var resources = angular.element(document.getElementById("projectResources")).val();
for (var i = 0; i < resources.length; i++) {
var resourceData = new ProjectResourcesModel(resources[i].ResourceID, resources[i].ProjectResourceID, resources[i].Employee, resources[i].IsApprover, resources[i].StartDate,
resources[i].EndDate)
$scope.resource.push(resourceData);
//$scope.$apply();
var id = "startDate" + resources[i].ResourceID;
angular.element(document.getElementById(id)).datetimepicker({
defaultDate: resources[i].StartDate,
format: 'MM/DD/YYYY',
})
}
})
I am trying to access a element with particular id that is present in my array element.
View HTML inside a ng-repeat is as follows
<div>
<div class="form-group">
<div class='input-group date' id='startDate{{item.ResourceID}}'>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
<input ng-model="item.StartDate" type='text' id="txtStartDate{{item.ResourceID}}" class="form-control" />
</div>
</div>
</div>
</div>
I have to attach bootstrap datetimepicker to a textbox so I want to access a div inside ng-repeat whose ID is present inside by ng-repeat array.
But when I access the item with that id inside success of get request I am not getting the element. for e.g id of div would be startDate151 after the view binding.
So I tried using $scope.$apply() with which I am able attach the datepicker to element inside ng-repeat but I started getting a console error
Error: [$rootScope:inprog] $digest already in progress
Can anyone help me to resolve this or can I use some other alternative in this situation.

Related

Getting value from ng-repeat in protractor test case

I want to add two counts in the protractor testcase from ng-repeat .But I am unable to fetch the count object via repeater.
html:
<div ng-repeat="act in actives">
<div>
<input id="checkbox-active" ng-model="act.checked"
ng-change="chooseActive(actInact)"/> {{actInact.name}}
</div>
<div><span id="active-count">{{actInact.count}}</span></div>
</div>
test.spec
this.getStateCount = function () {
var item = element.all(by.repeater('activeInactive in activeInactives'));
console.log("item"+item[0].count);
console.log("item"+item[0]);
item.getText().then(function(text){
console.log("text1+txt2"+text[0]+text[1]);
})
};
gives active 1 inactive 2 in console.
when I tried
console.log("item"+item[0].count); gives undefined
and exception is thrown.
and item is coming as object object.

Img src doesn't change when i updated the source

I have an ionic 1 app and im trying to dynamically change a img source. I thought all i had to do whas update the scope linked to this source, but it doesn't worked. Any idea on what might be wrong?
my View
<div ng-if="isList" class="item style-list" ng-repeat="(key, item) in items"
ng-click="goTo(item)">
<div class="img-container" ng-if="isList">
<m-img encode="true" src="item.image"></m-img>
</div>
<h1 ng-bind="item.title"></h1>
<p ng-bind="item.resume" ng-if="item.resume"></p>
<p ng-bind-html="stripHtml(item.description) | mCut:100" ng-if="!item.resume"></p>
</div>
my Controller
$scope.$on("update-data", function(event, args) {
$scope.items[1].description =
args.response.results[0].item.description;
$scope.items[1].id = args.response.results[0].item.id;
$scope.items[1].image = args.response.results[0].item.image;
$scope.items[1].resume = args.response.results[0].item.resume;
$scope.items[1].title = args.response.results[0].item.title;
});
My m-img component
html
<div class="thumb-size notloaded">
<div class="thumb" ng-if="imgStyle" ion-img-cache-bg ng-
style="imgStyle">
</div>
</div>
My m-img JS is kinda extensive, here https://codeshare.io/adABMe
it seems that the error is inside mImg component, from the code here you're not watching changes on src attribute (but you're doing it only on url attribute that here is not used) and you're triggering some logic (defined in $scope.load) to update the view.
You should add a watcher even on src and trigger your load method to update $scope.imgSrc variable , this should update your view consequently
controller: function($scope, $timeout, $mAppDef) {
$scope.$watch('src', function() {
$scope.load()
});
}

How to target specific field in Firebase / AngularFire when more than 1 level deep

I have an ng-repeat which looks like this:
<div ng-repeat="field in fields">
{{field.label}}
<div ng-repeat="choice in field.choices track by $index">
#{{$index+1}} <input type="text" ng-model="choice.value" ng-change="fields.$save(field)">
<a ng-click="field.choices.$remove(choice)">Remove</a>
</div>
</div>
As you can see, it is 2 levels deep it has choices inside of each field, I can't figure out how to remove a specific choice on click, I tried field.choices.$remove(choice) but it doesn't do anything, when I did fields.$remove(field) it removes the entire field, and when I did fields.$remove(choice) or field.$remove(field.choices) it doesn't do anything either.
Here is how fields is initiated:
var fieldsRef = firebase.child('Fields');
$scope.fields = $firebaseArray(fieldsRef);
$scope.fields is a $firebaseArray. But the choices property within a field is not. Therefore, you cannot call field.choices.$remove().
The solution is for the ng-click to call a function in your controller to remove the choice from the field, then call $scope.fields.$save(field).
<a ng-click="$scope.removeChoiceFromField(choice, field)">Remove</a>
Controller:
$scope.removeChoiceFromField = function(choice, field) {
var i = field.choices.indexOf(choice);
if(i != -1) {
field.choices.splice(i, 1);
$scope.fields.$save(field);
}
};

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.

duplicates in a repeater are not allowed angular

I'm trying to create a form like below, this using ng-repeat directive in angular and it whenever I created a new row complains
"Duplicates in a repeater are not allowed.".
While I understand the solution for this is by putting "track by $index", however it causes another issue, which clicking delete on one row deletes the value of other field. So I suspect that track by index is OK for static text but not input form. So how to use ng-repeat correctly for my case?
My jsfiddle : demo.
Edit : I do aware that json array of object will solve my issue ( because for object angular create $$hashKey ) and already implemented this for most of my other module. But I am actually expecting some fix that can be done without really change my json array of string. Sorry for not being clear.
My current code :
HTML
<div class="row-fluid spacer10">
<a ng-click="addAKA()" class="btn btn-primary spacer5 left30"><i class="icon-plus icon-white"></i> Add New Alias</a>
</div>
<div class="row-fluid spacer10"></div>
<div class="row-fluid spacer5" ng-repeat="item in aliasList track by $index">
<input type="text" class="span6 left30" ng-model="item">
<button class="btn btn-danger" ng-click="deleteAKA($index)">delete</button>
<BR/>
</div>
Javascript
$scope.addAKA = function ()
{
if($scope.aliasList == null)
{
$scope.aliasList = [];
}
$scope.aliasList.push("");
$scope.aliasjson = JSON.stringify($scope.aliasList);
}
$scope.deleteAKA = function (idx)
{
var aka_to_delete = $scope.aliasList[idx];
$scope.aliasList.splice(idx, 1);
$scope.aliasjson = JSON.stringify($scope.aliasList);
}
I would guess this is caused when there are more than one empty strings in the list.
If this is the case, it is caused because any two empty strings are equals in JS and Angular repeater does not allow duplicate values (as clearly stated in the message). This is a valid decision as they have to relate an object in the list with its DOM tree to minimize DOM manipulation.
A solution would be to insert simple objects containing the string in the model:
$scope.addAKA = function () {
...
$scope.aliasList.push({value:""});
...
};
And adjust your template:
<input type="text" class="span6 left30" ng-model="item.value">
Since all new objects are different, your problem should be solved.
See a fiddle where a filter is implemented to transform the model back to a list of strings.
When you type in a new created input, your list stays the same. Angular on any list change will update the view (ng-repeat) and remove all new stored text. Therefore we need to add ng-change to update our list on any input change
Add ng-change="change(i, $index) to your item and it should work
HTML
<div ng-controller='ctrl'>
<ol>
<li ng-repeat='i in list track by $index'>
<input type='text' ng-model='i' ng-change="change(i, $index)"></input>
<button ng-click='deleteItem($index)'>Delete</button>
</li>
</ol>
<button ng-click='addItem()'>Add</button>
<div>ITEM: {{list | json}}</div>
</div>
Javascript
angular.module("app", []).controller("ctrl", function ($scope) {
$scope.list = ["one","two"];
$scope.addItem = function ()
{
$scope.list.push("");
};
$scope.deleteItem = function (idx)
{
var item_to_delete = $scope.list[idx];
$scope.list.splice(idx, 1);
};
$scope.change = function (item, idx)
{
$scope.list[idx] = item;
};
});
See fixed Demo in DEMO
Yes, pushing more than one empty string will result in ng-repeat complaining.
In addition, you can also try:
if ($scope.aliasList.indexOf(VALUE_TO_ADD) === -1) {
...
}

Resources