I'm encountering a weird issue on IE9. What I'm trying to do is opening a popup window from my page and attaching the scope of the page to the window object like:
angularApp.controller('ParentCtrl', function($scope, $window) {
$window.parentScope = $scope;
$window.open(url, popupName, params);
});
In my popup/child controller I'm using some property from the parent scope to populate data. Then in my popup page I have some controls which lets a user to pick certain persons from a multi select box. After the user clicks on Assign persons from the popup page I'm calling a method in my parent page like:
childApp.controller('ChildCtrl', function($scope, $window) {
$window.opener.parentScope.updatePersons($scope.personInfo);
}
The method updatePersons is defined in the ParentCtrl as:
$scope.updatePersons = function(personInfo){
$scope.$apply(function() {
$scope.personInfo = personInfo;
});
};
Until now everything works fine and the updated personInfo array is copied from popup to the parent page. Now when I again open the popup page from a button on the parent page the value of $scope.personInfo in ParentCtrl mysteriously gets set to undefined.
The personInfo array on the Parent page is used in a ng-repeat to show a tabular list of persons with their attributes and in IE9 I see undefined in all the columns of the table.
Apparently, Safari, Firefox and Chrome all work flawlessly and don't set the value of this scope variable to undefined when the popup page is clicked again. I've tried looking for a solution to this problem but have failed. I've put id="ng-app" at the root of my application and followed some IE caveats mentioned on angularJs project page but w/o any success. I would really appreciate any help from the community as it's a show stopper for my application.
I created a plunker to demo what I'm trying to achieve. Somehow the child page doesn't show parent scope values and the child can't update parent scope but that's working in my application.
http://plnkr.co/edit/E5pzzYOCMXHTK1huCAF5?p=preview
Thanks in advance for the help!
The plnkr example wasn't working for me in Chrome or IE.
It, for some reason, was trying to put duplicate values into the repeater. Tracking the repeat items by index fixed the issues I had with the example.
<div ng-repeat="person in personInfo track by $index">
Edit: To clarify - the plnkr worked in both Chrome and an emulated IE9 (IE11 emulating IE9) session after adding the track by.
I ended up solving the issue by using angular.copy to send data from parent to popup and then while updating personInfo from popup to parent. It worked to some extent so much so that the personInfo object wasn't getting set to undefined when i clicked on the popup again.
But I encountered another problem when I was receiving the updated data from popup to parent. My personInfo array had 2 nested JS arrays within it and when I was receiving the array from popup everything was fine but when I assigned it to my parent scope using $scope.personInfo = angular.copy(personInfo) the two nested JS arrays within $scope.personInfo converted to JS objects and length method didn't work on them.
The only way I could solve this was to iterate over each person object and then check if any of the arrays had length property undefined and then use jQuery's map function to convert those objects back into JS arrays. It's a silly hack but if anyone knows why is it happening on IE and what's the best angular way to do it that would be awesome!
I still don't get it why IE is treating the array differently when all other browsers seem to work fine. Also, why do I have to use angular.copy everywhere to make IE happy!!!
Thanks!
Related
I have a bootstrap modal that will display info about a parent document. The modal then has next/prev buttons that can rotate around a list of parent documents and reactively change the info displayed on the modal. It does this by simply changing a ReactiveVar of the current parent's onClick of next/prev. This works like a charm.
My problem is that each parent shows some child data as well. The child data is an array of embedded documents. And each document's property has some HTML inputs. I'm trying to pre-populate the values of the html inputs. However, I'm finding this does not work. id and foo are properties on the child document(s).
A call to {{parent}} is a Helper method that returns the current parent from MiniMongo. This seems to work fine as the parent's metadata changes reactively.
{{#each parent.childrenArray}}
<input type="text" id="{{id}}" value="{{foo}}">
{{/each}}
So the issue is that the HTML value does not change. Clicking next will still show the old child's value. I understand there's no reactivity for embedded documents and I'm sure this is the problem. Strangely, upon inspecting, I am in fact seeing the input's id being changed though. Does anybody know the solution to this? Thanks!
This seems to happen when I try to save the children back to the DB. I.e. hitting next/prev should save whatever value users puts into the HTML input before moving on to the next Parent and pre-loading its children
If parent is a helper, then I don't think you can use the subscript notation to get its properties, at least not reactively. However you should be able to use this instead:
{{#with parent}}
{{#each childrenArray}}
<input {{attributes}}>
{{/each}}
{{/with}}
Update: The other thing that is probably required is to use an attribute helper. I've also updated the code above to use this.
Template.yourTemplateName.helpers({
attributes() {
return {
type: 'text',
id: this.id,
value: this.foo
};
}
});
Update 2:
Working "offline" in a git repo by the OP I realized that the modal body was better done as a separate template, such that the data context for that template would change reactively. The part of the code that was at odds is not represented in the question, so read this question and answer with a grain of salt.
I am trying to create kind of a drag and drop playground using AngularJS.
The code is on plunker. And to understand the question, mostly you'll need to see the plunker.
https://plnkr.co/edit/i82UOOqHRSJyEPN9293E
I am trying to create node like structures here which can be dragged over to the playground on the right side.
On drag, I am adding the node id to the nodeChain variable in $scope.
Now I am iterating the nodeChain array in UI with ng-repeat where I am drawing the node just to check that the nodes added to the chain are reflecting correctly.
However,
I do not see the nodeChain updating from watchCollection logs as well as the nodes drawn on the playground are not updating when new nodes are added.
Can someone please tell me why the binding is not taking effect?
Thanks in advance!
You need to trigger a $digest cycle to update the values. When the $digest cycle starts, it fires each of the watchers. These watchers check if the current value of the scope model is different from last calculated value. If yes, then the corresponding listener function executes. As a result if you have any expressions in the view they will be updated.
if (!Bounds.within(event.pageX, event.pageY, leftPane)) {
scope.$parent.nodeChain.push(NodeChain.getNodeFromId(attr.nid))
scope.$apply();
}
Working Plunker:https://plnkr.co/edit/SNNsVOZps5Fy35dAQIqk?p=preview
You are getting ng-repeat dupes error in your console because there are duplicate values in your array nodeChain.
If i understand your question correctly, you want to add the dropped nodes in the below white space. Please see the plunkr.
You should add $scope.apply() when you change the models outside the scope.
In my angular 1.5 html5 application, I have an accordion group and inside it's body I have Couple of check-boxes. Since direct scope binding will not work inside accordion, I'm using ng-click event as attached.
This works as expected, I'm getting click events with correct value.
I have another reset button on screen, when user clicks this button I have to reset all filters including the checkbox inside the accordion. Even after I reset the model value to false, checkbox still shows as checked. I know this is because the binding is not there.
How can I update the checkbox value from javascript. Is there any angular way. I'm not a big fan of JQuery.
Regards,
Nixon
We faced a similar issue with the data bindings while using accordian.
Instead of using directly model variable, we created an object of it.
For eg, instead of using $scope.includeLocalParties, try using $scope.checkbox.includeLocalParties.
Also initialize it in your controller. Something like this:
$scope.checkbox = { includeLocalParties : false};
Hope it helps!
I've just started out using Ionic, I am trying to print the value of the selected list Item {{ item.value }} , however can't find any documentation that suggests how to do this.
Possibly an ng-click="myFunction()" would work here, but I am stuck as to where to start. I have it working with the Ionic Radio buttons. But I want the list to re-direct to another page on click, but still hold this value in the main controller.
Here is the Codepen attempt.
Thank you.
Well, at first ng-click="do" will do absolutely nothing. It should be ng-click="do()", and of course, you have to have a function called do in your controller to perform an action.
The 2nd. problem is: you're inside ngRepeat, it creates a child $scope, so you have to use the Dot Rule:
$scope.model = {};
Then, you can do it:
ng-click="model.itemValue = item"
Check the forked DEMO
The code at this plnkr has a modal which pops up when a user clicks on a "Click to take quiz" button which calls a controller method that in turn calls a modal service. To get the plnkr to work, click anywhere in the code and press the space bar to add white space in a way that does not effect syntax. This will trigger plnkr to re-initialize the app and make the modal pop up after you click the button.
The problem is that the text printed in the modal does not update dynamically when timeLeft variable counts down. And also, the user's button click does not update the quizAnswer variable. In short, the modal is not able to talk interactively with the calling controller and view.
What specific changes need to be made to the plnkr to get the modal text to show the dynamic countdown, and to get the modal buttons to change the value of the $scope.quizAnswer variable?
Also, I have been carefully reading the documentation at this link. I think that the answer may be related to:
1.) $uibModal's options parameter passed in open(options) contains the parameter scope that defines the parent scope to be used for the modal's content, and also property bindToController which, when set to true, binds the scope property to a specific controller defined by controllerAs.
2.) The open(options) method returns a modal instance, which includes close(result) and dismiss(reason).
I suspect that the solution lies in these methods and parameters, but I am looking for good examples and would appreciate some experienced eyes looking at this problem.
NOTE: The solution to this came in the comments below the accepted answer, especially the link to another posting that contains 2 lines of code for emitting the modal button click's results back to the parent controller.
You have a number of issues.
First, takeQuiz at navigation.js - line 16, should be attached to $scope, not this, since this will mutate depending on context.
Second, $scope.$apply and $scope.$digst(); at navigation.js - lines 29/30 are unnecessary since you will already be in a digest cycle. They should be removed else they'll trigger an error.
Finally (and this is the meat of your issue), you are misunderstanding how modal options are bound across when creating a modal instance. It is NOT two-way binding; it is a single extends from one object to another. As a result, trying to bind to the options (or creating a concatenated string with the timeRemaining) will not update once it's bound across.
Instead, one possibility is to create an event handler inside of the modal and broadcast on each tick, updating the modal. In addition, if you pass the body text as prepend and append text, it is easier to insert your timestamp value:
You will need to inject (and broadcast from) $rootScope in your navigationController, since the modalService is registered somewhere very high in the scope chain.
On each tick, broadcast the time remaining navigation.js:
$rootScope.$broadcast('timeRemainingTick', $scope.timeRemaining);
In your modalService.js, register to receive the event inside of the controller assignment:
var timeRemainingUnbind = $scope.$on('timeRemainingTick', function(event, newTick) {
$scope.modalOptions.timeRemaining = newTick;
});
Finally, make sure that you unbind the event by calling timeRemainingUnbind() in the close events of your modal to prevent memory leaks:
$scope.modalOptions.ok = function (result) {
timeRemainingUnbind();
$modalInstance.close(result);
};
$scope.modalOptions.close = function (result) {
timeRemainingUnbind();
$modalInstance.dismiss('cancel');
};
See my working forked plunker here