Purpose
I am trying to get admin and customer show in different stages, admin can post the data after clicking the toggleShowDiv(), which allows customer to see the data.
Question
How to pass !isAdmin() into ng-if? Currently, I am only getting isAdmin as default.
Is able to post it into table TD by TD (row by row)? not sure, I am writing the correct code here.
My thought
Can I use ng-if to each single TD = isAdmin() or !isAdmin, and control by a click function?
$scope.showDiv = isAdmin();
$scope.toggleShowDiv = function (auction) {
var title = 'text.......';
var text = 'are you sure?';
ConfirmModal(title, text, function () {
$scope.showDiv = !isAdmin() ;
});
};
HTML
<div ng-if="showDiv">
<tbody class="auction-group" ng-repeat="a in foos">
<td ng-if="isAdmin()">
<input type="checkbox" ng-click="toggleShowDiv()" />
</td>
</div>
Update
isAdmin() is just a function that passed from the backend.
function isAdmin() {
return !!($aScope.currentUser && $aScope.currentUser.isAdministrator);
}
Please note: the question is not about the isAdmin() function, it works fine. What I want to do is to use a click function to show and hide the table row.
Have a look at this. Here you have 2 users online at the same time, dude1 (admin) and dude2 (non admin). You can toggle the display from the admin side for the non admin side by having a call to the back end that continuously checks if the display is valid or not. For putting a toggle on the table rows you need to just add the ng-if to the <tr> elements.
var app = angular.module('app', []);
app.controller("controller", function($scope) {
$scope.dude1 = {admin: true, name: [{name: 'A+', country:'India', publish: true}, {name: 'A', country:'Unknown', publish: true}]};
$scope.dude2 = {admin: false, name: [{name: 'A+', country:'India', publish: true}, {name: 'A', country:'Unknown', publish: true}]};
$scope.toggler = (index) => {
$scope.dude1.name[index].publish = !$scope.dude1.name[index].publish;
};
$scope.names = (dude) => {
return dude.name;
};
setInterval(() => {
/**
* Any backed function to get and repopulate the data.
* Update the value of publish from the server. I'm just using
* the other guys data. But you should fetch it from the server.
*/
$scope.dude2 = valfromServer();
// console.log($scope.dude2, $scope.dude1);
}, 2000);
var valfromServer = () => {
return {
admin: false,
name: $scope.dude1.name
};
};
$scope.publish = (dude, index) => {
return dude.admin || dude.name[index].publish;
};
$scope.isAdmin = (dude) => {
return dude.admin;
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.6.0" data-semver="1.6.0" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>
</head>
<body ng-app="app" ng-controller="controller">
<span>Admin Panel</span>
<div>
<table style="width:40%">
<tr ng-repeat="x in names(dude1)" ng-if="publish(dude1, $index)">
<td>{{ x.name }}</td>
<td>{{ x.country }}</td>
<td>{{ $index }}</td>
<td><button ng-click="toggler($index)" ng-if="isAdmin(dude1)">Publish</button></td>
</tr>
</table>
</div>
<hr>
<span>Non admin panel</span>
<div>
<table style="width:40%">
<tr ng-repeat="x in names(dude2)" ng-if="publish(dude2, $index)">
<td>{{ x.name }}</td>
<td>{{ x.country }}</td>
<td>{{ $index }}</td>
<td><button ng-click="toggler($index)" ng-if="isAdmin(dude2)">Publish</button></td>
</tr>
</table>
</div>
</body>
</html>
<div ng-if="showDiv == true || isAdmin == true">
<tbody class="auction-group" ng-repeat="a in foos">
<td ng-if="isAdmin == true">
<input type="checkbox" ng-click="toggleShowDiv()" />
</td>
</div>
JS code Let say first any one who enters will be customer
$scope.showDiv = false;
$scope.isAdmin = false;
now when response comes form backend check the response and change the value of $scope.isAdmin accordingly.
if(response == admin){
$scope.isAdmin= true;
}else{
$scope.isAdmin = false;
}
now in onclick checkbox function
$scope.toggleShowDiv = function (auction) {
var title = 'text.......';
var text = 'are you sure?';
ConfirmModal(title, text, function () {
if($scope.showDiv == false){
$scope.showDiv = true;
}else{
$scope.showDiv = false;
}
});
};
Well I think that you should use some var that change according if the user click like $scope.showTable = true /false. But not complety sure about your real need.
I am confused on your question -
I will suggest few points, i hope it will help you -
ng-if is a inbuilt directive. You can use it on any DOM element. You can control it by using attribute or function, only need to pass Boolean attribute to this directive. Eg:
ng-if="showHideAttribute" or ng-if="functionNameWhichReturnBoolean()"
Scope - if you are clicking on button/checkbox/ng-click applied element is available in the same scope of applied ng-if directive then no problem. Otherwise you need to use service or observers (on/emit/broadcast) or rootScope then only it will work.
I hope you are receiving isAdmin = true/false from backend in your function. So, i am thinking this is the problem of scope.
Instead of ng-if="showDiv" use something link ng-if="obj.showDiv"
In the controller define $scope.obj = {};
The issue is ng-if creates its own scope, so always pass data as an object because objects in JS are passed by reference.
You can do this
ng-if = "isAdmin == false"
You're really confusing me, but if I understood correctly, it is something like this you want?
First things first, your HTML is truely horrible, parts of tables in divs? Don't do that...
Secondly, don't hack kabout with the isAdmin to toggle things.
isAdmin should only be used to check if a user is an admin.
You can however create another variable that instantiates to the same value, and use that one to toggle stuff.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
this.content = 'This is some unpublished content, only the admin can view, unless you\'ve now gone and publish it.';
this.isPublished = false;
this.isAdmin = false;
});
/* Put your css in here */
textarea,
label,
button {
display: block;
margin: 15px 15px 0 0;
}
button {
display: inline-block;
}
<script data-require="angular.js#1.5.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
<div ng-app="plunker">
<div ng-controller="MainCtrl as $ctrl" ng-init="$ctrl.isAdmin=false">
<article>
<section class="content">
<section ng-if="$ctrl.isAdmin || $ctrl.isPublished">{{ $ctrl.content }}</section>
<section ng-if="!$ctrl.isAdmin && !$ctrl.isPublished"><pre>-- no published content found --</pre></section>
</section>
<section class="admin-only" ng-if="$ctrl.isAdmin">
<label><input type="checkbox" ng-model="$ctrl.isPublished"> publish article</label>
</section>
</article>
<hr />
<label><input type="checkbox" ng-model="$ctrl.isAdmin"> is admin</label>
</div>
</div>
edit:
You're still confusing me, but does this come closer to what you want / need?
What i understand is, "When admin trigger some action i.e. ng-click, user should be able to see that data/changes."
If this is the case, consider following:
I'm damn sure that this is not going to happen on the same machine.
Admin will be using the application on his machine from where it'll
do some action i.e. ng-click will gets fired and some data will gets
changed on server.
Now there will be (n) number of users using the application from
their machines. How they will get to know about the change which admin has made?
In such scenario, when there is changes on server and
client(browser) should be aware of that we make use of socket.io,
which listens to events from server and refreshes itself when there
is some changes on server state or we can say, when admin has
triggered some action i.e. ng-click.
Let me know, if you have any queries.
Thanks
Related
I have a table with an edit and delete button. But now I also want to make a clone button.
The clone button should work as follows: It clones almost all the data (data such as the id he cannot take) from the row that the user has clicked. Then it goes to the edit page and there it fills the data in the input/select values.
But I have no idea how I get this done.
I have now a function which output all the data: var cloneJob = angular.extend(job);
Then it goes to the edit page location.href = '#/jobs/add';
But the problem is that he doesn't fill the input/select values.
Does AngularJS has a function for this? And am I on the right track or do I need to do something else?
UPDATE
Here is a litle bit more code:
This is my the code of my table:
<tr ng-repeat="job in (filtered.rows = (jobs | orderBy: orderByDate:true | filter:filterByActive | filter:filter.query)) | skip:pagination.pageSkip() |limitTo:pagination.perPage" ng-class="{ inactive : !job.active }" style="cursor: pointer;">
<td>
<span ng-bind="job.title"></span>
</td>
<td>
<span ng-bind="job.client.name"></span>
</td>
<td>
<span ng-bind="job.referenceNumber"><span>
</td>
<td>
<span ng-bind="job.creationDate"><span>
</td>
<td>
<a ng-href="#/jobs/edit/{{job.id}}/tab/candidates" ng-bind="job.candidates.length"></a>
</td>
<td>
<span class="status" ng-class="job.status.value"></span>
</td>
<td>
<a ng-if="job.active" ng-href="#/jobs/edit/{{job.id}}" class="icon go">
<span class="tooltip" translate="job_name_details"></span>
</a>
<a ng-if="job.active" class="icon close" ng-click="showClosePopup(job)">
<span class="tooltip" translate="job_close"></span>
</a>
<a ng-click="cloneJob(job)" ><span>Clone!</span></a>
<!-- <button data-ng-click="cloneItem(food)" class="btn inline">Add</button> -->
</td>
</tr>
Function cloneJob is:
$scope.cloneJob = function (job){
var cloneJob = angular.extend(job);
location.href = '#/jobs/add';
}
This outputs a lot of json (all the correct data) and it goes to the add page.
Try something like
<tr ng-repeat="whatever in whatevers"><button ng-click="duplicateItem(whatever)">duplicate</button></tr>
And on the controller:
$scope.duplicateItem = function(item){
$scope.duplicatedItem = angular.copy(item); //this will do a copy, not just assign a reference.
//if you need clean the duplicate item
delete $scope.somePropertyYouWannaClean;
}
It would better if you provided a working example fiddle or at least more code, so we can give you more accurate answers.
Edit:
A cleaner way would be to make the clone function load the info into a service (or factory, a singleton). Then after loading the route you use that service to get the content back and play with it.
Like:
angular.module('some.namespace.factory', [])
.factory('CloneJobFactory', function () {
return {
job: null,
loadJob: function (job) {
var auxJob = angular.copy(job);//if you just need a shallow copy use angular.extend
this.job = this.cleanJob(auxJob);
},
getClonedJob: function(){
return this.job;
},
cleanJob: function(job) {
//code that cleans a job object that has been cloned
delete job.propertyYouDoNotWantToKeep;
return job;//return the cleaned job
}
};
});
Then the clone function that would be in the controller (that now has to inject the factory we just made) just has to wrap the loadJob method:
$scope.cloneJob = function (job) {
CloneJobFactory.loadJob(job);
}
The same for the function that would use the cloned data:
$scope.someFunction = function (whateverParams) {
var clonedJob = CloneJobFactory.getClonedJob();
//whatever you want
}
This can still be improved.
NOTE: Angular singletons are made to, among other things, share info between controllers, services and so on.
Make a new 'clone' route, that uses the same controller and view as your 'add' route, but passes in the id of the job that should be cloned:
.when('/jobs/add', { templateUrl: 'jobs/add', controller: 'AddController' })
.when('/jobs/clone/:id', { templateUrl: 'jobs/add', controller: 'AddController' })
Then, in the AddController, check if an id has been passed using $routeParams. If there is an id, fetch the job with the id, and initialize the model by cloning the job. If there's no id, initialize the model with an 'empty' job.
myModule.controller('AddController', function($scope, $routeParams){
if($routeParams.id) {
//TODO get existing job using $routeParams.id
$scope.newJob = angular.copy(job);
} else {
$scope.newJob = {};
}
});
This is my partial view settings.html
<div ng-controller="settingsController">
<h3>General Settings</h3>
<table class="table">
<tbody>
<tr class="success">
<td class = "col-xs-3">Language:</td>
<td class = "col-xs-9">Gmail display language:
<select ng-model = "userPreferences.selectedLang" ng-options="language for language in languages">
<option value = ""> Choose a language </option>
</select>
</td>
</tr>
<tr class="danger">
<td class = "col-xs-3">Conversation View</td>
<td class = "col-xs-9">
<input type = "radio" name = "conversation" id = "converseOn" ng-model="userPreferences.converse.state" value = "on" checked>
<label for="converseOn"> Conversation view on </label>
<aside>
<input type = "radio" name = "conversation" ng-model="userPreferences.converse.status" value = "off" id = "converseOff">
<label for="converseOff"> Conversation view off </label>
</aside>
</tr>
<tr class="info">
<td class = "col-xs-3">Maximum page size: </td>
<td class = "col-xs-9"> Show conversations per page
<select ng-model = "userPreferences.selectedNumber" ng-options="conversation for conversation in conversations">
<option value = ""> Choose the no.of conversations to display </option>
</select>
<aside id = "pageSize" > Show contacts per page
<select ng-model = "selectedNumberContacts" ng-options="contact for contact in contacts">
<option value = ""> Choose the no.of contacts to display </option>
</select>
</aside>
</td>
</tr>
</tr>
</tbody>
</table>
<div class="form-actions" >
<button type="submit" class="btn btn-primary" ng-click = "setPreference()">Save changes</button>
<button type="button" class="btn">Cancel</button>
</div>
</div>
This is the settingsController.js
(function() {
'use strict';
var settingController = function (fetchDataService, $scope, savePreferenceService) {
var url = 'app/mock/settings.json';
fetchDataService.getContent(url)
.then(function(response){
$scope.contacts = response.data.contacts;
$scope.languages = response.data.languages;
$scope.conversations = response.data.conversations;
$scope.undoSend = response.data.undoSend;
});
$scope.userPreferences = {
selectedLang : '',
converse : {
state: 'on'
},
selectedNumber : 0
}
$scope.setPreference = function () {
savePreferenceService.selectedPreferences($scope.userPreferences.selectedLang, $scope.userPreferences.converse.state, $scope.userPreferences.selectedNumber);
}
};
angular.module('iisEmail')
.controller ('settingsController',
['fetchDataService', '$scope', 'savePreferenceService', settingController]);
}());
This is the savePreferenceService.js
(function() {
'use strict';
var savePreferenceService = function () {
this.selectedPreferences = function (selectedLang, converse, selectedNumber) {
this.selectedLang = selectedLang;
this.converse = converse;
this.selectedNumber = selectedNumber;
console.log(this.selectedLang);
console.log(this.converse);
console.log(this.selectedNumber);
}
};
angular.module('iisEmail')
.service ('savePreferenceService', savePreferenceService);
}());
So, basically my goal is to save user preferences when the Save changes button is clicked.
To achieve this functionality, I have defined an object in the controller, and binded its properties to the view using ng-model. When the save changes button is clicked, the setPreference() function is called which makes a service savePreferenceService call. The savePreferenceService saves options chosen by the user. Everything works as expected.
I just want to know if there is a better way of doing this (in terms of best practices). My requirement is to save the options chosen by a user in a service.
I'm not really seeing how data could possibly be saved when you do something like a page refresh, unless you're not showing the code here?
If you want to preserve data saves whenever the user visits your page, you can always use localstorage, which stores info in the user's browser. There are various web tutorials on how to utilize localstore, but this website (which didn't take long to find), includes and extends localstorage as a service, and to use that service as a way to store data in there.
http://learn.ionicframework.com/formulas/localstorage/
(code below is taken from this link)
angular.module('ionic.utils', [])
.factory('$localstorage', ['$window', function($window) {
return {
set: function(key, value) {
$window.localStorage[key] = value;
},
get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
setObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value);
},
getObject: function(key) {
return JSON.parse($window.localStorage[key] || '{}');
}
}}]);
and to use this service:
angular.module('app', ['ionic', 'ionic.utils'])
.run(function($localstorage) {
$localstorage.set('name', 'Max');
console.log($localstorage.get('name'));
$localstorage.setObject('post', {
name: 'Thoughts',
text: 'Today was a good day'
});
var post = $localstorage.getObject('post');
console.log(post);
});
Or you could just use localstorage the old fashion way using plain old javascript..
https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
Sounds like what you have is working for you. Not sure exactly what you mean by 'I just want to know if there is a better way of doing this' since that is pretty subjective.
What I do notice right away is that the current implementation makes your user preferences session based because they are only retained in-memory, meaning that if the user uses the app, makes changes to the preferences, leaves the app and returns, the changes are lost. If you need to preserve them, then I would suggest using LocalStorage or actually storing them in some type of data store (file, database, ...).
Also, I would suggest making your service the owner of the preferences data instead of splitting the responsibility between the controller and the service. What I mean by that is that the default preferences are owned by the controller, but only when updated do they make it to the service. What I would do instead is
change the service to have a get method and a set method. Perhaps getPreferences() and setPreferences(...).
change the controller to call the getPreferences method instead of assigning a static preferences object
call the setPreferences method from the controller's setPreference method
I have a list of checkboxes for people, and I need to trigger an event that will display information about each person selected in another area of the view. I am getting the event to run in my controller and updating the array of staff information. However, the view is not updated with this information. I think this is probably some kind of scope issue, but cannot find anything that works. I have tried adding a $watch, my code seems to think that is already running. I have also tried adding a directive, but nothing in there seems to make this work any better. I am very, very new to Angular and do not know where to look for help on this.
My view includes the following:
<div data-ng-controller="staffController as staffCtrl" id="providerList" class="scrollDiv">
<fieldset>
<p data-ng-repeat="person in staffCtrl.persons">
<input type="checkbox" name="selectedPersons" value="{{ physician.StaffNumber }}" data-ng-model="person.isSelected"
data-ng-checked="isSelected(person.StaffNumber)" data-ng-change="staffCtrl.toggleSelection(person.StaffNumber)" />
{{ person.LastName }}, {{ person.FirstName }}<br />
</p>
</fieldset>
</div>
<div data-ng-controller="staffController as staffCtrl">
# of items: <span data-ng-bind="staffCtrl.infoList.length"></span>
<ul>
<li data-ng-repeat="info in staffCtrl.infoList">
<span data-ng-bind="info.staffInfoItem1"></span>
</li>
</ul>
</div>
My controller includes the following:
function getStaffInfo(staffId, date) {
staffService.getStaffInfoById(staffId)
.then(success)
.catch(failed);
function success(data) {
if (!self.infoList.length > 0) {
self.infoList = [];
}
var staffItems = { staffId: staffNumber, info: data };
self.infoList.push(staffItems);
}
function failed(err) {
self.errorMessage = err;
}
}
self.toggleSelection = function toggleSelection(staffId) {
var idx = self.selectedStaff.indexOf(staffId);
// is currently selected
if (idx >= 0) {
self.selectedStaff.splice(idx, 1);
removeInfoForStaff(staffId);
} else {
self.selectedStaff.push(staffId);
getStaffInfo(staffId);
}
};
Thanks in advance!!
In the code you posted, there are two main problems. One in the template, and one in the controller logic.
Your template is the following :
<div data-ng-controller="staffController as staffCtrl" id="providerList" class="scrollDiv">
<!-- ngRepeat where you select the persons -->
</div>
<div data-ng-controller="staffController as staffCtrl">
<!-- ngRepeat where you show persons info -->
</div>
Here, you declared twice the controller, therefore, you have two instances of it. When you select the persons, you are storing the info in the data structures of the first instance. But the part of the view that displays the infos is working with other instances of the data structures, that are undefined or empty. The controller should be declared on a parent element of the two divs.
The second mistake is the following :
if (!self.infoList.length > 0) {
self.infoList = [];
}
You probably meant :
if (!self.infoList) {
self.infoList = [];
}
which could be rewrited as :
self.infoList = self.infoList || [];
I have following code in my html where I want to toggle input and span fields. In a table row.
<table>
<tbody ng-repeat="(i, cont) in char.items">
<tr>
<td>
<div>
<a ng-click="choose()">
<input type="text" ng-model="item.desc" ng-show="sho==1" />
<span ng-show="sho==0">{{item.type}}</span></a>
</div>
</td>
</tr>
</tbody>
</table>
<div ng-click="addRows(char)" style="WIDTH: 974px">Add Row</div>
In my controller I have
app.controller("testCtrl", function($scope) {
$scope.sho=0;
$scope.addRows = function(char) {
if (typeof char.items == 'undefined') {
char.items = [];
}
char.items.push({ des: '', type: '', price: '', charge__id: ''});
};
$scope.choose= function() {
//some values are retrieved than I want to toggle so it shows the
//want to set sho=1 so input is hidden instead the span vaue is shown
$scope.sho=1;
};
});
Problem is when I set $scope.sho=1; it shows span value in all the row of the table.
While I add a new row I just want to show the input box leaving the other rows already inserted with span values.
Pleae let me know how can i set ng-show for each row in table.
Thanks
Since ng-repeat creates a child scope for each item you can leverage that within a directive. The parent scope of the directive will be the child scope created by ng-repeat and therefore isolated from other repeaters
Move your choose and sho out of main controller and put them into directive scope.
<div editable>
<a ng-click="choose()"></a>
<input type="text" ng-model="item.desc" ng-show="!sho" />
<span ng-show="sho">{{item.type}}</span>
</div>
app.directive('editable', function () {
return function (scope, elem, attrs) {
scope.sho = true;
scope.choose = function () {
scope.sho = !scope.sho;
}
}
});
This is the simplest version possible without going to isolated scope within the directive and without considering factors like more than one of these editables in a row.
For more insulated feature rich version would consider using a more robust directive like x-editable
I have trouble understanding what your code is actually used for. But my guess would be for you to pass the current item into the choose function and set a flag on the item itself. If you modify your ng-show and ng-hide attributes to react to this flag on each item, I guess you would reach your goal.
<a ng-click="choose(item)">
<input type="text" ng-model="item.desc" ng-show="item.sho==1" />
<span ng-show="item.sho==0">{{item.type}}</span></a>
</div>
And in your choose function you would do something like this:
$scope.choose= function(item) {
item.sho=1;
};
This is only a wild guess though, since it isn't quite clear to me what you are trying to accomplish.
Two things that come to mind immediately are:
1 - Pass in the item with the function and have the function accept an argument.
<a ng-click="choose(sho)">
and then in your controller
$scope.choose= function(sho) {
sho = 1;
};
2 - Just make ng-click set the value to one..
<a ng-click="sho = 1">
I have an array of users, I want to have my ng-repeat ordered by last name when first loaded. After a new user is added have the ng-repeat ordered by dated added then last name. Essentially I want the newest users pushed to the top of the ng-repeat.
<th ng-click="menuFilter('lastName', 1);">
<div ng-class='{"menuSort":sortColumn==1}'>Name <span ng-show="share.orderByField == 'lastName'">
</div>
</th>
<tr ng-repeat="user in users | orderBy:orderByField:reverseSort"></tr>
In my JS...
_this.sortColumn = 1;
_this.orderByField = 'lastName';
_this.reverseSort = false;
_this.menuFilter = function(section, column) {
_this.orderByField = section;
_this.reverseSort = !_this.reverseSort;
_this.sortColumn = column;
};
//my attempt to reset the order by created at date
if( _this.isRefreshing ) {
_this.orderByField = ['createdAt', 'lastName'];
}
Basically this code is not doing anything. I think I am missing a step in the HTML.
Thanks!
I think this is easiest done by sorting the array in pure javascript and then using a ng-repeat without the orderBy attribute.
HTML:
<div ng-repeat="user in users">{{user}}</div>
<input type="text" ng-model="name"></input>
<button ng-click="addName()">Add name</button>
JS:
$scope.users = ["Rusty", "Shackleford", "Dale", "Gribble"];
$scope.users.sort();
$scope.addName = function() {
$scope.users.unshift($scope.name);
}
JSFiddle: http://jsfiddle.net/asWF9/2/
This answer may help to sort your array: https://stackoverflow.com/a/6712080/3675149
Try using "unshift" instead of 'push' to add an item into the array. The unshift in js enables us to insert an item to the top of an array.