<div class="full-row" ng-repeat="row in pendingRequests | orderBy: '-modificationDate' | partition:3">
<div class="one-third" ng-repeat="request in row track by request.id">
<div class="incoming_request" ng-class="actionClass(request)">
<div class="request_comments">
<hr>
<p><span>Recipients:</span></p>
<div class="comments">
<p ng-repeat="comment in request.comments track by comment.id" class="dont-break-out">
<span class="author" ng-bind="comment.userName | employeeName">:</span>
{{comment.text}}
<span ng-if="comment.status == State.APPROVED" class="approval success" ng-click="changestatus(comment, request)"><i class="fa fa-check"></i></span>
<span ng-if="comment.status == State.REJECTED" class="approval error" ng-click="changestatus(comment, request)"><i class="fa fa-times"></i></span>
<span ng-if="comment.status == State.PENDING" class="approval" ng-click="changestatus(comment, request)" title="{{showApproveTooltip(comment)?'Click to approve the request on behalf of this user':''}}"><i class="fa fa-clock-o"></i></span>
</p>
</div>
</div>
<div class="request_resolve">
<hr>
<div class="textarea-holder">
<textarea placeholder="Your comment..." ng-model="request.newComment" ng-model-options="{ updateOn: 'default blur'}"></textarea>
</div>
<div class="button-container">
<button ng-click="approve(request);" ng-disabled="request.isProcessing" class="btn btn-primary" am-hide-request-resolve-div>Confirm<i ng-hide="request.isProcessing" class="fa fa-check"></i><span ng-show="request.isProcessing" class="spinner no-hover"><a><i class="fa-li fa fa-spinner fa-spin"></i></a></span></button>
<button ng-click="reject(request);" ng-disabled="request.isProcessing" class="btn btn-default pull-right" am-hide-request-resolve-div>Reject <i class="fa fa-times"></i></button>
</div>
</div>
Here is peace of code. As You may see there are many ng-repeats. My pendingRequests collection very often is updated from server. After 3 or more updates when I click on some button nothing is happend on UI.
Details :
On approve click I change status of one comment.
$scope.approve = function (request) {
var currentUserComment = request.comments.filter(function(comm) {
return comm.userId == user.id && comm.status == "Pending";
})[0];
currentUserComment.status = State.APPROVED; // change comments Status
currentUserComment.text = request.newComment;
delete request.newComment;
if (!currentUserComment) {
request.isProcessing = false;
return;
}
Comments.update(currentUserComment, function() {
// $rootScope.$broadcast('daysUpdated');
});
request.isProcessing = false;
};
This must show this span <span ng-if="comment.status == State.APPROVED" class="approval success" ng-click="changestatus(comment, request)"><i class="fa fa-check"></i></span> , cause now status is equal to State.APPROVED. But nothing happens.
After some research I think it all because ng-repeat and collection updates.
ng-repeat creates a child scope for each item, and so the scopes in play might look like this:
bookCtrl scope = { tags: [ 'foo', 'bar', 'baz' ] }
ng-repeat child scopes: { tag: 'foo' }, { tag: 'bar' }, { tag: 'baz' }
So when I update comment.status for some request Angular don't know in what scope it exists.
Am I right? And how can I solve my problem (after changing comment status show correct span)?
More Simple code :
<div ng-repeat="request in requests">
<div ng-repeat="comment in request.comments">
<span ng-if="comment.status == State.APPROVED" class="approval success"><i class="fa fa-check"></i></span>
<span ng-if="comment.status == State.REJECTED" class="approval error"><i class="fa fa-times"></i></span>
<span ng-if="comment.status == State.PENDING" class="approval"><i class="fa fa-clock-o"></i></span>
</div>
<div>
<button ng-click="approve(request)">
Approve
</button>
</div>
</div>
And approve function :
var user = LoggeInUser(); // some user who is loggedIn now
$scope.approve = function(request){
var currentUserComment = request.comments.filter(function(comm) {
return comm.userId == user.id && comm.status == "Pending";
})[0];
currentUserComment.status = State.APPROVED; // change comments Status
Comments.update(currentUserComment, function() { // send PUT request to API
// $rootScope.$broadcast('daysUpdated');
});
}
You may find your solution in moving the functions to $scope.functions.functionName. I believe from reviewing this that you are running into scoping issues, as you alluded to in your statement.
JS
$scope.functions.approve = function () { ... }
HTML
functions.approve(request)
You also might have a look at using controller as, sometimes that can help:
https://docs.angularjs.org/api/ng/directive/ngController
Related
I am currently working on ajax, jquery and javascript. I have slight problems with it.
I can use this code to send the data and they are stored in the database.
But the data will not be displayed directly after sending in the view, until I have reloaded the page.
How can I output the data directly in the view without reloading the page?
How can I output errors and success messages as flashmessage (toastr) message?
how can I rewrite this code that works? I get the error message that it is a duplicate of the selectors.
$('#todolist-create-modal').modal('hide');
$('#todolist-create-modal').on('keypress', ":input:not(textarea)", function(event) {
return event.keyCode != 13;
});
Code
<script type="application/javascript">
$(document).ready(function () {
var CSRF_TOKEN = $('meta[name="csrf-token"]').attr('content');
$('#add-todo-list').click(function(e) {
e.preventDefault();
var _token = $("input[name='_token']").val(); // get csrf field.
var title = $("input[name='title']").val();
var description = $("textarea[name='description']").val();
var privacy = $("select[name='privacy']").val();
var listid = $("select[name='privcy']").val();
$.ajax({
url:'{{ route('todolists.store') }}',
type: 'POST',
data: {_token:_token, title:title, description:description, privacy:privacy},
success: function (data) {
console.log(data);
if (privacy = 0) {
//go to the left side
} else {
//go to the right side
}
},
error: function(data){
console.log(data);
}
});
$('#todolist-create-modal').modal('hide');
$('#todolist-create-modal').on('keypress', ":input:not(textarea)", function(event) {
return event.keyCode != 13;
});
});
});
</script>
view
<div id="content" class="dashboard padding-10">
<div class="row">
<div class="col-md-offset-3 col-md-6">
<a data-toggle="modal" data-target=".todolist-create-modal" class="btn btn-success btn-block btn-sm margin-bottom-10">Neue Liste erstellen</a>
</div>
<div class="col-md-6">
<div id="panel-misc-portlet-l3" class="panel panel-default text-center">
<div class="panel-heading nohover">
<span class="elipsis">
<strong>Öffentliche Tasks</strong>
</span>
</div>
</div>
<div class="alert alert-danger margin-bottom-30 {{ $todolistpublic->count() ? 'hidden' : '' }}">
Es wurden keine <strong>Einträge</strong> gefunden.
</div>
#foreach ($todolistpublic as $list)
<div id="todo-list-{{$list->id}}" class="panel panel-default panel-primary margin-bottom-0">
<div class="panel-heading panel-pointer">
<span class="elipsis"><!-- panel title -->
<strong>{{ $list->title }}</strong> <span class="label label-info white">0</span>
</span>
<ul class="options pull-right relative list-unstyled hover-visible">
<li><a data-toggle="modal" data-target=".task-modal" class="btn btn-success btn-xs white hover-hidden">
<i class="fa fa-plus"></i> Erstellen
</a>
</li>
<li><a data-toggle="modal" data-target=".todolist-modal" data-id="{{ $list->id }}" data-title="{{ $list->title }}" data-description="{{ $list->description }}" class="btn btn-info btn-xs white hover-hidden">
<i class="fa fa-edit"></i> Bearbeiten
</a>
</li>
<li><a data-toggle="modal" data-target=".todolist-delete-modal" data-id="{{ $list->id }}" data-title="{{ $list->title }}" data-description="{{ $list->description }}" class="btn btn-danger btn-xs white hover-hidden">
<i class="fa fa-times"></i> Löschen
</a>
</li>
<li></li>
</ul>
</div>
<div class="panel-body">
<div class="slimscroll" data-always-visible="false" data-rail-visible="false" data-railOpacity="1" data-height="100">
{{ $list->description }}
</div>
</div>
</div>
#endforeach
<div class="panel-footer mtm-10">
<span id="todo-list-counter-public">{{ $todolistpublic->count() }}</span> <span>{{ $todolistpublic->count() > 1? 'Listen' : 'Liste' }}</span>
</div>
</div>
<div class="col-md-6">
<div id="panel-misc-portlet-l3" class="panel panel-default text-center">
<div class="panel-heading nohover">
<span class="elipsis">
<strong>Private Tasks</strong>
</span>
</div>
</div>
<div class="alert alert-danger margin-bottom-30 {{ $todolistprivate->count() ? 'hidden' : '' }}">
Es wurden keine <strong>Einträge</strong> gefunden.
</div>
#foreach ($todolistprivate as $list)
<div id="todo-list-{{$list->id}}" class="panel panel-default panel-primary margin-bottom-0">
<div class="panel-heading panel-pointer">
<span class="elipsis"><!-- panel title -->
<strong>{{ $list->title }}</strong> <span class="label label-info white">0</span>
</span>
<ul class="options pull-right relative list-unstyled hover-visible">
<li><a data-toggle="modal" data-target=".task-modal" class="btn btn-success btn-xs white hover-hidden"><i class="fa fa-plus"></i> Erstellen</a></li>
<li><a data-toggle="modal" data-target=".todolist-modal" class="btn btn-info btn-xs white hover-hidden"><i class="fa fa-edit"></i> Bearbeiten</a></li>
<li><i class="fa fa-times"></i> Löschen</li>
<li></li>
</ul>
</div>
<div class="panel-body">
<div class="slimscroll" data-always-visible="false" data-rail-visible="false" data-railOpacity="1" data-height="100">
{{ $list->description }}
</div>
</div>
</div>
#endforeach
<div class="panel-footer mtm-10">
<span id="todo-list-counter-private">{{ $todolistprivate->count() }}</span> <span>{{ $todolistprivate->count() > 1? 'Listen' : 'Liste' }}</span>
</div>
</div>
#include('elements.addTodoList')
#include('elements.createTodoList')
#include('elements.addTask')
</div>
</div>
Controller
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|min:5',
'description' => 'required|min:10',
'privacy' => 'required|integer'
]);
$attributeNames = array(
'title' => 'Title',
'description' => 'Description',
);
$validator->setAttributeNames($attributeNames);
//Redirect back if validation fails
if($validator->fails()) {
return response()->json(['error'=>$validator->errors()->all()]);
}
else{
$todolists = new Todolists();
$todolists->admin_id = Auth::id();
$todolists->title = $request->title;
$todolists->description = $request->description;
$todolists->privacy = $request->privacy;
$todolists->save();
return response()->json(['Your enquiry has been successfully submitted!']);
}
}
EDIT
I have revised and adapted the code. Currently I have two more problems:
The flashmessage is only output as 'empty'. Without text content. Where is the problem?
The div is also reloaded. But after it was loaded I can not send the same request again. Do I have to reset something or what is the error?
When I issue the errors in the console with console.log(data); I get the following error messages:
{error: Array(2)}
error
:
(2) ["The Title ist erforderlich.", "The Description ist erforderlich."]
<script type="application/javascript">
$(document).ready(function () {
var CSRF_TOKEN = $('meta[name="csrf-token"]').attr('content');
$('#add-todo-list').click(function(e) {
e.preventDefault();
$('.todolist-create-modal').on('keypress', ":input:not(textarea)", function(event) {
return event.keyCode != 13;
});
var _token = $("input[name='_token']").val(); // get csrf field.
var title = $("input[name='title']").val();
var description = $("textarea[name='description']").val();
var privacy = $("select[name='privacy']").val();
$.ajax({
url:'{{ route('todolists.store') }}',
type: 'POST',
data: {_token:_token, title:title, description:description, privacy:privacy},
success: function (response) {
if (response.error) {
_toastr((response),"top-full-width","error",false);
}
else{
$('.todolist-create-modal').modal('hide');
$("#content").load(location.href+" #content>*","");
_toastr((response),"top-full-width","success",false);
}
}
});
});
});
</script>
Q: How can I output the data directly in the view without reloading the page?
One way with jQuery was loading partial content, this will request again the page, get the contents of #content div and replace the HTML, fast and without reload the page:
$("#content").load("/url-of-page > #content > *");
Q: How can I output errors and success messages as flashmessage (toastr) message?
Just write the message on a HTML element:
success: function(data){
$(".alert-danger").addClass("hidden");
$(".alert-success").html(data.msg).removeClass("hidden");
},
error: function(data){
$(".alert-success").addClass("hidden");
$(".alert-danger").html(data.error).removeClass("hidden");
}
I have a collection of Items that can be marked as "read" by my users.
In order to mark a collection as "read", a subdocument is added:
"readby": [
{
"action": "read",
"owner": "w5XzMrCCJJfDxCn6d"
}
]
I then use the following helper to set up an array and push any "read" entries that match the current owner. If the array has a length bigger than 0, the helper returns "true" and we know the logged in user has read this item:
itemHasBeenRead() {
var subscribers = [];
var readItems = this.readby;
if(!readItems) {
return false;
}
var readiness = readItems.forEach(function(currentSubscriber) {
// loop over current users expenses
var newSubscriber = { owner: currentSubscriber.owner };
if (currentSubscriber.owner == Meteor.userid()) {
subscribers.push(newSubscriber);
}
});
return subscribers && subscribers.length > 0
}
This all works perfectly BUT as I understand it, subdocuments aren't reactive in Meteor, so the code doesn't pick up changes reactively. Refresh the page and it works fine.
Is there a way to do this reactively, rather than just on page load?
--
Edits as requested:
--
Template code:
{{#each playlists}}
<h2 class="playlistheader">{{playlistName}}<span class="badge badge-playlist badge-playlist-first badge-primary"><i class="fas fa-users playlist-fa"></i>{{numberOfSubscribers}} enrolled</span>{{#unless isPlaylistOwner}}{{#unless userIsSubscribed}}<span class="badge badge-playlist badge-success subscribe-unsubscribed" data-id="{{this._id}}"><i class="far fa-heart playlist-fa"></i>Enrol</span>{{/unless}}{{#if userIsSubscribed}}<span class="badge badge-playlist badge-success subscribe-subscribed" data-id="{{this._id}}"><i class="fas fa-heart playlist-fa"></i>Enrolled</span>{{/if}}{{/unless}}
<span class="badge badge-playlist badge-completion" data-id="{{this._id}}"><i class="fas fa-check-circle" style="margin-right:3px;"></i>0/6 items marked complete</span>
{{#if isPlaylistOwner}}<span class="badge badge-playlist badge-danger badge-delete" data-id={{this._id}}><i class="fas fa-ban playlist-fa"></i>Delete</span>{{/if}}</h2>
<span class="playlist-subheader">{{playlistPrivacyType}} collection by {{playlistOwnerName}}</span>
<div class="row" style="display:inherit; margin-left:0px; margin-bottom:20px;margin-top:8px;">
<div class="scrolling-wrapper-playlist">
{{#if isPlaylistOwner}}
<div class="playlist-product playlist-product-add" data-playlistid="{{playlistid}}">
<div class="clampcontainer">
<div class="add-playlist-plus" data-playlistid="{{playlistid}}">+</div>
</div>
</div>
{{/if}}
{{#each playlistItems this._id}}
<div class="playlist-product" data-id={{this._id}}>
{{#if isPlaylistItemOwner}}
<div class="playlist-product-delete">
<i class="fas fa-times-circle product-delete"></i>
</div>{{/if}}
<div class="playlist-product-overlay" id="overlay-{{this._id}}" style="opacity:0;">
<div class="playlist-product-overlay-description">
"{{itemDescription}}"</div>
<div class="playlist-product-overlay-icons">
{{#unless itemHasBeenRead}}
<i class="far fa-check-circle playlist-circle"></i>
{{/unless}}
{{#if itemHasBeenRead}}
<i class="fas fa-check-circle playlist-circle"></i>
{{/if}}
{{#if hasPrice}}
<i class="fas fa-shopping-cart playlist-cart"></i>
{{/if}}
<i class="fas fa-external-link-alt playlist-external"></i>
</div>
</div>
<div style="width:90px; float:left; margin-right:10px;"><img src="{{itemImage}}" width="90"></div>
<div class="clampcontainer">
<div class="itemTitle linkColor">{{itemTitle}}</div>
<p class="itemDescription">{{itemDescription}}...</p>
<div class="fadeout"></div>
</div>
</div>
{{/each}}
</div>
</div>
{{/each}}
Publication code:
Meteor.publish('UserPlaylists', function() {
var loggedinuser = Meteor.user();
// Reveal ALL expenses if it's an admin who's logged in
return Playlists.find({
owner: loggedinuser._id
});
});
// Publish public playlists, as long as they're both public and from the same company
Meteor.publish('PublicPlaylists', function() {
var loggedinuser = Meteor.user();
var companyid = loggedinuser.userofcompanyid;
// Reveal ALL expenses if it's an admin who's logged in
return Playlists.find({
companyid: companyid,
published: true
});
});
// Publishes absolutely all expenses for superadmins
Meteor.publish('Playlists', function() {
var loggedinuser = Meteor.user();
if (loggedinuser.issuperadmin) {
return Playlists.find({});
}
});
Subscribing to the publications in the template js:
Template.Playlists.onCreated(function playlistsOnCreated() {
var self = this;
self.autorun(function() {
self.subscribe('UserPlaylists');
self.subscribe('PublicPlaylists');
self.subscribe('UserPlaylistItems');
self.subscribe('PublicPlaylistItems');
});
});
The playlists helper:
playlists() {
return Playlists.find({}, {
sort: {
timestamp: -1
}
});
},
The playlistItems helper:
playlistItems(playlistid) {
return PlaylistItems.find({
playlistid: playlistid
}, {
sort: {
timestamp: -1
}
});
},
I am getting the error from the title:
Uncaught TypeError: cloned[i].apply is not a function
at HTMLInputElement.invoker (vue.esm.js?65d7:1810)
Made standard setup with vue-cli (simple webpack), and this is my component:
<template>
<div class="column is-4">
<nav class="panel">
<p class="panel-heading">
Authors in our library
</p>
<div class="panel-block">
<p class="control has-icons-left">
<input class="input is-small" type="text" placeholder="Search"
v-model="search"
#keyup="filterAuthors">
<span class="icon is-small is-left">
<i class="fa fa-search"></i>
</span>
</p>
</div>
<a class="panel-block is-active" v-for="author in filterAuthors">
<span class="panel-icon">
<i class="fa fa-book"></i>
</span>
{{ author }}
</a>
</nav>
</div>
</template>
<script>
export default {
data () {
return {
'search' : ''
}
},
computed: {
filterAuthors() {
let search = this.search.toLowerCase();
return this.$store.state.authors.filter((author) => {
return author.toLowerCase().indexOf(search) >= 0;
})
}
}
}
</script>
Strange part is that the filter is working, but every time I type into the input field, I get this error. Anyone have any idea what can it be?
Computed properies are reactive by default, and in fact you can't attach them to event handler.
Removing the keyup event handler that calls computed property should fix the problem.
<p class="control has-icons-left">
<input class="input is-small" type="text" placeholder="Search">
<span class="icon is-small is-left">
<i class="fa fa-search"></i>
</span>
</p>
For some reason, I had a property name under data and a function with the same name under methods.
Removing the property fixed a similar error.
new Vue({
el: '#elem',
data: {
function_name: null, // <- removed this
},
methods: {
function_name: function() {
// ...
}
}
});
I just encounter a problem I have written a directive but its not getting update, I dont know why, in console it does change but in directive it does not.
Here is my directive
mainControllers.directive('mallsproduct', function () {
return {
restrict: 'E',
scope: {
productInfo: '=info',
linkid: '=linkid'
},
templateUrl: 'directives/dashboard_product.html'
};
});
Here is my `html`
<div class="aa-properties-content-body mg-7" ng-controller="DashboardController as ctrl">
<ul class="aa-properties-nav aa-list-view">
<li style="border: 1px solid #ccc;margin-bottom: 25px;" ng-repeat="active_products in productInfo.items">
<article class="aa-properties-item mg-top-0-notimp">
<a class="aa-properties-item-img" href="#/product/{{active_products.id}}">
<img ng-if="active_products.photos[0].path" resize-image alt="img" class="" src="{{active_products.photos[0].path}}">
<img ng-if="!active_products.photos[0].path" resize-image class="" src="img/default_product.jpg" alt="">
</a>
<div class="aa-properties-item-content">
<div class="aa-properties-about padding-0-notimp">
<h5>{{active_products.name| limitTo : 10}}{{active_products.name.length > 10 ? '...' : ''}}</h5>
<p class="font-size-11-imp"><i class="fa fa-building-o" aria-hidden="true"></i> {{active_products.mall.name| limitTo : 10}}{{active_products.mall.name.length > 10 ? '...' : ''}}</p>
<p class="font-size-11-imp"><i class="fa fa-map-marker" aria-hidden="true"></i> {{active_products.mall.address| limitTo : 10}}{{active_products.mall.address.length > 10 ? '...' : ''}}</p>
<p class="font-size-11-imp"><i class="fa fa-phone" aria-hidden="true"></i> {{active_products.shop.telephone}}</p>
<p class="font-size-11-imp" ng-if="linkid == 3"><i class="fa fa-eye" aria-hidden="true"></i> {{active_products.views}}</p>
<div class="modal-demo">
<script type="text/ng-template" id="myModalContent.html">
<div ng-include src="'partials/update_product.html'"></div>
</script>
<div ng-controller="AddProductController">
<button ng-click="view_product(active_products.id)"><i class="fa fa-pencil" aria-hidden="true"></i></button>
<button ng-click="del_product(active_products.id)"><i class="fa fa-trash-o" aria-hidden="true"></i></button>
<button ng-if="linkid == 2" ng-init="status = 1" ng-click="reactivate_product(active_products.id, status)"><i class="fa fa-lock" aria-hidden="true"></i></button>
</div>
<div class="modal-parent">
</div>
</div>
</div>
</div>
</article>
</li>
</ul>
<div class="aa-title pad-top-30" ng-if="linkid == 1">
<p>Global page count for active product is {{global_pagecount}} and active product count from API is {{productInfo._meta.pageCount}}</p>
<h3 ng-if="global_pagecount < productInfo._meta.pageCount" class="text-align-center color-feroz cursor-pointer" ng-click="load_more(global_pagecount, linkid)">{{$root.translated_labels.dashboard.load_more}}</h3>
</div>
<div class="aa-title pad-top-30" ng-if="linkid == 3">
<p>Global page count for most viewed is {{global_pagecount_mostv}} and most viewed count from API is {{productInfo._meta.pageCount}}</p>
<h3 ng-if="global_pagecount_mostv < productInfo._meta.pageCount" class="text-align-center color-feroz cursor-pointer" ng-click="load_more(global_pagecount_mostv, linkid)">{{$root.translated_labels.dashboard.load_more}}</h3>
</div>
</div>
I am including directive in dashboard partial like this
<div class="active tab-pane" ng-if="linkid === '1'">
<malls-product info="active_products" linkid="linkid"></malls-product>
</div>
<!--Active products list ends here -->
<!-- Get Inactive Products -->
<div class="active tab-pane" ng-if="linkid === '2'" >
<malls-product info="$root.inactive_products" linkid="linkid"></malls-product>
</div>
<!--Get Inactive products ends here -->
<div class="active tab-pane" ng-if="linkid === '3'" >
<malls-product info="$root.mostviewed_products" linkid="linkid"></malls-product>
</div>
<!-- View Profile-->
and This is the api which does show the result in console.
$scope.global_pagecount = 1;
$scope.active_product = function () {
$http.get($rootScope.baseurl + 'abc?&page=' + $scope.global_pagecount,
{headers:
{'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': $rootScope.keyword_auth_token, 'Accept-Language': $cookies.get('type')}
})
.success(function (data) {
//$scope.active_product_pageCount = data._meta.pageCount;
if ($scope.global_pagecount === 1) //I know for sure the first page of pagination is 1
{
$scope.active_products = data;
}
if ($scope.global_pagecount > 1) // If user click load more global count gets incremented and new results push in active_producst
{
/* for loading new results Pagination Applied */
for (var times = data.items.length - 1; times >= 0; times--) {
$scope.active_products.items.push(data.items[times]);
}
}
console.log($scope.active_products);
})
.error(function (data) {
// console.log(data);
});
};
What is the issue, why it is not getting update, If I use rootscopethen it works fine, obviously it has too, but not with $scope.
Note : when scope.global_pagecount value is equal to 2 i get new results but not in directive only in console. By default scope.global_pagecount has value equal to 1.
You don't use your directive correctly. You define it as:
mainControllers.directive('mallsproduct'
Which means you should use it as:
<mallsproduct ..>
Or define your directive camelcased:
mainControllers.directive('mallsProduct'
Then you can use it as you do now:
<malls-product ..>
This is because of the Isolated scope doesn’t know anything about its parent scope. You just created a directive with an isolated scope.
To access any parent scope data, we need to pass the scope data to our directive explicitly. This is achieved by setting properties on the scope object in the DDO.
Another important thing is that, these properties also MUST be set as the attributes of the directive html element.
I'm new to Angular, but it seems to be the "Angular way" to avoid DOM manipulation whenever possible. I am not sure if I'm taking the right approach.
I have three buttons on the bottom (next,back, and home). These get enabled and disabled by way of the actions of other controllers. For example if a text box is NULL the next button will be disabled until something is entered. I have the following:
<div ng-controller="nextBackController" class="navbar navbar-app navbar-absolute-bottom">
<div class="btn-group justified">
<i class="fa fa-arrow-left fa-navbar" ng-class="{'btn-disabled' : btnBack}"></i> Back
<a id="btnHome" ng-click="handleClick()" href="#/" class="btn btn-navbar btn-icon-only btn-bounded" ng-class="{'btn-disabled' : btnHome}"><i class="fa fa-home fa-navbar"></i> Home</a>
Next <i class="fa fa-arrow-right fa-navbar" ng-class="{'btn-disabled' : btnNext}"></i>
</div>
</div>
And now for the JS:
app.service("nxtBackClick", function($rootScope, bottomButtonWatcher) {
this.btn = "";
this.broadcast = function(btn) {
//Toggle the Next button as an example
bottomButtonWatcher.btnNext = ~bottomButtonWatcher.btnNext;
//set the btn that is clicked
this.btn = btn;
$rootScope.$broadcast('bottomBtnClick');
};
});
app.service("bottomButtonWatcher", function($rootScope) {
//Set all these to disabled
this.btnHome = true;
this.btnNext = false;
this.btnBack = true;
});
And now the controller:
//####################### - Next Back Controller ########################
app.controller("nextBackController",
function($scope, nxtBackClick, bottomButtonWatcher){
$scope.btnHome = bottomButtonWatcher.btnHome;
$scope.btnNext = bottomButtonWatcher.btnNext;
$scope.btnBack = bottomButtonWatcher.btnBack;
$scope.handleClick = function(btn) {
nxtBackClick.broadcast(btn);
};
}
);
Having both a class attribute and an ng-class attribute can be problematic. They should both be condensed into a single ng-class attribute. You are already passing an object, so just add another property to it. Setting classes to always be shown is accomplished by setting their property to true.
<div ng-controller="nextBackController" class="navbar navbar-app navbar-absolute-bottom">
<div class="btn-group justified">
<a href="#/" class="btn btn-navbar btn-icon-only btn-bounded">
<i ng-class="{'fa fa-arrow-left fa-navbar': true, 'btn-disabled' : btnBack}"></i>
Back
</a>
<a id="btnHome" ng-click="handleClick()" href="#/" ng-class="{'btn btn-navbar btn-icon-only btn-bounded':true, 'btn-disabled' : btnHome}">
<i class="fa fa-home fa-navbar"></i>
Home
</a>
<a href="#/second" class="btn btn-navbar btn-icon-only">
Next
<i class="" ng-class="{'fa fa-arrow-right fa-navbar':true, 'btn-disabled' : btnNext}"></i>
</a>
</div>
</div>
You're using $rootScope.$broadcast but nobody is listening, you need to have a scope watcher in your controller that actually controls the buttons:
//####################### - Next Back Controller ########################
app.controller("nextBackController",
function($scope, nxtBackClick, bottomButtonWatcher){
$scope.btnHome = bottomButtonWatcher.btnHome;
$scope.btnNext = bottomButtonWatcher.btnNext;
$scope.btnBack = bottomButtonWatcher.btnBack;
$scope.handleClick = function(btn) {
nxtBackClick.broadcast(btn);
};
$scope.$watch('bottomBtnClick', function(buttonVal){
$scope[buttonVal] = true;
});
}
);