Update documents based on multiple dropdown values - javascript

I am building an administration panel within a meteor app to be able to manage the users of the app. Currently I am displaying all the users with a dropdown for each user to change their role .
I need to update the changed roles only after I hit some confirmation button at the end.
I have a template that gets rendered with the username and a dropdown with the current role of the user, my template is similar to this:
<div class="ui selection dropdown">
<input type="hidden" name="role">
<i class="dropdown icon"></i>
<div class="default text">{{role}}</div>
<div class="menu">
<div class="item" data-value="admin">Admin</div>
.
.
.
<div class="item" data-value="user">User</div>
</div>
</div>
This renders as many as users I have in my database. I need to update all users who have their role changed after clicking a button.
My current approach is to plug the user id to the dropdown:
<div class="ui selection dropdown" id={{_id}}>
Then I have an event handler for the dropdown change and catch the value:
Template.templateName.events({
"change .ui.selection.dropdown": function(event, template) {
var id = template.find(".ui.selection.dropdown").id;
var role = template.find("input[name=role]").value;
...
},
});
Now I am wondering if I should push those id,role pairs in some session key and update the users after clicking save button or there is a better and more effective alternatives for this ?

Instead of saving them in session variable and clearing session variable on template's destroyed callback, you can add an attribute to the changed element indicating that the element has changed. When save button is clicked, you can use attribute selector to get all the elements that were changed by this user. See the code sample below for further reference.
Template.templateName.events({
"change .ui.selection.dropdown": function(event, template) {
var target = $(event.target);
target.attr("data-changed", "data-changed");
},
"click #saveButtonId": function(event, template) {
var changedElements = template.$(".ui.selection.dropdown[data-changed]");
var changedItems = [];
for (var i = 0; i < changedElements.length; i++) {
var changedElement = changedElements[i];
changedItems.push({ id: changedElement.id, value: changedElement.value });
// or
//changedItems.push([changedElement.id, changedElement.value]);
}
// here you can call a meteor method like
// Meteor.call('updateUserRoles', changedItems); //this updateUserRoles will now get only changed items that you can update in your DB.
}
});

Related

How to add Add button to enter dynamic value in Angular 2/4/6/8 Multiselect Dropdown?

I am trying to add dynamic add button angular2 multiselect. Any one has done this dynamic addition of user entered value by selecting Plus button. I have tried with addNewItemOnFilter: true, I'm not able to see any + button.
I am putting (onAddFilterNewItem)="onAddItem($event)" for using angular2-multiSelect.
In settings, I am having
addNewItemOnFilter: true in the settings file.
Still I am not getting an extra 'ADD' button to add extra field. Any help I would appreciate.
There You have dynamic multiselect buttons.
Updated as you want input inside.
Template:
<div *ngIf="buttons" class="btn-group">
<button *ngFor="let button of buttons" (click)="addDynaminMultiseletButton()">
{{button}} <input type="text" (click)="setInput($event)">
</button>
</div>
Component:
buttons = ['Another button']
input: string
constructor() { }
addDynaminMultiseletButton() {
this.buttons.push("" + this.input)
}
setInput(event: any) {
this.input = event.target.value
}
You can store them in database:
buttons$ = Observable<any[]>
constructor(private db: SomeDatabase){}
ngOnInit() {
this.buttons = this.db.collection('fancyButtons').valueChanges()
}
addDynaminMultiseletButton() {
this.db.collection('fancyButtons').add({ fancyButton: 'fancyButton' })
}
Remember to subscribe when you want to display async buttons:
Template:
<div *ngIf="buttons$ | async as buttons " class="btn-group">
<button *ngFor="let button of buttons" (click)="addDynaminMultiseletButton()">+</button>
</div>
Have a nice day and good luck with programming! Remember to assign at least one element in array so there will be at least one button on screen.

ng-check="true" not working when radio choices update

