I am trying to toggle a span containing a loading animation on a button press until the function completes using v-if. But when I press the button the DOM freezes and the span element is unchanged until the function call ends. How can I make the DOM not freeze and the loading icon to show? Non blocking button press might be a solution?
HTML
<form class="form-inline">
<div class="form-group">
<div style="display: inline-flex" class='input-group date' id='datetimepicker1'>
<input placeholder="Start Date" id="pick1" type='text' class="form-control"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<div class="form-group">
<div style="display: inline-flex" class='input-group date' id='datetimepicker2'>
<input placeholder="End Date" id="pick2" type='text' class="form-control"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
<div class="form-group">
<input class="form-control" type="text" v-model="Keyword" placeholder="keyword">
</div>
#*Start Date<input type="text" v-model="StartDate" placeholder="Start Date" style="width: 177px" />
End Date <input type="text" v-model="EndDate" placeholder="End Date" style="width: 177px" />*#
<button type="button" v-show="resetShow" class="btn btn-primary btn-lg " v-on:click="reset" id="reset-btn">Reset filters</button>
<button type="button" v-show="GetShow" type="button" class="btn btn-primary btn-lg " v-on:click="getEdges">Get Edges</button>
<button v-show="GetShow" type="button" class="btn btn-primary btn-lg " v-on:click="getNodes">Get Nodes</button>
<button v-on:click="send" type="button" class="btn btn-default" id="load" >Submit</button>
<span v-if="loading" id="loader" style="display:none"> <i style="font-size: 197%; color: purple" class='fa fa-circle-o-notch fa-spin'></i> <span style="font-size: 190%"> Processing </span> </span>
Javascript
var vm = new Vue({
el: '#main',
data: {
resetShow: false,
Keyword: '',
StartDate: '2016-06-08T17:03:36.000Z',
EndDate: '2016-06-16T17:03:36.000Z',
Dialog: [],
EdgeList: [],
NodeList: [],
loading: false,
StartDate1: '',
GetShow: false
},
// define methods under the `methods` object
methods: {
getById: function(event) {
},
send: function(event) {
this.loading = true;
console.log(this.StartDate1);
var StartDate = $('#datetimepicker1').data("DateTimePicker").date().utc().format().split('+')[0]+".000Z";
var EndDate = $('#datetimepicker2').data("DateTimePicker").date().utc().format().split('+')[0]+".000Z";
if (this.Keyword != null) {
var g = GetElasticSearch(this.Keyword, StartDate, EndDate);
s.graph.clear();
s.graph.read(g);
sigma.canvas.edges.autoCurve(s);
s.refresh();
// Start the ForceLink algorithm:
sigma.layouts.startForceLink();
//Louv
var louvainInstance = sigma.plugins.louvain(s.graph,
{
setter: function(communityId) { this.my_community = communityId; }
});
var nbLevels = louvainInstance.countLevels();
console.log(nbLevels);
var partitions = louvainInstance.getPartitions();
var nbPartitions = louvainInstance.countPartitions(partitions);
// Color nodes based on their community
s.graph.nodes()
.forEach(function(node) {
//console.log(node.my_community);
node.color = colors[node.my_community];
});
s.refresh({ skipIndexation: true });
s.graph.nodes()
.forEach(function(node) {
node.color = colors[node.my_community];
});
s.refresh({ skipIndexation: true });
this.loading = true;
}
}
}
});
Vue updates the DOM asynchronously, when the method is finished, so to speak.
So you have to make the part of the function that takes a long tim async, so it does not block the event loop.
You can use setTimeout(callback, 0) to run the function async.
Example: https://jsfiddle.net/Linusborg/tn8q4sub/
(Edit: The example does not always work for me though I don't quite get why that is - give it a try with your situation)
For your code, this should look something like this:
send: function(event) {
this.loading = true;
setTimeout(function () {
//rest of the send code.
// and at the end:
this.loading = false
}.bind(this),0) // the `bind(this)` is important!
}
Related
I've setup a view which contains a search form:
<form>
<input type="hidden" id="product_id" value="{{$tour->id}}">
<div class="form-group col-md-4">
<label>Date:</label>
<div class="input-group date">
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
<input type="text" class="form-control pull-right" id="start" placeholder="Start Date">
</div>
</div>
<div class="form-group col-md-4">
<label>End:</label>
<div class="input-group date">
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
<input type="text" class="form-control pull-right" id="end" placeholder="End Date">
</div>
</div>
<div class="form-group col-md-4" id="dateprice-search">
<label></label>
<button type="submit" class="btn" id="btn-search" >
Search
</button>
</div>
The below is the ajax code to handle the request of the form:
$(document).ready(function() {
$('#dateprice-search').on('click', '#btn-search', function() {
$.ajax({
type: 'post',
url: '/date-price',
data: {
'_token': $('input[name=_token]').val(),
'product_id': $("#product_id").val(),
'start': $("#start").val(),
'end': $("#end").val()
},
success: function(data) {
$('.shadow-z-1').show();
$('.shadow-z-1').append("<tr class='liquid-row><td>" + data.start + "</td><td>"+ data.end + "</td><td>" + data.end + "</td><td><a class='btn-m btn btn-m-success'>Available</a></td></tr>");
}
});
});
});
Route:
Route::post('/date-price','GetPublicController#datePrice')
->name('searchDate');
And finally method in controller to give the results:
public function datePrice(Request $request){
$start = $request->start;
$end = $request->end;
$dates = DatePrice::where('tour_id','=',$request->product_id)
->whereBetween('start', array($request->start, $request->end))
->whereBetween('end', array($request->start, $request->end))
->get();
return response()->json($dates);
}
At first the url looks like this before submitting the form http://localhost:8000/trips/popular/trekking/test and url becomes http://localhost:8000/trips/popular/trekking/test? after clicking the search button. Console section of inspect element shows no error in script. What mistake I had made over here ?
It mean your form submiting to same page due to type="submit"
1) change to type="button"
<button type="button" class="btn" id="btn-search" >
2) Here click event should be for button not to div so change the selector and add e.preventDefault(); in jquery to prevent the default submit .
$('#btn-search').on('click', '#btn-search', function() { e.preventDefault(); });
note :
1st : your action attribute missing so form will be submit same page .
2nd : your method attribute missing so it will take default GET method
You have given button type as submit, either remove it as
<button type="button" class="btn" id="btn-search" >
Search
</button>
or use the jquery preventDeafult() function to prevent the default behavior of submit button i.e form submit in your button click event as
$(document).ready(function() {
$('#dateprice-search').on('click', '#btn-search', function(e) {
e.preventDefault();
//your ajax code
});
});
Iam working with chat application.Here I have to store the messages in local storage those entered by individual user as well as group messages also.So I want to use local storage inorder to store the messages so that when I click on the particular user,previous messages sent by that user has to be shown.Iam strucked at writing code for this.How can I implement this code in javascript?
Here is my entire code:
var grp = angular.module("grpApp", [])
.config(function ($interpolateProvider) {
$interpolateProvider.startSymbol('{|');
$interpolateProvider.endSymbol('|}');
})
.directive('scrollBottom', function () {
return {
scope: {
scrollBottom: "="
},
link: function (scope, element) {
scope.$watchCollection('scrollBottom', function (newValue) {
if (newValue) {
$(element).scrollTop($(element)[0].scrollHeight);
}
});
}
}
})
.controller("grpCtrl", function ($scope) {
document.getElementById("name").innerHTML = "Group";
$scope.friendsList = ["Devi", "Teja", "Sneha", "Srujana"];
$scope.msgdata = [];
var d = new Date();
$scope.time = d.toLocaleString();
console.log("hlooo" + $scope.time);
//$scope.uname=JSON.parse($stateParams.uname);
document.getElementById("clear").addEventListener("keyup", function (event) {
if (event.keyCode == 13) {
document.getElementById("submitmsg").click();
}
});
$scope.send = function () {
document.getElementById("submitmsg").disabled = false;
var retData = localStorage.getItem("storedData");
console.log("hiieeee" + retData);
var retrieveData = JSON.parse(retData);
console.log("heloooo" + retrieveData);
$scope.msgdata.push(retrieveData);
$scope.msgdata.push($scope.message + " " + " " + $scope.time);
$scope.message = "";
console.log("hi" + $scope.msgdata);
}
$scope.showGrp = function () {
document.getElementById("name").innerHTML = "Group";
$scope.msgdata = [];
var preData = $scope.msgdata;
localStorage.setItem("storedData", JSON.stringify(preData));
}
$scope.frndgrp = function (frnd) {
document.getElementById("name").innerHTML = frnd;
$scope.msgdata = [];
var preData = $scope.msgdata;
localStorage.setItem("storedData", JSON.stringify(preData));
}
});
<div class="container-fluid">
<div class="row content">
<div class="col-sm-4 sidenav" id="divlength" style="height:600px;overflow-y:scroll">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search friend.." ng-model="search">
<span class="input-group-btn">
<button class="btn btn-default" type="button">
<span class="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
<br>
<button class="btn btn-primary btn-lg" style="width:50%" ng-click="showGrp()">Group</button>
<br>
<br>
<div class="nav nav-pills nav-stacked" ng-repeat="frnd in friendsList|filter:search">
<a class="btn btn-primary" style="width:50%" ng-bind="frnd" ng-click="frndgrp(frnd)"></a>
<br>
<br>
</div>
<br>
</div>
<div class="col-sm-8" style="height:100px">
<h4 align="center" id="name" style="font-weight:bold"></h4>
<footer id="wrapper">
<div class="chatbox">
<div class="chatBox" scroll-bottom="msgdata" id="clearmsg">
<div ng-repeat="item in msgdata track by $index"><span class="btn" id="msg" style="background-color:lightgreen;color:black; margin-bottom:5px" ng-bind="item"></span>
</div>
</div>
</div>
<div class="input-group add-on" style="width:100%;">
<input class="form-control usermsg chatTextField" placeholder="Type a message" name="srch-term" type="text" ng-model="message" id="clear" required>
<div class="input-group-btn">
<button class="btn btn-success" type="submit" style="float:right" id="submitmsg" ng-click="send()" ng-disabled="!message"><span class="glyphicon glyphicon-send"></span></button>
</div>
</div>
</footer>
</div>
</div>
</div>
Noob question but I can get fields to render in Vue but not sure how to delete my fields. I added an index option in the v-for directives but not sure what to do after that. Thanks!
Here is a working JSFiddle: https://jsfiddle.net/xu55npkn/
<body>
<div id="app"></div>
<script>
const createNewOption = () => {
return {
text: '',
isAnswer: false
}
}
const createNewQuestion = () => {
return {
text: '',
options: [createNewOption()]
}
}
var vm = new Vue({
el: '#app',
template: `<div class="quiz-builder container">
<div v-for="question in questions">
<div class="input-group">
<input type="text" class="form-control" v-model="question.text" placeholder="Enter a question">
<span class="input-group-btn">
<button class="btn btn-danger" type="button">X</button>
</span>
<span class="input-group-btn">
<button class="btn btn-secondary" type="button" #click="addOption(question)">Add an option</button>
</span>
</div>
</br>
<div class="input-group" v-for="(option, index) in question.options" style="margin-bottom: 20px">
<span class="input-group-addon">
<input type="checkbox" v-model="option.isAnswer">
</span>
<input type="text" class="form-control" v-model="option.text" placeholder="Enter an option">
<span class="input-group-btn">
<button class="btn btn-danger" type="button">X</button>
</span>
</div></br>
</div>
<button class="btn btn-default" #click="addQuestion" :disabled="questions.length >= 5 ? true : false">
Add another question
</button>
<button class="btn btn-primary" style="background-color: #ffcc00; border: #ffcc00">
Create quiz
</button>
</div>`,
data () {
return {
questions: [createNewQuestion()],
showQuestions: false,
}
},
methods: {
addQuestion () {
this.questions.push(createNewQuestion())
},
removeQuestion (index) {
this.questions.shift(index)
},
addOption (question) {
question.options.push(createNewOption())
}
}
})
</script>
Based on your updated question, you have already solved for removing questions, although yev's answer is a much better way for removing questions.
To remove options, you need to add a new handler for removeOption that takes in both the question (which you are iterating over) and the option (which you are iterating over. Vue handles both of these scenarios for you. You can then find the index of the option and splice the array. See this fiddle.
template:
<button class="btn btn-danger" type="button" #click="removeOption(question, option)">
X
</button>
component:
removeOption (question, option) {
var index = question.options.indexOf(option);
if (index > -1) {
question.options.splice(index, 1);
}
}
Your delete button should look like:
<div v-for="(question, i) in questions">
<div>
<input v-model="question.text">
<span>
<button #click=removeQuestion(i)>X</button>
</span>
<span>
<button #click="addOption(question)">Add an option</button>
</span>
</div>
</div>
Notice I've added i (index) in your for loop and click handler for X button.
Your remove function will look like:
removeQuestion (index) {
this.questions.splice(index, 1);
}
Array.shift will remove only first item in the array which is not exactly what you want :)
In the code below when I click edit the other boxes loose the edited icon until cancel is clicked.
Is there away that I can have it so that if a box is not being edited it keeps the normal state of code?
The library I am using is: https://vitalets.github.io/x-editable/
Normal State:
When an edit button is clicked:
jQuery:
/* X-Editable */
$(function(){
$.fn.editable.defaults.mode = 'inline';
$.fn.editable.defaults.params = function (params) {
params._token = $("#_token").data("token");
return params;
};
var dataURL = $('.updateField').data('url');
var inputName = $('.updateField').attr("name");
$('.updateField').editable({
type: 'text',
url: dataURL,
name: inputName,
placement: 'top',
title: 'Enter public name',
toggle:'manual',
send:'always',
ajaxOptions:{
dataType: 'json'
}
});
$('.edit').click(function(e){
var container = $(this).closest('.input-group'); // !!
var input = container.find('.updateField');
var inputName = input.attr('name');
var dataURL = input.data('url');
console.log(inputName);
e.stopPropagation();
container.find('.updateField').editable('toggle'); // !!
container.find('.edit').hide(); // !!
});
$(document).on('click', '.editable-cancel, .editable-submit', function(e){
$(e.target).closest('.input-group').find('.edit').show(); // !!
})
//ajax emulation. Type "err" to see error message
$.mockjax({
url: '/post',
responseTime: 100,
response: function(settings) {
if(settings.data.value == 'err') {
this.status = 500;
this.responseText = 'Validation error!';
} else {
this.responseText = '';
}
}
});
});
Normal State HTML:
<input name="__RequestVerificationToken" type="hidden" value="{{ csrf_token() }}" />
<div class="box-body">
<div class="form-group">
<label class="col-sm-2 control-label" for="siteName">Website Name</label>
<div class="col-sm-3">
<div class="input-group">
<input class="form-control updateField" data-url="{{ route('generalDataSubmit', 1)}}" data-title="Website Name" name="siteName" placeholder="Email" type="input" value="{{ old('siteName', $siteSettingsData->siteName)}}"> <span class="input-group-btn"><button class="btn btn-default edit" type="button"><span class="glyphicon glyphicon glyphicon-pencil"></span></button></span>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="siteEmail">Website E-Mail Address</label>
<div class="col-sm-3">
<div class="input-group">
<input class="form-control updateField" data-url="{{ route('generalDataSubmit', 1) }}"data-title="Website E-Mail Address" name="siteEmail" placeholder="Site E-Mail" type="email" value="{{ old('siteEmail', $siteSettingsData->siteEmail) }}"> <span class="input-group-btn"><button class="btn btn-default edit" type="button"><span class="glyphicon glyphicon glyphicon-pencil"></span></button></span>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="siteCopyright">Website Copyright</label>
<div class="col-sm-3">
<div class="input-group">
<input class="form-control updateField" data-url="{{ route('generalDataSubmit', 1)}}" data-title="Website Copyright" name="siteCopyright" placeholder="Site Copyright" type="input" value="{{ old('siteCopyright', $siteSettingsData->siteCopyright)}}"> <span class="input-group-btn"><button class="btn btn-default edit" type="button"><span class="glyphicon glyphicon glyphicon-pencil"></span></button></span>
</div>
</div>
</div>
</div>
<!-- /.box-body -->
try changing the following line:
container.find('.edit').hide();
to
$(this).hide();
It seems like you are using some bootstrap design template.From my point of view the code
$('.edit').click(function(e){
var container = $(this).closest('.input-group'); // !!
var input = container.find('.updateField');
var inputName = input.attr('name');
var dataURL = input.data('url');
console.log(inputName);
e.stopPropagation();
container.find('.updateField').editable('toggle'); // !!
container.find('.edit').hide(); // !!
});
seems ok.I don't understand the line container.find('.updateField').editable('toggle'); // !! in the function.Are you using some kind of library. My suggestion is to remove that line from your code and test.Also check whether you are getting the correct value of inputName outputted.And finally check in the console for any errors when you click the edit button.
Try using $(e) instead of $(this) in the following code:
$('.edit').click(function(e){
//var container = $(this).closest('.input-group');
var container = $(e).closest('.input-group');
var input = container.find('.updateField');
var inputName = input.attr('name');
var dataURL = input.data('url');
console.log(inputName);
e.stopPropagation();
container.find('.updateField').editable('toggle'); // !!
container.find('.edit').hide(); // !!
});
I have a form where there is a need for me to have 2 or more date fields for different things. I tried the Angular UI Bootstrap which works fine when I have only 1 date field in the form. But it stops working if I have multiple date fields and I dont know the easier method to get this to work.
This is my HTML sample:
<label>First Date</label>
<div class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}" name="dt" ng-model="formData.dt" is-open="opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
<label>Second Date</label>
<div class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}" name="dtSecond" ng-model="formData.dtSecond" is-open="opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
My JS is:
myApp.controller('DatePickrCntrl', function ($scope) {
$scope.today = function() {
$scope.formData.dt = new Date();
};
$scope.today();
$scope.showWeeks = true;
$scope.toggleWeeks = function () {
$scope.showWeeks = ! $scope.showWeeks;
};
$scope.clear = function () {
$scope.dt = null;
};
// Disable weekend selection
$scope.disabled = function(date, mode) {
return ( mode === 'day' && ( date.getDay() === 0 || date.getDay() === 6 ) );
};
$scope.toggleMin = function() {
$scope.minDate = ( $scope.minDate ) ? null : new Date();
};
$scope.toggleMin();
$scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened = true;
};
$scope.dateOptions = {
'year-format': "'yy'",
'starting-day': 1
};
$scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'shortDate'];
$scope.format = $scope.formats[0];
});
I implemented based on the sample here. The problem I have here is:
1) When one of the date field is clicked, the pop-up datepicker is messed up and seems to show 2 datepicker in 1.
2) When I remove is-open="opened" attribute, the pop-up window seems to work fine. But without is-open="opened", the ng-click="open($event) for the button doesnt work.
3) Since each of the date fields have different ng-models, I am unable to set default dates for any date fields except for the first one with ng-model="formData.dt"
The only long way to resolve this that I can think of is to separate the controller for each date field.
I would like to know how others implement multiple date fields in 1 form itself when using Angular UI Bootstrap.
I have 30 in one form one controller no problem. use the same concept if you need it on ng-repeat.
<label>First Date</label>
<div class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}"
name="dt" ng-model="formData.dt" is-open="datepickers.dt"
datepicker-options="dateOptions" ng-required="true"
close-text="Close" />
<span class="input-group-btn">
<button class="btn btn-default" ng-click="open($event,'dt')">
<i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
<label>Second Date</label>
<div class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}"
name="dtSecond" ng-model="formData.dtSecond"
is-open="datepickers.dtSecond" datepicker-options="dateOptions"
ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button class="btn btn-default" ng-click="open($event,'dtSecond')">
<i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
myApp.controller('DatePickrCntrl', function ($scope) {
$scope.datepickers = {
dt: false,
dtSecond: false
}
$scope.today = function() {
$scope.formData.dt = new Date();
// ***** Q1 *****
$scope.formData.dtSecond = new Date();
};
$scope.today();
$scope.showWeeks = true;
$scope.toggleWeeks = function () {
$scope.showWeeks = ! $scope.showWeeks;
};
$scope.clear = function () {
$scope.dt = null;
};
// Disable weekend selection
$scope.disabled = function(date, mode) {
return ( mode === 'day' && ( date.getDay() === 0 || date.getDay() === 6 ) );
};
$scope.toggleMin = function() {
$scope.minDate = ( $scope.minDate ) ? null : new Date();
};
$scope.toggleMin();
$scope.open = function($event, which) {
$event.preventDefault();
$event.stopPropagation();
$scope.datepickers[which]= true;
};
$scope.dateOptions = {
'year-format': "'yy'",
'starting-day': 1
};
$scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'shortDate'];
$scope.format = $scope.formats[0];
});
// ***** Q2 ***** somemodel can be just an array [1,2,3,4,5]
<div ng-repeat="o in somemodel">
<label>Date Label</label>
<div class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}"
name="dt{{o}}" ng-model="datepickers.data[o]"
is-open="datepickers.isopen[o]" datepicker-options="datepickers.option"
ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button class="btn btn-default" ng-click="open($event,o)">
<i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
</div>
myApp.controller('DatePickrCntrl', function ($scope) {
$scope.datepickers = {
data: {},
options: {
'year-format': "'yy'",
'starting-day': 1
},
isopen: {}
}
$http.get("get/data/for/some/model", function(result) {
$scope.somemodel = result;
for (var i = 0; i < result.length; i++) {
$scope.datepickers.isopen[result] = false;
$scope.datepickers.data[result] = new Date(); //set default date.
}
});
// fill in rest of the function
});
Simpler Solution. Requires only modding the HTML and can be used in a ng-repeat if you like. Just be creative with what you name the opened
Stick this in your Controller:
$scope.calendar = {
opened: {},
dateFormat: 'MM/dd/yyyy',
dateOptions: {},
open: function($event, which) {
$event.preventDefault();
$event.stopPropagation();
$scope.calendar.opened[which] = true;
}
};
HTML:
<div class="form-group row">
<div class="col-lg-6">
<label for="formDOB">Date of Birth</label>
<p class="input-group">
<input type="text" class="form-control" datepicker-popup="{{calendar.dateFormat}}" ng-model="record.birthDate" is-open="calendar.opened.dob" datepicker-options="calendar.dateOptions" close-text="Close" placeholder="Date of Birth" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="calendar.open($event, 'dob')"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
<div class="col-lg-6">
<label for="formWinDate">Win Date</label>
<p class="input-group">
<input type="text" class="form-control" datepicker-popup="{{calendar.dateFormat}}" ng-model="record.winDate" is-open="calendar.opened.win" datepicker-options="calendar.dateOptions" close-text="Close" placeholder="Win DAte" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="calendar.open($event, 'win')"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
wayne's answer is great. I would only add, that you can make the function 'open()' better:
$scope.open = function ($event, datePicker) {
$event.preventDefault();
$event.stopPropagation();
$scope.closeAll();
datePicker.opened = true;
};
And then you have to use it like that:
ng-click="open($event, dateFrom)"
Where dateFrom is your ng-model (i.e. you use $scope.dateFrom).
EDIT: $scope.closeAll(); is a function that closes all the datePickers. It can be written like that:
$scope.closeAll = function() {
$scope.dateFrom.opened = false;
$scope.dateTo.opened = false;
};
I'd prefer not to mix ng-model with UI info.For this purpose, is necessary to define an array to store the opening flags, as well as checking if the datePicker is opened or not.
Moreover, I have changes the 'open' behavior to 'toggle' instead, in order to allow closinf the datePicker with the button.
Here is my controller:
$scope.toggleOpenDatePicker = function($event,datePicker) {
$event.preventDefault();
$event.stopPropagation();
$scope[datePicker] = !$scope[datePicker];
};
Then it can be used as:
<input type="text" class="form-control" ng-model="model.date1" is-open="date1" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="toggleOpenDatePicker($event,'date1')"><i class="glyphicon glyphicon-calendar"></i>
</button>
</span>
The $scope idea was borrowed from here:
I solved my problem by use this plunker with minor changes.
HTML
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}" ng-model="dt" is-open="openDatePickers[0]" min-date="minDate" max-date="'2015-06-22'" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event, 0)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
<h4>Popup</h4>
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}" ng-model="dt" is-open="openDatePickers[1]" min-date="minDate" max-date="'2015-06-22'" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event, 1)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
and in controller
$scope.openDatePickers = [];
$scope.open = function ($event, datePickerIndex) {
$event.preventDefault();
$event.stopPropagation();
if ($scope.openDatePickers[datePickerIndex] === true) {
$scope.openDatePickers.length = 0;
} else {
$scope.openDatePickers.length = 0;
$scope.openDatePickers[datePickerIndex] = true;
}
};
my changes
instead numbers (0 or 1) i use $index in angular ng-repeat.
like this:
is-open="openDatePickers[**$index**]"
ng-click="open($event, **$index**)"
<p class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}" ng-model="dt" is-open="openDatePickers[$index]" min-date="minDate" max-date="'2015-06-22'" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close">
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event, $index)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
i used it in a different way and it looks a bit easier to me. I was using one of the mentioned approaches, but i was to lazy to create tons of calendars since i was running i a loop without having any static identifier.
So this solution is just valid for you, if you want to create lots of calendars inside of a ng-repeat. Hope it helps!
That is the controller
$scope.datepickers = {
data: {},
isopen: {}
}
// setting the defaults once
for (var i = 0; i < $scope.array.length; i++) {
$scope.datepickers.isopen[i] = false;
$scope.datepickers.data[i] = new Date();
}
// aso..
$scope.valuationDatePickerOpen = function($event, index) {
if ($event) {
$event.preventDefault();
$event.stopPropagation();
}
$scope.datepickers.isopen[index] = true;
};
And this is the HTML snipped inside my loop
<!-- ng-repeat="entry in array track by $index" -->
<input type="text" class="form-control"
datepicker-popup="dd-MMMM-yyyy"
is-open="datepickers.isopen[$index]"
ng-click="valuationDatePickerOpen($event, $index)"
ng-model="entry.date" />