get ng-click, on injected element, to change the class of an element already in the view? - angularjs

I have a <ul> that gets populated with the server. But in that controller there is also an iframe. When the <li>'s arrive there is some disconnect between them and the iframe even though they are in the same controller.
When you click one of the li's it should change the class on the iframe but it's not. However, If I move the iframe inside of the ng-repeat that injects the iframe it works.
View
<div class="content" ng-controller="FeedListCtrl">
<ul>
<li ng-repeat="item in items">
<div data-link="{{item.link}}" ng-click="articleShowHide='fade-in'">
<div ng-bind-html="item.title" style="font-weight:bold;"></div>
<div ng-bind-html="item.description"></div>
<!-- it works if i put the iframe here -->
</div>
</li>
</ul>
<!-- doesn't work when the iframe is here -->
<iframe id="article" ng-class="articleShowHide" src=""></iframe>
</div>
Here is the controller. It does an ajax call to get the data for each <li>
Controller
readerApp.controller('FeedListCtrl', ["$scope", "$http", "FeedListUpdate", function ($scope, $http, FeedListUpdate) {
$scope.setFeed = function (url) {
$http.get('feed?id=' + FeedListUpdate.GetCurrentFeedUrl()).success(function (data) {
$scope.items = data.currentFeed.items;
});
};
}]);

When inside of an ng-repeat you are in a different scope which means you are not setting the variable you think you are. Use $parent and that should work. The syntax is:
<div data-link="{{item.link}}" ng-click="$parent.articleShowHide='fade-in'">
Side note for others finding this - sometimes adding curly brackets helps as well. For more information on ng-class see here: http://docs.angularjs.org/api/ng/directive/ngClass
An Example
In case anyone wants to see this in action, I put together an example demonstrating a few ways to set the class as well as demonstrating the issue in scope (See: http://plnkr.co/edit/8gyZGzESWyi2aCL4mC9A?p=preview). It isn't very pretty but it should be pretty clear what is going on. By the way, the reason that methods work in this example is that the scope doesn't automatically redefine them the way it does variables so it is calling the method in the root scope rather than setting a variable in the repeater scope.
Best of luck!

Related

How to evaluate a template from a controller?

I've got this template:
<div class="container-fluid">
<div class="row">
<div class="col-xs-12" ng-repeat="product in ad.products">
<a href="{{product.link}}">
<h1>{{product.title}}</h1>
<img src="{{product.src}}">
<p>{{product.description}}</p>
<h5>{{product.price}}</h5>
</a>
</div>
</div>
</div>
From my controller I need to evaluate this template so that it checks how many products that have been selected and then it interpolates each product's values into the template. After that is done I also need to remove the ng-repeat so it doesn't fire an error in the external pages that will use this where angular is not present. However I'd figure that I'd just use a regex to look up the ng-repeat and everything in the expression and then remove it.
I've been looking at $interpolate and $compile but I can't figure out how to work with them from my controller so that it does what I want. This is because when I use these on my template and then console log the template value it's a function with a whole lot of nonsense in it.
So doing this:
ad.html = $compile(res.data, $scope);
Generates something like this:
function(b,c,d){rb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var h=d.parentBoundTranscludeFn,k=d.transcludeControllers;d=d.futureParentElement;h&&h.$$boundTransclude&&(h=h.$$boundTr…
Can someone shed some light on how to achieve what I want?
Your are using $compile function in wrong way, you should call $compile(html) function by passing $scope parameter like below.
var compiledDOM = $compile(res.data)($scope);//then do append this DOM to wherever you want
ad.html = compiledDOM.html(); //but this HTML would not make angular binding working.

Changing $scope variable value in controller not changing value on view

I am working on Cordova tool and angularjs for my application.
cordovaApp.controller("VacationCtrl", function ($scope, $http, $location) {
$scope.tempdate = "2222";
$scope.ruleDetails = function () {
$scope.tempdate = "3333";
}
});
view 1
<div ng-controller="VacationCtrl">
<a ng-repeat="data in rules" ng-click="ruleDetails()" class="summaryListBorder" href="#detailVacationRule">
</a>
</div>
view 2
<div ng-controller="VacationCtrl">
{{tempdate}}
</div>
In above given code, I sat value of $scope.tempdate to "2222". When I am click on link, it calls ruleDetails() and set $scope.tempdata = "3333". But when the new page is open with ng-view, it shows only old value, i.e. "2222". I want to change it with "3333". I have tried with $scope.$apply() too.
Thanks.
Every ng-controller attribute creates a new instance of the controller, which won't share the same scope as other instances. You want to wrap both divs in a single controller instance, like:
<div ng-controller="VacationCtrl">
<div>
<a ng-click="ruleDetails()" href="#detailVacationRule">
</a>
</div>
<div>
{{ tempdate }}
</div>
</div>
If you need separate controllers, then you want to move common functions/fields into a service, which operates as a singleton so you can use it to share information between controllers. Or you could contain the separate controller instances in a parent controller, which will hold common fields and can be accessed through each controller's scope.

How can i get rid of $parent in angular

Here's Plunker
I have an external template within in a controller with ng-include. It is shown and hidden based on click event of Button.It is working as required but with $parent in ng-include Template.Is there any other better way of doing this ?
Html
<body ng-controller="MainCtrl">
<div data-ng-include="'terms.html'" data-ng-show="otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{name}}!</p>
<button data-ng-click="mainPage=false; otherContent=true">Link to some Content</button>
</div>
</body>
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.mainPage=true;
});
External Template
<p>Some content here </p>
<button data-ng-click="$parent.mainPage=true; $parent.otherContent=false">Back</button>
Option1 - Set property on an object in the scope
In the main controller add an object on the scope.
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.page={mainPage:true};
});
and in the ng-click do:-
<div data-ng-include="'terms.html'" data-ng-show="page.otherContent"></div>
<div ng-show="page.mainPage">
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
<!-- -->
<button data-ng-click="page.mainPage=true; page.otherContent=false">Back</button>
Demo - setting property on an object in the scope
Option2 - Use function
Instead of setting properties on the view (Which is anyways a good idea to abstract out too much logic from the view), Do your set operations in the controller exposed as a function that can be invoked from the view, which also gives extensibility when you need to add more logic for that particular action. And in your case you could even use the same function and call it from both the button clicks and flipped based on a boolean argument.
Demo - with function
Option3 - Use Controller Alias
Using an alias for the controller, which is nothing but instance of the controller is set as a property on the scope with the property name same as the alias provided. This will make sure you are enforce to use dot notation in your bindings and makes sure the properties you access on the child scopes with the controller alias are inherited as object reference from its parent and changes made are reflected both ways. With angular 1.3, it is also possibly to set the isolate scoped directive properties are bound to the controller instance automatically by setting bindToController property in the directive configuration.
Demo - With Controller alias
ControllerAs is the recommend way of avoiding this problem.
Using controller as makes it obvious which controller you are accessing in the template when multiple controllers apply to an element.
If you are writing your controllers as classes you have easier access to the properties and methods, which will appear on the scope, from inside the controller code.
Since there is always a . in the bindings, you don't have to worry about prototypal inheritance masking primitives.
<body ng-controller="MainCtrl as main">
<div data-ng-include="'terms.html'" data-ng-show="main.otherContent"></div>
<div ng-show="mainPage">
<p>Hello {{main.name}}!</p>
<button data-ng-click="main.mainPage=false; main.otherContent=true">Link to some Content</button>
</div>
</body>
Here are some resources for controller as:
http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
http://toddmotto.com/digging-into-angulars-controller-as-syntax/
https://docs.angularjs.org/api/ng/directive/ngController#example