I have 3 columns of radio drop down boxes. I have logic where if a user selects a radio button in column 1, the radio buttons in column 2 will refresh with a new list. I always want the top radio button all to default to check on each new refresh of a new list in column 2. However, on a second new refresh the ng-check="true" will not apply and all will not be checked.
How can I make all default to checked when the list of radio buttons refresh with a new list?
<div class="filter-column">
<div class="filter-title">Market</div>
<div class="bottom-line"></div>
<div class="overflow-container">
<div ng-show="markets.length >= 1">
<input type="radio" value="all" name="marketRadio" ng-checked="true" class="resetAll"
ng-model="filter.market" ng-change="radioMap('market')">All
</div>
<div ng-repeat="choice in markets| orderBy: 'name'">
<input type="radio" value="{{choice.name}}" name="marketRadio"
ng-change="radioMap('market', $index)" ng-model="filter.market" >
{{choice.description}}
</div>
</div>
</div>
Logic when second column of radio buttons refresh with new list:
var ppvFilter = {
regions: [],
markets: [],
dealers: []
};
function populateMarkets(regionCode) {
ppvFilter.markets.length = 0;
ppvFilter.dealers.length = 0;
var urlPath = API_BASE_URI + 'service/org/' +
regionCode + "/markets";
var params = {
url: urlPath,
method: 'GET'
};
$http(params)
.then(function(response) {
ppvFilter.markets = ppvFilter.markets.concat(response.data);
})
.catch(failure);
}
The ng-checked attribute in this case is being overridden by the value of the ng-model you have bound to that input. If you need to set it on page load, you could use ng-init="filter.market = true" to set filter.market to be truthy, which will check the box.
In general, if you have an ng-model bound to an input of any kind, the model will take priority over any hardcoded attributes that attempt to set that value. You need to programmatically set filter.market to be true for it to work.

How to display the selected items in the new page?

Whenever I click on view the items are selected on same page. I need to display them on the new page, but I am not able to do that.
<html ng-app="countryApp">
<body ng-controller="CountryCtrl">
<div ng-repeat="x in models track by $index">
<button ng-click="select(x, $index)">View Me</button>
</div>
Selected Model:
<p> {{selected.name}} </p>
<p> {{selected.brand}} </p>
<p> {{selected.price}} </p>
<p> {{selected.quan}} </p>
<p> {{selected.sku}} </p>
</body>
Function:
<script>
var countryApp = angular.module('countryApp', []);
countryApp.controller('CountryCtrl', function ($scope){
$scope.select = function(brand) {
$scope.selected = brand;
}
$scope.models = [
{ brand: "Brand: Apple", id: 980190962, name: "Name: iPhone 5", price: "Price: $199", quan: "Quantity: 1", sku: "SKU:1234" },
{ brand: "Brand: Samsung", id: 298486374, name: "Name: Galaxy S3", price: "Price: $199", quan: "Quantity: 2", sku: "SKU:5678"}
]
});
</script>
</html>
You can use two options either store that selected user to localstorage or somewhere and read that in details view like
localStorage.setItem('myObj',JSON.stringify(brand));
and retrieve that in details view like
JSON.parse(localStorage.getItem('myObj'));
OR
I will prefare to use parent child relationship in this suppose you have a parent controller named 'CountryCtrl' and two child controllers 'CountryListCtrl' and 'CountryDetailCtrl'. List page is bind to 'CountryListCtrl' and details view is bind to 'CountryDetailCtrl' and on top you have 'CountryCtrl'. You can change your view using ng-include etc. When moving from List to Detail Controller set an object on parent controller and it will be accessible to both Controllers. You can learn about parent and child relationship in angularjs on internet there are tons of article available.
Thanks.
To show the details of the selected item in a new tab, there are multiple ways to achieve this. One way is to add a form with attribute target="_blank" (to open the action attribute in a new tab) and submit it in the select() function.
So you can add a form, like this:
<form id="openNewTabForm" target="_blank" method="GET"></form>
Then update the function to submit the form. There are multiple ways to pass the data about the selected phone - e.g. store the data in form fields, serialize the data and store it in a cookie, in localStorage, etc. Here is an example using hidden form fields:
$scope.select = function(phone) {
var form = document.getElementById('openNewTabForm');
form.action = action="selectedPhone.html";
Object.keys(phone).forEach(function(field,index) {
var input = form.elements[field]; //look for an existing form field for this field
if (input) { //update existing form field
input.value = phone[field];
}
else { //create a new form field for this
input = document.createElement('input');
input.type = 'hidden';
input.name = field;
input.value = phone[field];
form.appendChild(input);
}
});
form.submit();
$scope.selected = phone;
}
Then have a new page (e.g. selectedPhone.html) that takes the values submitted with the form. This could be a server-side page (e.g. with PHP, Ruby, etc), in which case you would set the attribute method="POST" on the form. Or you can just use a regular HTML page and process the query string. You could also use AngularJS + UI Router, VueJS, etc. to modify the URL after it has been loaded.
Take a look at this Plunker example I made for an example. I would have included a code snippet that would run here but SO doesn't (currently) allow having a form submission open a new tab because it is sand-boxed.
Another approach might be to use window.open() for opening a new browser window but that might lead to issues with popup blockers for some users.
You can open new modal on click of button function like following -
In controller:
$scope.showDetails = function(){
// open new html modal to show new screen using following
$ionicModal.fromTemplateUrl('templates/views/details.html',{
scope: $scope,
animation: 'none',
backdropClickToClose: false
}).then(function(modal){
$scope.modal= modal;
$scope.modal.show();
});
}
In HTML:
<div ng-repeat="x in models track by $index">
<button ng-click="showDetails (x, $index)">View Me</button>
</div>
By using new modal (screen) you can design as you required as well.

