Ng-show in a directive template - angularjs

I'm trying to make a simple app with Angularjs and MongoDB. I have some articles where it's possible to add a comment and display them.
The comments section is in a template defined on a Directive, who is himself in the Home template with another Controller.
To be clear (or maybe not?) : HomeTemplate [with MainCtrl] own a COMMENT element [with his directive].
Here is my directive
app.directive('Comment', function(){
return{
templateUrl: '/comments.html',
scope:{
post:'=datapost'
},
controller: ['$scope', 'posts', 'auth', function($scope, posts,auth){
$scope.isLoggedIn = auth.isLoggedIn;
$scope.addComment = function(){
if($scope.commentToAdd === '')
return;
$scope.isLoading = true;
posts.addComment($scope.post._id, {
body: $scope.commentToAdd,
author: auth.currentUser()
}).success(function(comment){
$scope.post.comments.push(comment);
});
$scope.commentToAdd = '';
$scope.isLoading = false;
};
}]
}
});
Auth & Posts directives are defined later
Here it's my MainCtrl where I'm supposed to trigger the event :
$scope.showComments = function(){
$scope.showingComments = true;
};
And here my HTML :
<div ng-show="showingComments =!showingComments">
<div comment datapost="post" ></div>
</div>

I'm not too sure what your question here is but:
<div ng-show="showingComments =!showingComments">
should be
<div ng-show="showingComments">
=! is not a valid operator, and you're comparing the same variable to itself
use ng-show="showingComments" to show if true, or ng-show="!showingComments" to show if false

Related

Angularjs update controller variable from directive and use in view

I've just started to use Angularjs and with the help of some stackoverflow answers I have created an image fallback directive with Angularjs.
The fallback functionality is working, but now I would like the use a boolean, a variable set in the controller I guess, in combination with ng-show in the view which indicates if the fallback image is used, or if the original image is loaded. I've changed my code several times, but it never worked....
(The teamCtrl is a seperated controller which does work and can be ignored in this issue, so I did not include the code.)
This is a piece of my html:
<div class="thumbnail margin-bot-20px">
<img ng-src="../img/team{{teamCtrl.selectedteam.id}}.jpg" myfallback-src="../img/onbekend.jpg" />
</div>
<div ng-controller="fallbackController as fbCtrl">
<p>
Wijzig foto
Verwijder foto
</p>
</div>
This is the directive and the directive's controller:
(function () {
'use strict';
angular.module('PD.fallback', [])
.directive('myfallbackSrc', myfallbackSrc);
angular.module('PD.fallback')
.controller('fallbackController', fallbackController);
function fallbackController()
{
this.directivedummy = false;
};
function myfallbackSrc()
{
var directive = {
link: link
//controller: fallbackController, // controllerfunctie
//controllerAs: 'vm' // controllerAs-alias
//bindToController: true
//scope: {}
};
return directive;
};
// 3. Link-function implementeren
function link(scope, element, attrs)
{
element.bind('error', function()
{
scope.directivedummy = false;
if (attrs.src != attrs.myfallbackSrc)
attrs.$set('src', attrs.myfallbackSrc);
});
element.bind('load', function()
{
if (attrs.src != attrs.myfallbackSrc)
scope.directivedummy = true;
});
}
})();
So I would like to show/hide a button in the view html. The button must be visible when the src image was loaded successfully and must be hidden when the fallback image is loaded.
Hopefully someone can help me?
Assuming your are not using ControllerAs syntax, you need to trigger the $digest since the bind callback is happening outside of Angular
element.bind('error', function(){
scope.$apply(function (){
scope.directivedummy = false;
if (attrs.src != attrs.myfallbackSrc)
attrs.$set('src', attrs.myfallbackSrc);
});
});

Passing parent controller to modal (Angular-ui bootstrap)