ngRoute RouteController getting called only once

I've a fiddler to show my setup.
http://jsfiddle.net/smartdev101/dt6kkyy3
The problem is the routeController function is getting called only once on the page load, but then on hash change (as a result of link click), the function routeController is not getting called again.
I've my links generated in another ng module, not sure if the links have to be with in the same module as the router.
<div id="router">
<div ng-view></div>
</div>
<div id="navigation" data-ng-cloak>
<ul id="folios" data-ng-controller="FoliosController" class="nav nav-pills nav-stacked">
<li data-ng-repeat="folio in folios" ng-class="{active: isActive('/search/{{folio.productId}}')}">
{{folio.title}}
</li>
</ul>
<div ui-view></div>
</div>
Edit:
Problem has been further diagnosed. Any list/anchor that was generated by an ng controller, is not responding to hash changes outside the scope, while the static links are. from the plunkr link, "foo" and "bar" clicks trigger the controller but "abc" and "def" are not. please review. Question is how to make "abc" and "def" (dynamic links) respond to changes as well.
http://plnkr.co/edit/ea1OHa?p=preview
The code is behaving as intended with the way you have it coded. You are calling routeController in the constructor of the controller, but its not being invoked anywhere else (the router isn't going to do that either).
If you need the routeController function to be invoked on every route change consider binding to route changing events:
https://docs.angularjs.org/api/ngRoute/service/$route
Take a look at this plunk. Note I am binding to stateChangeSuccess instead since you are using the ui-router instead of ngRoute.
http://plnkr.co/edit/CiYLqnqAzrXoouMSXkuk

Where to put menu when using angular-snap?

I'm using snap.js with AngularJS using the angular-snap.js directive.
https://github.com/jtrussell/angular-snap.js
I'm also using Andy Joslin's angular-mobile-nav.
I'm wondering where I should store the code for the menu:
<snap-drawer>
<p>I'm a drawer! Where do I go in the angular code?</p>
</snap-drawer>
Because this isn't a unique page within the angular-mobile-nav, I'm currently putting the on every page and just using a directive that contains all my menu code/html.
Seems like this could be inefficient as it is loading a new directive on each page, right? Any idea on how to do this better?
Thanks!
So this is what I've done (I also use angular-mobile-nav and angular-snap.js).
This is my HTML Code
<body ng-app="MyApp">
<div snap-drawer>
<ul class="list">
<li ng-repeat="item in sidebar.items" ng-i18next="{{item.name}}" ng-tap="snapper.close();go(item.link)"></li>
</ul>
</div>
<div id="container" snap-content snap-options="snapOpts">
<div mobile-view=""></div>
</div>
</body>
please note that go() is the function to change the page and that I'm using ng-i18next to translate my items. Also ng-tap is a directive which listens for touch events instead of mouse events. With Angular >1.1.5 there's a mobile module, so my ng-tap directive won't be needed anymore.
And by using $rootScope I can put items in the sidebar:
$rootScope.sidebar = {
items: [
{
name: 'item_one',
link: 'page_one'
},
...
]
};
So if you want to change the items in the sidebar, simply override $rootScope.sidebar (not $scope.sidebar) in your controller ;)
If you don't like two animations happen at the same time, you could write a function, which waits for the sidebar to close and then change the page. It could look like this:
$rootScope.waitThenGoTo = function (page, time) {
time = time || 200;
$timeout(function () {
$navigate.go(page);
}, time);
};
If you have still question, please comment. I'll try to update this answer as soon as possible.

Resources