AngularUI-Bootstrap Typeahead: Grouping results

I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a sample scenario
There are some users in the database, each user has a "Department". One user name can be available in multiple departments.
The end-user types in the names to search users from the database and retrieves the list in the typeahead list. Since one user name can belong to multiple departments, the requirement is to show the user names grouped by different departments. Something like this:
Then the user can select the desired user name and proceed.
As per the Typeahead documentation present here, I don't see any option to cater to my requirement.
I have tried the this workaround: Whenever the typeahead array is getting formed, I appended the user department to the array element:
$scope.fetchUsers = function(val) {
console.log("Entered fetchUsers function");
return $http.get("http://localhost:8080/TestWeb/users", {
params : {
username : val
}
}).then(function(res) {
console.log("Response:",res);
var users = [];
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
console.log("users=",users);
return users;
});
};
This way, at least the end user sees the department. But when I select the record, the selected value is the full content of the array element. Below is sample screenshot to elaborate:
HTML
Users from local service
<pre>Model: {{userList | json}}</pre>
<input type="text" ng-model="userList" placeholder="Users loaded from local database"
typeahead="username for username in fetchUsers($viewValue)"
typeahead-loading="loadingUsers" class="form-control">
<i ng-show="loadingUsers" class="glyphicon glyphicon-refresh"></i>
User types in the string
User selects one record
I want to avoid the department (in this case, string - Desc 4 ) when user selects a record.
Is there any way I can achieve this grouping without any workaround? Or is there any way I can enhance my workaround?
I used to have a similar requirement and here is how I did it that time.
Example Plunker: http://plnkr.co/edit/zujdouvB4bz7tFX8HaNu?p=preview
The trick is to set the typeahead-template-url to a custom item template:
<input type="text" class="form-control" placeholder="Users loaded from local database"
ng-model="selectedUser"
typeahead="user as user.name for user in getUsers($viewValue)"
typeahead-template-url="typeahead-item.html" />
The item template, this represent each item in a dropdown:
<div class="typeahead-group-header" ng-if="match.model.firstInGroup">Desc {{match.model.group}}</div>
<a>
<span ng-bind-html="match.label | typeaheadHighlight:query"></span>
</a>
As you can see, there is an ng-if to show a group header if that item has a property firstInGroup set to true.
The firstInGroup properties are populated like this using lodashjs:
$scope.getUsers = function (search) {
var filtered = filterFilter(users, search);
var results = _(filtered)
.groupBy('group')
.map(function (g) {
g[0].firstInGroup = true; // the first item in each group
return g;
})
.flatten()
.value();
return results;
}
Hope this fit to your requirement too.
please see here http://plnkr.co/edit/DmoEWzAUHGEXuHILLPBp?p=preview
instead of creating new objects here:
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
use create template :
<script type="text/ng-template" id="customTemplate.html">
<a> {{ match.model.name}} - department : {{match.model.dept}}</a>
</script>
and use it in your Typeahead directive
<input type="text" ng-model="selected"
typeahead="user.name as user for user in users | filter:$viewValue | limitTo:8" class="form-control"
typeahead-template-url="customTemplate.html">

Loading specific div based on the results of multiple dropdowns