I feel like this should work, but it's not. I want to be able to pass the controller that is opening the modal so I can use its submit function etc. I am using the controllerAs pattern (so not using $scope).
var _this = this;
this.openModal = function(modalId,dataIn){
this.modalInstance = $modal.open({
templateUrl: modalId,
controller: 'ModalCtrl',
controllerAs: 'modal',
resolve: {
modalObject: function(){
return dataIn;
},
title: function(){
return 'Training Info';
},
parent: function(){
return _this
}
}
});
_this.modalInstance.result.then(function (data) {
$log.log('submitting data for '+_this.doctorId);
experienceFactory.submitTraining(data).then(function(){
loadExperience();
},function(error){
console.log(error);
});
}, function () {
//something on close
$log.info('Modal dismissed at: ' + new Date());
});
};
this.something = function(){
$log.warn('omg it workt?');
};
That is opened up with a simple aCtrl.openModal('a-modal',aCtrl.data) however for some reason I can't access the parent controller
<script type="text/ng-template" id="a-modal">
<div class="modal-header">
<h3 class="modal-title">{{modal.title}}</h3>
</div>
<div class="modal-body">
<button ng-click="parent.something()">Something</button>
</div>
</script>
The button does nothing, but should be printing the warnings. Any ideas appreciated. Thanks.
Unfortunately you didn't include the code of your controller, however I think that you misunderstood how dependencies are provided to the modal's controller.
The resolve part provides dependencies to the controller, it neither binds them to the scope nor to the controller. Take a look at this JS part of UI-Bootstrap's Modal example:
.controller('ModalInstanceCtrl', function ($scope, $modalInstance, items) {
$scope.items = items;
In your case it should be
.controller('ModalCtrl', ['parent', function (parent) {
this.parent = parent;
And in HTML, if you are using controllerAs pattern:
<button ng-click="modal.parent.something()">Something</button>

Directive not rendering after $http request

I began working with angularjs and have a problem.
app.js (just the relevant route)
$routeProvider.when('/search', {templateUrl: 'partials/search-results.html', controller: 'SearchController', title: 'Product Search'});
search-results.html
<div product-list></div>
index.html
<div class="page_content_offset p_bottom_0">
<div class="container mh600">
<div ng-view></div>
</div>
</div>
product-list.html
<div ng-repeat="item in items">
<p>{{item.address_components[0].long_name}}</p>
</div>
controller.js just relevant code:
$scope.search = function() {
$scope.loading = true;
$scope.items = {};
ProductSearchService.searchItems($scope.term).then(function(data) {
$scope.items = data;
$scope.loading = false;
});
};
directives.js (just relevant code)
directive('productList', function() {
return {
restrict: 'EA',
templateUrl: 'partials/list/product-list.html'
};
My Problem now is:
The ProductSearchService loads the data. But the directive not rendering as expected.
If i move the directive code from search-results.html to my index.html like this:
<div class="page_content_offset p_bottom_0">
<div class="container mh600">
<div product-list></div>
<div class="clearfix"></div>
</div>
</div>
evrything is rendered nicely. So i think i included my directive wrongly or forgot something important.
I've created a plunkr similar to my setup:
http://plnkr.co/edit/60dvyFnz74yrsK12J2J4?p=preview
Everything works fine in that.
In my local application i changed the "product-list.html" model property access to this
<div ng-repeat="item in $parent.items">
<p>{{item.address_components[0].long_name}}</p>
</div>
Update controllers.js
angular.module('myApp.controllers', [])
.controller('SearchController', ['$scope','$http','ProductSearchService', function($scope, $http, ProductSearchService) {
$scope.items = {};
$scope.search = function() {
$scope.loading = true;
ProductSearchService.searchItems($scope.term).then(function(data) {
$scope.items = data;
$scope.loading = false;
});
};
}]);
Update 2:
I now have a plnkr where the problem can be shown:
http://plnkr.co/edit/QgU1cf3JeJYKu6Fkl9zv?p=preview
You did not set any scope attribute to your directive.
This means that your directive use the defining/containing scope as the directive own scope.
Thus , the scope that is used in product-list.html is the same as the one used by search-result.html (and so by the SearchController).
The only reason , you could have to use the $parent.items would be if you specified scope:{}, in your directive.You can even test it in your plunker (I did).
Can you double check the directive scope attribute ?
Thx
directive('productList', function() {
return {
restrict: 'EA',
replace: true,
templateUrl: '<section data-ng-include=" 'partials/list/product-list.html' "></section>'
};
});
try this one as the directive :)

How $watch controller method in directive

I have CKEDITOR, a controller and a directive. This is the method of the controller which should add ng-show and remove ng-hide from the <span>:
$scope.deleteEditorAndSave = () ->
angular.forEach CKEDITOR.instances, (editor) ->
id = editor.element.getAttribute('data_id')
text = editor.getData()
field = editor.element.getNameAtt()
html_field = $(editor.element.$)
html_field.val(text)
showing = editor.element.getAttribute('ng-show')
console.log showing
$timeout( ->
html_field.trigger('input')
$scope.save_field(text, id, field, 'no_call')
editor.destroy()
angular.forEach allClaims(), (claim) ->
console.log "CLAIM", claim
claim[showing.split('.')[1]] = false
)
And I want to call this method from the directive. When I try to do this, the <span> element doesn't re-render. Does anyone know how this can be solved?
Thanks in advance.
I think the recommended practice is to resolve DOM in the directives, once thing that can help you is a directive with isolated scope, this will allow you to use & in your directives scope like this:
<body ng-controller="MainCtrl">
<div user-name="" callme="enableEditor()"></div>
<div>
add
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$scope.enableEditor = function() {
alert("123");
};
}]);
myApp.directive("userName", function() {
return {
restrict: "A",
scope: {
value: "=userName",
callme:"&"
},
template: '<div class="click-to-edit">' +
'Edit' +
'</div>'
};
});
The attribute callme="enableEditor()" is used to pass the method to the scope directive, the directive scope uses & to indicate it is method callme:"&". Another example:
method2="someMethod()" like
scope: {
value: "=userName",
callme:"&",
method2:"&"
},template: '<div class="click-to-edit">' + 'Edit' + 'Save' + '</div>'
This is the recommended way to communicate directives with controllers.

In AngularJs, how do I identify a specific scope within a controller that is used multiple times on a page?

I have an application that uses multiple tabs with a grid in each tab. I have multiple pages using this same configuration.
I have a single controller that works for all grids. I would like to reuse this controller throughtout my application.
My problem is that I am trying to lazy load the data for each tab until the tab is clicked. What I can't figure out is how to tell the controller which scope belongs to which tab and ONLY get the data for that grid. I know internally AngularJs does this because if I just load all the data for each tab at once I can click on my paginator, search, etc. for each tab and only that scope is updated.
I have ng-click setup for each tab and I can get my controller to fire when a tab is clicked. However, the controller calls all instantiated scopes to load data for their respective grids.
My approach may not be the best but it seems silly to create seperate controllers that do exactly the same thing.
Note: Angular UI tabs with bootstrap is not an option.
My view
<div ng-app="TabsApp">
<div tabs>
<div class="tabs">
<a ng-click="clickTab(0)" ng-class="{selected: selectedTab==0}">Localized Text From Server</a>
<a ng-click="clickTab(1)" ng-class="{selected: selectedTab==1}">Localized Text From Server</a>
<a ng-click="clickTab(2)" ng-class="{selected: selectedTab==2}">Localized Text From Server</a>
</div>
<div class="tab-content">
<div ng-show="selectedTab==0" ng-init="init(#Model.UserId, 'useraddresses')" ng-controller="ListCtrl">#Html.Partial("_Grid0")</div>
<div ng-show="selectedTab==1" ng-init="init(#Model.UserId, 'userphones')" ng-controller="ListCtrl">#Html.Partial("_Grid1")</div>
<div ng-show="selectedTab==2" ng-init="init(#Model.UserId, 'usernotes')" ng-controller="ListCtrl">#Html.Partial("_Grid2")</div>
</div>
</div>
</div>
My app and factory url
var TabsApp = angular.module("TabsApp", ['ngResource', 'ngRoute']);
TabsApp.factory('Api', function($resource){
return $resource('/api/user/:userId/:ctrl', { userId: '#userId', ctrl: '#ctrl'});
});
My controller - child scope(s)
var ListCtrl = function ($scope, $location, Api){
$scope.search = function () {
Api.get({
userId: $scope.userId,
ctrl: $scope.ctrl,
q: $scope.query
//etc.
},
function(data){
$scope.items = data.items;
//other mapping
});
};
$scope.init = function(userId, ctrl){
$scope.userId = userId;
$scope.ctrl = ctrl;
};
$scope.reset = function(){
$scope.items = [];
$scope.search();
};
//kind of a hack but broadcasting was the only way I could figure out to listen for tab changes
$scope.tabModelChange = { 'isChanged': false };
$scope.$on('tabModelChange', function(event, args) {
$scope.tabModelChange.isChanged = true;
var activeTab = args.val[$scope.selectedTab];
if (!activeTab.isLoaded) {
$scope.reset();
}
});
//filtering, sorting, pagination, etc.
};
My directive: Parent scope
TabsApp.directive('tabs', function () {
return {
controller: function ($scope, $element) {
$scope.selectedTab = 0;
$scope.tabModel = [];
//I use localized text and any number of tabs on my views from the server so the client wont know how many tabs each view will have
var tabs = angular.element($element.children()[1]).children();
angular.forEach(tabs, function (value, key) {
$scope.tabModel.push({'isLoaded' : false, 'isActive': false});
});
$scope.clickTab = function(tab) {
$scope.selectedTab = tab;
$scope.tabModel[$scope.selectedTab].isActive = true;
};
$scope.$watch('tabModel', function(newVal, oldVal) {
if (newVal !== oldVal) {
$scope.$broadcast('tabModelChange', { 'val': newVal });
}
}, true);
}
};
});
I suspect that when my controller receives a broadcast that the tab model has changed it calls $scope.reset with $scope being a parent scope which then traverses the child scopes looking for 'reset'. Because there are 3 instances of ListCtrl, parent scope finds 3 child scopes with 'reset'. Hence, all the data gets loaded at once.
How can I get $scope.reset to match the tab that was clicked? Thanks.
With minimal change to your existing code, here's a plunker that does what you want, I think.
http://plnkr.co/edit/TVwWQehgWJay7ngA8g6B?p=preview
Basically I made a second directive called tab that accepts an argument which is a function to evaluate when that tab is switched to.
TabsApp.directive('tab', function () {
return {
require: '^tabs',
link: function (scope, element, attrs, tabsCtrl) {
tabsCtrl.add({
'isLoaded' : false,
'isActive': false,
'changed': function () { scope.$eval(attrs.tab); }
});
}
};
});
Here's what we do (i'm a bit lazy and going to pull use our code), We load when tab is clicked.
Here's the tabs
<ul class='nav nav-pills'>
<li ng-class="{ active : mode == 'incomplete'}"><a tabindex="-1" href="#/incomplete" ng-click='mode="incomplete"'>Incomplete orders</span></a></li>
<li ng-class="{ active : mode == 'completed'}"><a tabindex="-1" href="#/completed" ng-click='mode="completed"'>Completed orders</a></li>
</ul>
We setup routes
.config(['$routeProvider', '$env', function ($routeProvider, $env) {
$routeProvider.when("/completed", {
templateUrl: $env.urlRoot + "content/partials/orders/completed.htm", controller: 'CompletedOrdersCtrl'
});
$routeProvider.when("/incomplete", {
templateUrl: $env.urlRoot + "content/partials/orders/incomplete.htm", controller: 'IncompleteOrdersCtrl'
});
$routeProvider.otherwise({
templateUrl: $env.urlRoot + "content/partials/orders/incomplete.htm", controller: 'IncompleteOrdersCtrl'
});
} ]);
We then have the two controllers IncompleteOrdersCtrl and CompleteOrdersCtrl that call the service to get the correct data. You could possibly consolidate into one controller with a parameter passed in the route config.
Hopefully this works for you
--dan

Resources