I am trying to use a uib-popover-template on a font awesome icon as a sort of settings menu but can't get the popup to show. This is in the header of the page I am using:
<h3>{{vm.title}}
<i class="fa fa-ellipsis-v pull-right"
aria-hidden="true"
uib-popover-template="'options-panel.html'"
popover-placement="left"></i>
</h3>
And i have the ellipses in the right corner kind of like a 'more options' menu. options-panel.html is just 4-5 <select> dropdowns depending on the page that looks like this:
<div class="row">
<div class="form-group col-md-12">
<label for="viwemode">View Mode</label>
<select id="viewmode"
class="form-control"
ng-model="vm.currentViewer"
ng-options="view for view in vm.views"
ng-change="vm.viewChange();"></select>
</div>
<div class="form-group col-md-12"
ng-repeat="viewOption in vm.genericOptions">
<label for="{{viewOption.label}}">{{viewOption.label}}</label>
<select id="{{viewOption.label}}"
class="form-control"
ng-model="vm.config[viewOption.label]"
ng-options="v as k for (k,v) in viewOption.values"
ng-change="vm.optionChange(viewOption.label);"></select>
</div>
<div class="form-group col-md-12"
ng-repeat="viewOption in vm.viewerOptions">
<label for="{{viewOption.label}}">{{viewOption.label}}</label>
<select id="{{viewOption.label}}"
class="form-control"
ng-model="vm.config[viewOption.label]"
ng-options="value for value in viewOption.values"
ng-change="vm.optionChange(viewOption.label);"></select>
</div>
</div>
And lastly in the controller for the class I just have this object which has the template url in it:
vm.popover = {
template: '<a href uib-popover-template="\'options-panel.html\'" popover-placement="left"></a>'
};
I am not sure what I am missing but not even a popover will show let alone the one with my template in it. If I just do the ellipses (fa-icon) with uib-popover="test" the popover will show with 'test' in it so I do have the uib dependency correctly installed.
Consider this uib-popover-template plunker
I don't know your data structure but should work.
You don't need anymore
vm.popover = {
template: '<a href uib-popover-template="\'options-panel.html\'" popover-placement="left"></a>'
};
This line
uib-popover-template="'options-panel.html'"
Should be
uib-popover-template="options-panel.html"
Notice I removed the single quotes, uib-popover-template accepts an expression that will evaluate to the template location on your $scope. by passing your template path in single quotes you are only passing a plain string as an expression.
From the Docs
uib-popover-template - A URL representing the location of a template to use for the popover body
Related
I would like to be able to dynamically update a quantity field when I click on a '+' (Plus Button) However, depending where I click on the button, I am getting a different result for the parent element.
The HTML that is in use to call the function is below. This is HTML from my frontend developer and I am trying to connect a few things to make this work.
<div id="item-list" class="product_list_container">
<div class="bluedog-flex-container noPaddingLeftRgt payhub-well col-xs-12 col-sm-12 col-lg-12" data-id="32" style="margin-top:0;">
<div class="subtotal-div col-xs-12 col-sm-12 col-lg-12">
<div class="bluedog-inner-flex-container product_info_container col-xs-12 col-sm-12 col-lg-12">
<div class="order-overview-left noPaddingLeft alignLeft col-xs-4 col-sm-4 col-lg-4">Macbook Pro # 50.00</div>
<div class="quantity_range_div col-xs-6 col-sm-6 col-lg-6">
<div class="value-button createPage_decrease_qty decrease_value_btn" style="display:inline;" onclick="decreaseQty(event)"><i class="far fa-minus"></i></div>
<input type="number" class="itm_qty" id="number" value="1">
<div class="value-button increase_value_btn" style="display:inline;" onclick="increaseQty(event)"><i class="far fa-plus"></i></div>
</div>
<input type="hidden" name="item_price" data-qty="1" data-prod-name="Macbook Pro" data-id="32" data-tax="0" value="50.00"><a class="popup-close-btn order-overview-right close_btn_plans col-xs-2 col-sm-2 col-lg-2" onclick="removeItem(event)"> <i class="far fa-times-circle hosted_small_delete"></i></a>
</div>
</div>
</div>
</div>
What I am trying to achieve is, when I click on the Plus Button, (Screenshot attached) https://prnt.sc/oq770q the value will increase.
Originally, I was just using the following code to try and get the parent DIV id, which in this case is item-list
var master = $(e.target).parents().eq(5).attr('id'); // Name of item-list container
However, was receiving unexpected results, whereby if I tapped on the outside of the plus button, the surround grey area, it would not increase, but only if I tapped directly on the plus icon itself.
I noticed it was picking up a different element and the eq(5) was returning an undefined value. As you will see in in the script below, I have put in an if statement for a class check to see which part of the button has been pressed and this returns the expected results.
However, I dont' see this as scalable and transferable if someone was to change the FontAwesome Tag, as the class would likely be different.
function increaseQty(e) {
var clicked = e.path[0];
if ($(clicked).hasClass('fa-plus')) {
var master = $(e.target).parents().eq(5).attr('id'); // Name of item-list container
} else {
var master = $(e.target).parents().eq(4).attr('id'); // Name of item-list container
}
var input = $(e.target).parent().parent().find('input.itm_qty');
var qty = $(input).val();
var newQty = parseInt(qty) + 1;
$(input).val(newQty);
updatePrices(master);
}
I would like to know how, if possible, that regardless where on the container that is being clicked, the plus icon or the gray area around it, to increase the value, without having to do a dirty class check.
Many thanks,
David.
Based on you html code I am seeing like
<input type="number" class="itm_qty" id="number" value="1">
In this line you have id number so you can easily update.Based on your description your above line repeat. So you modify your increaseQty like that.
function increaseQty(e) {
var input = $(e.target).prev('input.itm_qty');
var qty = $(input).val();
var newQty = parseInt(qty) + 1;
$(input).val(newQty);
updatePrices(master);
}
If you see in your code increaseQty bind with div element and input itm_qty is sibling of div. So I am hoping above code works.
So to answer my own question, with some help from #Dhiren (Thank You)
I found out that I could assign FontAwesome Icon to the class of a SPAN. Therefore I have changed the Divs which contained the FontAwesome Icons to Spans, and assigned the FA Icon class to the span, removing the element.
<div class="quantity_range_div col-xs-6 col-sm-6 col-lg-6">
<span class="value-button createPage_decrease_qty decrease_value_btn far fa-minus" style="display:inline;" onclick="decreaseQty(event)"></span>
<input type="number" class="itm_qty" id="number" value="1">
<span class="value-button increase_value_btn far fa-plus" style="display:inline;" onclick="increaseQty(event)"></span>
</div>
In doing so I was able to use a single parents().eq(4) line of code, as the target will now always be a span, and not a Div element or an element.
function increaseQty(e) {
var master = $(e.target).parents().eq(4).attr('id');
var input = $(e.target).prev('input.itm_qty');
var qty = $(input).val();
var newQty = parseInt(qty) + 1;
$(input).val(newQty);
updatePrices(master);
}
Dhiren, thank you for the prev() suggestion as it has helped me better refactor not only this function, but more also! I appreciate the help and support.
David
I have an input box which uses ngb-datepicker for the date. When I am trying to get the value from date picker through ngModel, it is working. But it is not working when I am trying to update ngModel from the function, input box is not getting updated. Please find the snippet below just for reference.
working stackblitz link is - Working Link
Selecting the date from calendar first and then the next day is updating the value in modal but not in the input box.
<!-- typescript code -->
import {Component} from '#angular/core';
#Component({
selector: 'ngbd-datepicker-popup',
templateUrl: './datepicker-popup.html'
})
export class NgbdDatepickerPopup {
model ;
nextDay() {
this.model.day = this.model.day +1 ;
}
}
<!-- Html code -->
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input class="form-control" placeholder="yyyy-mm-dd"
name="dp" [(ngModel)]="model" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary calendar" (click)="d.toggle()" type="button"></button>
</div>
</div>
</div>
</form>
<hr/>
<button class="btn btn-sm btn-outline-primary mr-2" (click)="nextDay()">Next Day</button>
<pre>Model: {{ model | json }}</pre>
As I said in the comment, there is some point in the component tree where has been set ChangeDetectionStrategy.OnPush. In this case, inside the ngb-datepicker source code, you can see that this strategy is used.
This means that the change detection algorithm will be executed in its lighter version, and it will trigger an update only if the variable reference is changed.
So, in order to trigger change detection, you have to assign a new object to the variable rather than changing the property in place.
You can take advantage of the spread operator to have a more elegant code:
this.model = {...this.model, day: this.model.day+1};
Or just create a new object in the old style way:
this.model = Object.assign({}, this.model, {day: this.model.day+1});
I would like to create dynamically 3 input tags in Angular 6 to not copy/paste html code because that input elements have similar html and functionality.
For this purpose I created an array "reusableItems" inside component and initialize it :
let numberOfInputElements = 3;
for (let i = 0; i < numberOfInputElements; i++) {
this.reusableItems.push({
answer: 'Answer ' + (i +1),
passwordRecoveryAnswer: this.user['passwordRecoveryAnswer' + (i + 1)]
});
}
Then I put code inside my html :
<div *ngFor="let item of dropDownDataManagerService.reusableItems" >
<li class="col-xs-12 pl-lg pr0 pv-sm bd1-bottom">
<div class="col-xs-4 ph0 pt"> {{item.answerTitle}}</div>
<div class="col-xs-8">
<input type="text" name={{item.answer}} ref-{{item.answer}}="ngModel" class="col-sm-12 k-textbox ph0"
[(ngModel)]=item.passwordRecoveryAnswer
[pattern]="[a-z]"
required autocomplete="off"/>
</div>
</li>
</div>
It seems works fine but then I need to add error messages when these fields will be empty and not match to pattern. Something like :
<div *ngIf="__{{item.answer}}__.errors?.required ">
{{'Please provide an answer' | translate}}
</div>
<div *ngIf="__{{item.answer}}__.errors?.pattern">
{{'Pattern is not match'}}
</div>
I don't know what should i put inside ngIf condition.
How can I do it if my template reference variables are comes from array?
Is anyone have ideas?
Thanks
Angular creates unique template reference variable for each embedded template so that you can use the same template reference variable name inside ngFor loop:
<div *ngFor="let item of reusableItems">
<li class="col-xs-12 pl-lg pr0 pv-sm bd1-bottom">
<div class="col-xs-4 ph0 pt"> {{item.answerTitle}}</div>
<div class="col-xs-8">
<input type="text" name={{item.answer}} ref-answer="ngModel" class="col-sm-12 k-textbox ph0" [(ngModel)]="item.passwordRecoveryAnswer"
[pattern]="'[a-z]'" required autocomplete="off" />
<div *ngIf="answer.errors?.required">
{{'Please provide an answer'}}
</div>
<div *ngIf="answer.errors?.pattern">
{{'Pattern is not match'}}
</div>
</div>
</li>
</div>
In the code above I use the same name for each input in array
ref-answer="ngModel" // or you can also use #answer="ngModel
This drive me crazy, i've this simple html bootstrap-gentelella template that works almost in every page of the site that im developing, the html is so simple:
(this code make the part in the class "x_content" visible or not visible by clicking the "collapse-link" class in the "a" tag)
<div class="x_panel">
<ul class="nav navbar-right panel_toolbox">
<li>
<a class="collapse-link freccia_feed">Replies
<i class="fa fa-chevron-up"></i>
</a>
</li>
</ul>
<div class="x_content" style="display: none;">
<div class="input-group col-md-12 col-sm-12 col-xs-12">
<input data-role="tagsinput" id="risposta_'+j+'_D_'+element+'" class="form-control risposta_domanda_'+element+'" name="reply" value="" placeholder="Insert one solution" type="text">
<span class="input-group-btn">
<button id="elimina_risp_'+j+'_D_'+element+'" class="btn btn-danger glyphicon glyphicon-trash" > </button>
</span>
</div>
</div>
</div>
be carefull that the part like id="risposta_'+j+'_D_'+element+'" have no sense in this static part, and rendered as is it, just for example, I use that in the jQuery function that fail to make it works!
here:
$("#domanda_"+element).append(
'<div class="x_panel">'+
'<ul class="nav navbar-right panel_toolbox">'+
'<li>'+
'<a class="collapse-link freccia_feed" >Replies'+
'<i class="fa fa-chevron-up"></i>'+
'</a></li></ul>'+
'<div class="x_content" style="display: none;">'+
'<div class="input-group col-md-12 col-sm-12 col-xs-12">'+
'<input data-role="tagsinput" id="risposta_'+j+'_D_'+element+'" class="form-control risposta_domanda_'+element+'" name="reply"'+
'value="" placeholder="Insert one solution" type="text">'+
'<span class="input-group-btn"><button id="elimina_risp_'+j+'_D_'+element+'" class="btn btn-danger glyphicon glyphicon-trash" >'+
'</button></span>'+
'</div></div></div>');
I've tried many options to make this string works inside append() and the only one that seems working it to wrap the whole string with single ' using double " for ids class etc. and then inject javascript variable with single ' and +. Same thing for new line.
I even try to escape the whole string with online escape tools like https://www.freeformatter.com/javascript-escape.html
but still the same result, the component not works if is appended!
the problem is in the "inizialization" of the class ".collapse-link" by gentelella js.
So i move the function under my "div" generation function's.
I even add a "unbind()" func to resolve the problem that "inizialization" can toggle even the allready rendered div making them open anc closing several time...
thats the code if someone can find it usefull
cheers
$(".collapse-link").unbind("click");
$(".collapse-link").on("click", function() {
var a = $(this).closest(".x_panel")
, b = $(this).find("i")
, c = a.find(".x_content");
a.attr("style") ? c.slideToggle(200, function() {
a.removeAttr("style")
}) : (c.slideToggle(200),
a.css("height", "auto")),
b.toggleClass("fa-chevron-up fa-chevron-down")
});
I'm listing an array of objects saved into Localstorage in a table-like layout.
Each row displays data saved in a particular object. I want to be able to edit and update certain properties from the object once it has already been saved into LocalStorage.
This is how a couple of my objects looks like:
[{
"date":"2014 10 16",
"time":"20.22",
"car":"396",
"driver":"Seb",
"from":"A",
"destination":"B",
"pax":"3",
"arrival":"23.10"
},
{
"date":"2014 10 16",
"time":"23.22",
"car":"46",
"driver":"Eric",
"from":"C",
"destination":"E",
"pax":"3",
"arrival":"00.10"
}]
So far my frontend code displaying the Destination property looks like this:
HTML
<div class="col-md-3"
ng-show="editItem == false"
ng-hide="editItem">{{record.destination}}</div>
// Shows current value
<div class="col-md-3"
ng-show="editItem == true"
ng-hide="!editItem">
<select class="form-control"
ng-model="locationList2"
ng-options="location.place for location in locationlist | orderBy:'place'">
<option value="">Destination</option>
</select>
</div>
// Shows select with options to be picked to update property
<div class="col-md-1">
<button ng-click="editItem = !editItem"
ng-show="!editItem">Edit</button>
<button ng-click="editData(record); editItem = !editItem"
ng-show="editItem">Ok</button>
</div>
//Toggles between current value and select and triggers editData function
Relevant JS:
$scope.editData = function (record) {
record.destination = $scope.locationList2;
jsonToRecordLocalStorage($scope.recordlist);
}
So far when I trigger editData it just deletes the Destination property, it doesn't update it with the model of locationList2 from the Select.
What am I missing?
EDIT
Here's the complete ng-repeat piece of code:
<div class="row msf-row" ng-repeat="record in recordlist | filter: search">
<div class="col-md-1">{{record.time}}</div>
<div class="col-md-1"><strong>{{record.car}}</strong></div>
<div class="col-md-1">{{record.driver}}</div>
<div class="col-md-3">{{record.from}}</div>
<div class="col-md-3"
ng-show="editItem == false"
ng-hide="editItem">
{{record.destination}}
</div>
<div class="col-md-3"
ng-show="editItem == true"
ng-hide="!editItem">
<select class="form-control"
ng-model="locationList2"
ng-options="location.place for location in locationlist | orderBy:'place'">
<option value="">Destination</option>
</select>
</div>
<div class="col-md-1">{{record.pax}}</div>
<div class="col-md-1">
<button
ng-click="editItem = !editItem"
ng-show="!editItem">
<i class="fa fa-pencil"></i>
</button>
<button
ng-click="editData(record); editItem = !editItem"
ng-show="editItem">
<i class="fa fa-check"></i>
</button>
</div>
</div>
Also, I here's a Plunkr to ilustrate the issue!
Add a driver, car code and location before starting to see the app running and the mentioned problem.
You could use angular-local-storage as an abstraction over LocalStorage API.
If you want to just hack it, you can do something along localStorage.setItem('data', JSON.stringify(data)) when setting data and use JSON.parse(localStorage.getItem('data')) to extract it. LocalStorage doesn't deal with objects by default so we have to serialize it.
Regardless of the solution you choose, it could be a good idea to extend your edit a bit:
$scope.editData = function (recordlist) {
$scope.recordlist.destination = $scope.locationList2;
// replace whole LocalStorage data here now. no need to "patch" it
updateLocalStorage('data', <data containing your objects goes here>);
}
If you have multiple ways to modify the data and want to avoid explicit update, you could set up a watcher instead:
$scope.$watch(<data name goes here>, function(newVal) {
// update your LocalStorage now
});
Why it fails with ng-repeat?
The reason you see the behavior is quite simple. $scope.locationList2 is a single variable that gets bound for each member created by ng-repeat. That explains why it stays empty during edit.
You will need to bind the data using some other way. Consider binding it directly to your record models. Example: AngularJS - Using $index in ng-options .
Solution
The original code had bits like this:
JS:
$scope.editData = function (record) {
record.destination = $scope.location;
jsonToRecordLocalStorage($scope.recordlist);
};
HTML:
<select class="form-control" ng-model="location" ng-options="location.place for location in locationlist | orderBy:'place'">
<option value="">Destination</option>
</select>
Note that the markup is inside a ng-repeat and effectively each item created by it points at the same location! This isn't good.
To make it work I changed it like this:
JS:
$scope.editData = function () {
jsonToRecordLocalStorage($scope.recordlist);
};
HTML:
<select class="form-control" ng-model="record.destination" ng-options="location.place as location.place for location in locationlist | orderBy:'place'">
<option value="">Destination</option>
</select>
As mentioned above the JS could be replaced by a watcher. The important thing to note here is that I bind the data directly to the records. That avoid hassle at editData and more importantly gets rid of the problematic ng-model reference.