Here's the problem - a limited number of licences can be assigned to users, when the available number is 0 no more can be assigned and other buttons will be disabled. Licences can removed and reassigned.
The list of users is in an ngRepeat loop, and the assign / remove licence function is in a component. When I click the assign / remove button it updates itself and the total, but the button in other components don't update until the next click.
Here's the full code of what I have so far: http://plnkr.co/edit/T4soR8qpSAzY0cANknsE?p=preview
The HTML:
<body ng-controller="RootController as root">
<pre>qty: {{ root.qtyAvailable }} / {{ root.qtyMax }}</pre>
<div ng-repeat="user in root.users | orderBy: 'firstname' ">
{{ user.firstname }}
<assign
has-licence="user.hasLicence"
reassignable="user.reassignable"
qty="root.qtyAvailable"
qty-max="root.qtyMax"
></assign>
</div>
</body>
The controller and component:
.controller('RootController', function() {
this.qtyMax = 2;
this.qtyAvailable = 1;
this.users = [
{firstname: 'john', hasLicence: false, reassignable: true},
{firstname: 'jane', hasLicence: false, reassignable: true},
{firstname: 'joey', hasLicence: false, reassignable: true},
{firstname: 'bob', hasLicence: true, reassignable: true},
];
})
.component('assign', {
template: `<button ng-click="$ctrl.click($ctrl.hasLicence)">{{ $ctrl.text }}</button>`,
controller: function() {
this.text = '';
// set the button text
this.buttonText = function() {
if(this.hasLicence) {
this.text = 'remove';
}
else if(!this.hasLicence && this.reassignable && this.qty>0) {
this.text = 'assign';
}
else {
this.text = '-'; // eg button disabled
}
}
this.buttonText();
// click function
this.click = function(licence) {
if(licence === true) {
this.hasLicence = false;
this.qty++
}
else if(this.qty>0) {
this.hasLicence = true;
this.qty--
}
this.buttonText(this.hasLicence);
console.log(this.qty)
}
},
bindings: {
hasLicence: '<',
reassignable: '<', // not relevant for this demo
qty: '=',
qtyMax: '<'
}
});
Something like this:
template: `<button ng-disabled="$ctrl.qty <= 0 && !$ctrl.hasLicence" ng-click="$ctrl.click($ctrl.hasLicence)">{{ $ctrl.text }}</button><span ng-if="$ctrl.qty <= 0 && !$ctrl.hasLicence">No licenses are free</span>`
Using extendend syntax : ng-disabled="$ctrl.qty <= 0 && !$ctrl.hasLicence" for only disabling the buttons to add a license when the 'free licenses' var is <= 0.
Updated Plunkr
If you want to execute the buttonText() function specifically you can add a watch on the qty variable and execute it:
.component('assign', {
template: `<button ng-click="$ctrl.click($ctrl.hasLicence)">{{ $ctrl.text }}</button>`,
controller: function($scope) { // $scope injection here
...
// Note: you can use arrow functions to omit the assignment of context
var me = this;
$scope.$watch(function() {
return me.qty;
}, function() {
me.buttonText();
});
},
bindings: {
...
}
});
Updated plunker here: plunkr
Related
I'm designing a game in Vue JS called Higher or Lower. The code below will trigger an alert to be shown on the webpage every time one of the buttons is clicked. However, the alert does not disappear after a button is clicked again.
I want to make is so that the current alert disappears after a button click so that the next alert shows up. That way, there is only one relevant alert on the webpage.
This is what the webpage looks like:
<template>
<div class="main">
<h2>
{{ message }}
</h2>
<div class="alert alert-success" v-if="correctMessage">
{{ correctMessage }}
</div>
<div class="alert alert-danger" v-if="incorrectMessage">
{{ incorrectMessage }}
</div>
<h3>
The current number is: <strong> {{ currentNumber }} </strong>
</h3>
<h4 class="score"> SCORE: {{ score }} / 8 </h4>
<p> Is the next number higher or lower? </p>
<button #click="guessHigher" class="button1">
Higher
</button>
<button #click="guessLower" class="button2">
Lower
</button>
<p> {{ numbers }} </p>
</div>
</template>
<script>
export default {
name: "HighLow",
data: function () {
return {
// the collection of numbers that the user will need to progress thru
numbers: [],
// the users current guess
guess: null,
// the users score
score: 0,
// Some info text to display to the user after their guess
correctMessage: '',
incorrectMessage: ''
}
},
computed: {
currentNumber: function () {
return this.numbers[this.score];
},
nextNumber: function () {
return this.numbers[this.score + 1];
}
},
methods: {
// Prepare the numbers array
// reset the game logic
initGame: function () {
this.score = 0;
this.guess = null;
this.numbers = this.generateNumbers(10)
},
guessHigher: function () {
// work out if the guess is correct...
var correct = this.nextNumber > this.currentNumber;
if (correct)
{
this.correctMessage = 'Correct!';
this.score++;
}
else
{
this.incorrectMessage = 'Incorrect! Game Over!';
this.shuffle(this.numbers);
this.score = 0;
}
if (this.score === 8)
{
Swal.fire({
title: 'Brilliant. Absolutely brilliant. You are not a monkey after all.',
text: 'Play again?',
imageUrl: 'https://unsplash.it/400/200',
imageWidth: 400,
imageHeight: 200,
imageAlt: 'Custom image',
})
this.shuffle(this.numbers);
this.score = 0;
}
},
guessLower: function () {
// work out if the guess is correct...
var correct = this.nextNumber < this.currentNumber;
if (correct)
{
this.correctMessage = 'Correct!';
this.score++;
}
else
{
this.incorrectMessage = 'Incorrect! Game Over!';
this.shuffle(this.numbers);
this.score = 0;
}
if (this.score === 8)
{
Swal.fire({
title: 'Brilliant. Absolutely brilliant. You are not a monkey after all.',
text: 'Play again?' ,
imageUrl: 'https://unsplash.it/400/200',
imageWidth: 400,
imageHeight: 200,
imageAlt: 'Custom image',
})
this.shuffle(this.numbers);
this.score = 0;
}
},
shuffle: function (array) {
return array.sort(() => Math.random() - 0.5);
},
generateNumbers: function (num) {
var randomNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
return this.shuffle(randomNumbers).slice(0, num);
},
// MY METHODS
disableButton: function() {
},
getHint: function () {
for (var i in this.numbers) {
return i;
}
// currentNumber and numbers
},
},
mounted: function () {
// component is ready for action!
this.initGame();
},
}
</script>
you can add this lines inside of your functions guessHigher & guessLower
if (correct) {
this.incorrectMessage = '' // +
this.correctMessage = 'Correct!'
this.score++
} else {
this.correctMessage = '' // +
this.incorrectMessage = 'Incorrect! Game Over!'
this.shuffle(this.numbers)
this.score = 0
}
also should be more convenient to use v-show directive in your template:
<div class="alert alert-success" v-show="correctMessage">
{{ correctMessage }}
</div>
<div class="alert alert-danger" v-show="incorrectMessage">
{{ incorrectMessage }}
</div>
My bad. there is no .remove() function in Vuejs. Mixed something up there.
var vue = new Vue({
el:"YourDivClassOrID",
data: {
isShowing:false,
}
})
Then you can change the isShowing variable to true or false to see or hide it.
Hope this would help you :)
I use select2 in my Angular project , Actually I have a problem that is I have no idea about how to set default value for select-option. Here is my code :
HTML :
<select-tag-manager parent-id="2" value="restaurant.type" ></select-tag-manager>
Angular :
app.directive('selectTagManager', function() {
return {
restrict: "E",
replace: true,
scope: {
parentId: '#',
value: '='
},
controller: function($rootScope, $scope, Gateway, toaster, $element, Tags) {
var element;
$scope.update = function () {
};
var makeStandardValue = function(value) {
var result = [];
angular.forEach(value , function(tag , key) {
if(result.indexOf(tag.tagId) < 0) {
result.push(tag.tagId);
}
});
return result;
};
var init = function () {
Gateway.get('', '/tag?' + 'parentId=' + $scope.parentId, function(response) {
$scope.allPossibleTags = response.data.result.tags;
});
element = $($element).children().find('select').select2();
console.log(element);
};
$scope.$watch('value', function(newval) {
if( newval ) {
$scope.standardValue = [];
angular.forEach(newval, function(val, key) {
$scope.standardValue.push(val.tagName);
});
console.log($scope.standardValue);
}
});
init();
},
templateUrl: 'selectTagManager.html'
}
});
selectTagManager.html:
<div class="row">
<div class="col-md-12">
{{ standardValue }}
<select class="select2" multiple="multiple" ng-model="standardValue" ng-change="update()">
<option ng-if="tag.tagId" ng-repeat="tag in allPossibleTags" data-id="{{tag.tagId}}" value="{{tag.tagId}}">{{ tag.tagName }}</option>
</select>
</div>
</div>
I got value
console.log($scope.standardValue);
result: ["lazzania", "pizza", "kebab"]
But I don't know how to set them as default value in select-option. Any suggestion?
EDITED :
I've just edited my question using Angular-ui/ui-select2. I changed my template :
<select ui-select2 = "{ allowClear : true }" ng-model="standardValue" multiple="multiple" >
<option value="standardId" ></option>
<option ng-repeat="tag in allPossibleTags" value="{{tag.tagId}}">{{tag.tagName}}</option>
</select>
And also my js:
$scope.$watch('value', function(newval) {
if( newval ) {
$scope.standardValue = [];
$scope.standardId = [];
// $scope.standardValue = makeStandardValue(newval);
console.log('----------------------------------------------------------------------');
angular.forEach(newval, function(val, key) {
$scope.standardValue.push(val.tagName);
$scope.standardId.push(val.tagId);
});
console.log($scope.standardValue);
console.log($scope.standardId);
}
});
Nevertheless , Still I can't set default value.
as demonstarted at http://select2.github.io/examples.html#programmatic, one can set default values for multiple select2 element as follows:
$exampleMulti.val(["CA", "AL"]).trigger("change");
so, in you case you have already element variable pointing to your select2:
element.val($scope.standardValue).trigger('change');
note, that this is jQuery approach of setting/changing values, angular approach would be to update values via ng model and its life cycle events
The IDs in your model need to match the IDs in your data source, so if your model is:
["lazzania", "pizza", "kebab"]
Then allPossibleTags needs to look like:
[{ tagId: "lazzania", tagName: "Lazzania" }, { tagId: "pizza" ...
Check out this plunk for a working example:
http://plnkr.co/edit/e4kJgrc69u6d3y2CbECp?p=preview
I want to filter object inside nested ng-repeat.
HTML:
<div ng-controller="MyController">
<input type="text" ng-model="selectedCityId" />
<ul>
<li ng-repeat="shop in shops">
<p ng-repeat = "locations in shop.locations | filter:search" style="display: block">
City id: {{ locations.city_id }}
<span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
</p>
</li>
</ul>
Controller:
var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
$scope.search = function (location) {
if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
return true;
}
if (location.city_id === parseInt($scope.selectedCityId)) {
return true;
}
};
$scope.item = function (detail) {
if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
return true;
}
if (detail.pin == parseInt($scope.selectedCityId)) {
return true;
}
};
$scope.shops =
[
{
"category_id":2,
"locations":[
{
"city_id":368,
"details": [{
"pin": 627718,
"state": 'MH'
}]
}
]
},
{
"name":"xxx",
"category_id":1,
"locations":[
{
"city_id":400,
"region_id":4,
"details": [{
"pin": 627009,
"state": 'MH'
},{
"pin": 129818,
"state": 'QA'
}]
},
]
},
];
});
Here's the fiddle:
http://jsfiddle.net/suCWn/210/
I want to use multiple filter inside ng-repeat.
Example: Whenever user enters the ID in the input box. The list should filter based on cityID or PinCode.
if user enter '129818' it should show pin code result of 129818 along with its parent cityID
Similarly, if a user enter 400, the list should filter and show cityID result with 400 along with its child pin code.
EDIT:
Update Code http://codepen.io/chiragshah_mb/pen/aZorMe?editors=1010]
First, you must not filter locations with matching details. Use something like this in the search filter:
$scope.search = function (location) {
var id = parseInt($scope.selectedCityId);
return isNaN(id) || location.city_id === id ||
location.details.some(function(d) { return d.pin === id });
};
To show details if filtered by cityID, you have to find the parent location and check if it was filtered.
$scope.item = function (detail) {
var id = parseInt($scope.selectedCityId);
return isNaN(id) || detail.pin === id || locationMatches(detail, id);
};
function locationMatches(detail, id) {
var location = locationByDetail(detail);
return location && location.city_id === id;
}
function locationByDetail(detail) {
var shops = $scope.shops;
for(var iS = 0, eS = shops.length; iS != eS; iS++) {
for(var iL = 0, eL = shops[iS].locations.length; iL != eL; iL++) {
if (shops[iS].locations[iL].details.indexOf(detail) >= 0) {
return shops[iS].locations[iL];
}
}
}
}
EDIT Another, more flexible solution would be to remove all the filters from ngRepeats and do the filtering in a method that you call on ngChange of the search text. Here is the basic structure for this approach.
myApp.controller('MyController', function($scope, $http) {
var defaultMenu = [];
$scope.currentMenu = [];
$scope.searchText = '';
$http.get(/*...*/).then(function (menu) { defaultMenu = menu; } );
$scope.onSearch = function() {
if (!$scope.searchText) {
$scope.currentMenu = defaultMenu ;
}
else {
// do your special filter logic here...
}
};
});
And the template:
<input type="text" ng-model="searchText" ng-change="onSearch()" />
<ul>
<li ng-repeat="category in currentMenu">
...
</li>
</ul>
I have updated your filters. The problem is in your search filter you are only checking for the city_id, what you should do is:
Check if the typed id is city_id
Check if typed id is a pid of a child detail of given location
Similar thing for the item filter:
Check if the typed id is a pid of the detail being filtered
Check if the typed id is a city_id of the parent location of the detail passed in
Here is a working jsFiddle. I hope this helps.
By simply modifying the JSON to include the city_id for children so you don't need to loop through it to get the parent's city_id, the solution is as easy as this:
var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
$scope.search = function (location) {
if (!$scope.selectedCityId)
return true;
//if user's input is contained within a city's id
if (location.city_id.toString().indexOf($scope.selectedCityId) > -1)
return true;
for (var i = 0; i < location.details.length; i++)
//if user's input is contained within a city's pin
if (location.details[i].pin.toString().indexOf($scope.selectedCityId) > -1)
return true;
};
$scope.item = function (detail) {
if (!$scope.selectedCityId)
return true;
//if user's input is contained within a city's id
if (detail.city_id.toString().indexOf($scope.selectedCityId) > -1)
return true;
//if user's input is contained within a city's pin
if (detail.pin.toString().indexOf($scope.selectedCityId) > -1)
return true;
};
Modified JSON
$scope.shops=[{"category_id":2,"locations":[{"city_id":368,"details":[{"city_id":368,"pin":627718,"state":'MH'}]}]},{"name":"xxx","category_id":1,"locations":[{"city_id":400,"region_id":4,"details":[{"city_id":400,"pin":627009,"state":'MH'},{"city_id":400,"pin":129818,"state":'QA'}]},]},];});
If directly modifying the JSON is not possible, you can modify it like this in this controller directly after this $scope.shops = ...json... statement:
for(var i=0; i<$scope.shops.length; i++)
for(var j=0, cat=$scope.shops[i]; j<cat.locations.length; j++)
for(var k=0, loc=cat.locations[j]; k<loc.details.length; k++)
loc.details[k].city_id=loc.city_id;
Working fiddle:
http://jsfiddle.net/87e314a0/
I tried to make the solution easier to understand :
index.html :
<div ng-controller="MyController">
<input type="text" ng-model="search.city_id" />
<ul>
<li ng-repeat="shop in shops">
<p ng-repeat = "locations in shop.locations | filter:search.city_id" style="display: block">
City id: {{ locations.city_id }}
<span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
</p>
</li>
</ul>
</div>
app.js :
var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
$scope.shops =
[
{
"category_id":2,
"locations":[
{
"city_id":368,
"details": [{
"pin": 627718,
"state": 'MH'
}]
}
]
},
{
"name":"xxx",
"category_id":1,
"locations":[
{
"city_id":400,
"region_id":4,
"details": [{
"pin": 627009,
"state": 'MH'
},{
"pin": 129818,
"state": 'QA'
}]
},
]
},
];
});
Here's the fiddle :
mySolution
This is difficult to phrase but:
I have 1 collection called users.
Every user has 3 properies: id, name, skill.
{
_id: 1,
name: 'frank young',
skill: 'java'
},
I have 1 form collects search results upon pressing enter.
<form ng-submit="pushToNewArry(searchTerm)">
<input type="text" ng-model="searchTerm" />
<input type="submit">
</form>
this is not the best way to do this
$scope.newUsers = [];
$scope.pushToNewArry = function(msg) {
$scope.trackedUsers.push(msg);
$scope.searchTerm = '';
};
Question:
How do I create a filter that will run over multiple search terms and create a list proper matches based on the users collections vs inputed values.
<ol ng-repeat = "user in users | filter: trackedUsers">
<li class="names"><span>{{$index}}. </span>{{user.name}}</li>
<li class="skills">{{user.skill}}</li>
</ol>
Upon submission, the user input will be saved and create a new array of users based on inputed values of search. therefore, multiple matching values.
Updated:
JSFiddle
not excatly the same as example above because I keep playing with it.
You mean like this jsfriddle
updated fiddle
updated fiddle2
<div>{{listSearchTerms | json}}</div>
<form ng-submit="saveSearchTerm()">
<input type="text" ng-model="searchTerm" />
<input type="submit">
</form>
<ol ng-repeat = "user in users | filter:filterSearch(searchTerm)">
<li class="names"><span>{{$index}}. </span>{{user.name}}</li>
<li class="skills">{{user.skill}}</li>
</ol>
Javascript
var app = angular.module('app', []);
//App.directive('myDirective', function() {});
//App.factory('myService', function() {});
app.controller('MainCtrl', function ($scope) {
$scope.users = [
{
_id: 1,
name: 'frank young',
skill: 'java'
},
{
name: 'jeff qua',
skill: 'javascript'
},
{
name: 'frank yang',
skill: 'python'
},
{
name: 'ethan nam',
skill: 'python'
},
{
name: 'ethan nam',
skill: 'javascript'
},
];
$scope.searchTerm = "";
$scope.listSearchTerms = [];
$scope.filterSearch = function (terms) {
return function (item) {
return terms.length < 1 ? true :
terms.reduce(function (lastresult, term) {
return lastresult + (item.name.indexOf(term) > -1 || item.skill.indexOf(term) > -1);
}, 0) > 0;
}
};
$scope.saveSearchTerm = function () {
$scope.listSearchTerms.push($scope.searchTerm);
$scope.searchTerm = "";
}
});
I have a User object in Angular controller. I also have an array of Account objects with respective ID for each.
In User I have a field "default_account" where I want to put ID of a default account. So, user can have a lot of accounts but only one of them can be default. When I go to Account options, I have a checkbox there which is responsible for setting/unsetting the account as default.
Now I want to set checkbox on/off depending on its being default for the user. And I also need to respectively change default_account field inside User object on checkbox change. It puzzles me quite much how I can do it.
Any advice is appreciated!
Very approximate (didn't text that):
html:
<div ng-repeat="account in accounts">
<input type="checkbox" ng-checked="account == user.default_acount"
ng-click="SelectAssDefault(account )" />
</div>
js:
function MyCtrl($scope) {
$scope.user = { name: 'user', default_acount: null};
$scope.accounts = [{ }, { }, ...];
$scope.SelectAssDefault = function (account) {
$scope.user.default_acount = account;
};
}
EDIT: a working example: http://jsfiddle.net/ev62U/120/
If you want to set a checkbox to true based on a variable, you can set ng-checked="variable" within the input tag.
If the variable is true the box will be checked. If it's false it won't. Alternatively, an expression will also work e.g. ng-checked="1===1" will evaluate to true.
If you want to alter something else based on user clicking on the checkbox, set ng-click="someCtrlFunction()" within the input tag. This will call a function in your controller. You can look up the value of the checkbox from your controller if you've bound to it.
Here's a fiddle: http://jsfiddle.net/E8LBV/10/ and here's the code:
HTML
<div ng-app="App">
<div ng-controller="AppCtrl">
<ul>
<li ng-repeat="user in users">{{user.name}}
<ul>
<li ng-repeat="account in user.accounts">
<input type="checkbox" ng-model="checked" ng-checked="account == user.default" ng-click="changeDefault(user.id,account,checked)">{{account}}</input>
</li>
<ul/>
</li>
</ul>
</div>
</div>
JS
var app = angular.module('App', []);
app.service('Users', function () {
var Users = {};
Users.data = [{
'id': 1,
'name': 'jon',
'default': null
}, {
'id': 2,
'name': 'pete',
'default': null
}];
return Users;
});
app.service('Accounts', function () {
var Accounts = {};
Accounts.data = [{
'user': 1,
'ac': 123456
}, {
'user': 2,
'ac': 456832
}, {
'user': 2,
'ac': 345632
}, {
'user': 1,
'ac': 677456
}];
return Accounts;
});
app.controller('AppCtrl', function ($scope, Users, Accounts) {
$scope.users = Users.data;
//attach accounts to user
for (i = 0; i < $scope.users.length; i++) {
$scope.users[i].accounts = [];
for (ii = 0; ii < Accounts.data.length; ii++) {
if (Accounts.data[ii].user == $scope.users[i].id) {
$scope.users[i].accounts.push(Accounts.data[ii].ac);
}
}
}
//function to change the default account for the user
$scope.changeDefault = function (id, account, checked) {
if (!checked) {
return;
}
for (i = 0; i < $scope.users.length; i++) {
if ($scope.users[i].id == id) {
$scope.users[i].
default = account;
}
}
}
});
Here is my solution that perfectly worked for me!
<tbody ng-repeat="account in accounts">
<tr>
<td ><a ng-click="getloandetails(account.idAccount)">{{account.accountName}}</a></td>
<td>$ {{account.currentBalance}}</td>
</tr>
</tbody>
and in Angular side just do this:
$scope.getloandetails = function(accountId) {
alert('Gettring details for the accountId: ' + accountId);
};