EDIT: Would the approach be much easier if the Javascript listed was removed completely, and the dropdown menus restyled as <div>'s within <li>'s, and the final div was generated by a Javascript onclick event? e.g.
<a id="click_link">click me</a>
$("#click_link").click(function(){
$('#div').load('http://www.link.com/');
});
Either way, the problem at hand...
My decision to use an elegant-looking javascript solution is highlighting my massive inexperience when it comes to javascript! The problem is, on the face of it, simple...
Once an option has been chosen on each of the dropdown menus, I need a final div to load so that a specific button can be shown (a link to buy the item with the specified options, e.g. choosing Necklace D, with Stone Option B, and Delivery Option A = loading div with 'Buy' Button #17)
The dropdowns are divs that are filled and styled through the Javascript (as opposed to using the simpler <form> and <input> method), giving the flexibility to add two lines of differently styled text for each option etc. - This is where I step into the realm of the unknown and my inexperience shines through.
The isolated section is viewable in its entirity here
Ok, to the code.
Here's the Javascript:
function createByJson() {
var pearlData = [
{description:'Choose your pearl...', value:'Pearls', text:'Pearls'},
{description:'Beautiful black stone', value:'Black Pearl', text:'Black Pearl'},
{description:'Classic white stone', value:'White Pearl', text:'White Pearl'}
];
$("#DropItPearls").msDropDown({byJson:{data:pearlData, name:'pearls', width: 200}}).data("dd");
var blodeuweddData = [
{description:'Choose your item...', value:'Blodeuwedd', text:'the Blodeuwedd Collection'},
{description:'A striking statement', value:'BlodeuweddCelticStatement', text:'Celtic Statement Piece'},
{description:'Gold laced flower and pearl', value:'BlodeuweddBracelet', text:'Bracelet'},
];
$("#DropItBlodeuwedd").msDropDown({byJson:{data:blodeuweddData, name:'blodeuwedd', width: 250}})
.msDropDown({on:{change:function(data, ui) {
var val = data.value;
if(val!="")
window.location = val;
}}}).data("dd");
var deliveryData = [
{description:'Choose your method...', value:'Delivery', text:'Delivery Options'},
{description:'4-6 weeks delivery', value:'Four Weeks', text:'Made To Order'},
{description:'(unavailable on this item)', value:'Rush', text:'Express Delivery', disabled:true}
];
$("#DropItDelivery").msDropDown({byJson:{data:deliveryData, name:'delivery', width: 200, selectedIndex: 1}}).data("dd");
paymentData = [
{ description:'How would you like to pay?', value:'Payment', text:'Payment Method'},
{image:'images/msdropdown/icons/Visa-56.png', description:'Secure online payment', value:'Visa', text:'Visa'},
{image:'images/msdropdown/icons/Paypal-56.png', description:'Secure online payment', value:'Paypal', text:'Paypal'},
{image:'images/msdropdown/icons/EmailPay-56.png', description:'Order by email', value:'Email Payment', text:'Send Your Details'},
{image:'images/msdropdown/icons/Mastercard-56.png', description:'(coming soon)', value:'Mastercard', text:'Mastercard', disabled:true},
{image:'images/msdropdown/icons/Collect-56.png', description:'(coming soon)', value:'Collection', text:'Order and Collect', disabled:true},
{image:'images/msdropdown/icons/Email-56.png', description:'email Menna', value:'Other Method', text:'Alternatives'}
];
$("#DropItPayments").msDropDown({byJson:{data:paymentData, name:'payments', width: 250}}).data("dd");
}
$(document).ready(function(e) {
//no use
try {
var pages = $("#pages").msDropdown({on:{change:function(data, ui) {
var val = data.value;
if(val!="")
window.location = val;
}}}).data("dd");
var pagename = document.location.pathname.toString();
pagename = pagename.split("/");
pages.setIndexByValue(pagename[pagename.length-1]);
$("#ver").html(msBeautify.version.msDropdown);
} catch(e) {
//console.log(e);
}
$("#ver").html(msBeautify.version.msDropdown);
//convert
$("select").msDropdown();
createByJson();
$("#tech").data("dd");
});
function showValue(h) {
console.log(h.name, h.value);
}
$("#tech").change(function() {
console.log("by jquery: ", this.value);
})
//
And the html:
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Item</p></div>
<div id="DropItBlodeuwedd"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Precious Stones</p></div>
<div id="DropItPearls"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Payment</p></div>
<div id="DropItPayments"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Delivery</p></div>
<div id="DropItDelivery"></div>
</div>
<div id="dropOptions">
<div id="dropOptionsTitle"><p>Buy Now!</p></div>
<div id="DropItBuy"></div>
</div>
Again, working version viewable here
Many thanks in advance!
What I think you want is for your Buy button to dynamically read what the dropdowns currently say and build a link for redirection based on that, rather than trying to update the Buy button every time a dropdown changes.
From your code I can't see what the form of the final URL is supposed to be. For example, to get the current value of the delivery option, you can check $('#DropItDelivery :selected').text() which will be something like "Made To Order".
Your Buy Now! could be a button with a click event that reads these values and constructs the URL with basic string concatenation, e.g.:
window.location = "buynow.html?delivery=" + $('#DropItDelivery :selected').text() +
"&payment=" + $('#DropItPayments :selected').text()
// etc.
Of course you'd have to handle these options on the server.
In case you want to redirect to the payment page of the processor, you can just branch based on the payment method and give them the URL you want based on that.
var pm = $('#DropItPayments :selected').text();
if (pm == "Visa")
{
// Visa payment URL construction
}
else if (pm == "Send Your Details")
{
// Send your details URL construction
}
// etc.

Categories

Resources