I'm developing a date-picker based on AngularJS.
The HTML is quite simple:
<div class="calendar-slider-actions">
<a class="slider-prev" ng-click="vm.prevWeek()">
<i class="ico ico-arrow-left"></i>
</a>
<div class="slider-clip">
<ul class="slides">
<li class="slide" data-ng-repeat="date in vm.dates">
<div class="radio">
<input type="radio" name="group-days" id="field-{{date.weekDay | lowercase}}" ng-value="vm.selectedDate" ng-checked="vm.selectedDate === date.date">
<label class="btn btn-primary btn-primary-alpha radio-label" for="field-{{date.weekDay | lowercase}}" ng-click="vm.selectDate(date)">
<strong>{{date.weekDay}}</strong> {{date.shortDate}}
</label>
</div>
</li>
</ul>
</div>
<a class="slider-next" ng-click="vm.nextWeek()">
<i class="ico ico-arrow-right"></i>
</a>
</div>
Styled correctly this gives me something like this
The dates are generated on the fly in the controller and only seven at the time are generated. Once I click left or right the next seven dates are calculated, the array is updated and the GUI is refreshed as well with the new dates. So far so good.
The controller looks like this:
function DayPickerCtrl($scope) {
var vm = this;
vm.selectedDate = moment().startOf('day').format();
_changeDisplayedWeek(0);
vm.selectDate = function(date) {
vm.selectedDate = date.date;
};
vm.nextWeek = function() {
_changeDisplayedWeek(7);
};
vm.prevWeek = function() {
_changeDisplayedWeek(-7);
};
function _changeDisplayedWeek(daysToAdd) {
var selectedDate = moment(vm.selectedDate).add(daysToAdd, 'days');
vm.selectedDate = selectedDate.format();
vm.weekOfYear = selectedDate.format('WW');
vm.dates = _expandWeek(selectedDate);
};
function _expandWeek(startDate) {
var dates = [];
var dayOfWeek = moment(startDate).startOf('isoweek');
for (var i = 0; i<7; i++) {
dates.push({ weekDay: dayOfWeek.format('dd'), shortDate: dayOfWeek.format('DD.MM'), date: dayOfWeek.format() });
dayOfWeek.add(1, 'd');
}
return dates;
};
The problem I'm facing is that I'm not able to animate during the dates update. As intended it would be very cool if the dates could slide either left or right. I did try to achieve this with ng-animate but I couldn't figure out how to.
Here's a basic example of the date picker: https://plnkr.co/edit/4Oz0RBvJafiTm6ZAfidt?p=preview
Any ideas on how to get this to work?
Regards and thanks in advance
Lukas
Sometimes using angular-animate can be tricky. Looking at your markup I'm assuming you want to slide each radio button individually (maybe to throw in some staggering effects). The problem with angular-animate it does not maintain the order of the items in the list that are marked as ng-leave. In this case the (to be deleted) items are appended to the end of the <ul><li></li></ul>, which screws the relative position.
Have a look into this plunker. I've quickly eyeballed the styling from the image, so you'll probably want to tweak it a bit.
Cheers.
Related
I can't seem to change the array that the uib-typeahead uses for the autocomplete on a search field. I have a button that chooses the categories to search by name, title, education, expertise and the button is working to filter the search results. But I am having issues trying to do the same thing for the uib-typeahead autocomplete.
I have a few arrays that pull the correct information and if I put them in and publish the page it works as intended, but I can't get them to switch out dynamically when a new filter is chosen.
The arrays that I am using. The default is the searchText with every category included. I tested them all and if I switch out searchText for any other one it works fine.
$scope.searchText = []; // all text that will be searched
$scope.searchname = []; // Name text that will be searched
$scope.searchtitle = []; // Title text that will be searched
$scope.searchexpertise = []; // Expertise text that will be searched
$scope.searcheducation = []; // Education text that will be searched
$scope.searchpositionsHeld = []; // PositionsHeld text that will be searched
Here are my button and input sections.
<button type="button" class="btn btn-outline dropdown-toggle rounded-0" data-toggle="dropdown">
<span id="search_selection">Search by</span> <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<a class="dropdown-item" ng-click="changeFilterTo('name'); changeSearchTo('$scope.searchname')" href="#name">Name</a>
<a class="dropdown-item" ng-click="changeFilterTo('title'); changeSearchTo('$scope.searchtitle')" href="#title">Title</a>
<a class="dropdown-item" ng-click="changeFilterTo('tags'); changeSearchTo('$scope.searchtags')" href="#tags">Lab Capability</a>
<a class="dropdown-item" ng-click="changeFilterTo('expertise'); changeSearchTo('$scope.searchexpertise')" href="#expertise">Expertise</a>
<a class="dropdown-item" ng-click="changeFilterTo('education'); changeSearchTo('$scope.searcheducation')" href="#education">Education</a>
<a class="dropdown-item" ng-click="changeFilterTo('positionsHeld'); changeSearchTo('$scope.searchpositionsHeld')" href="#positionsHeld">LANL Positions</a>
</ul>
</div>
<input
class="form-control"
id="search_param"
placeholder="Type something"
type="text"
ng-model="search[filter]"
uib-typeahead="item for item in searchText | filter:$viewValue | limitTo:25"
ng-change="changeHandler()"
/>
And here is what I was trying to set out the uib-typeahead attribute.
$scope.changeSearchTo = function(pr) {
$scope.searchText = [];
$scope.searchText = pr;
}
So far nothing I have tried can switch out the arrays and I have tried searching for more answers, but nothing seems to match. I appreciate the help anyone can provide. Thank you!
remove $scope in the template. Try changeSearchTo(searchname) without the comma.
Merlin was mostly correct I removed $scope and then used this code to set the attribute:
$scope.changeSearchTo = function(prs) {
$scope.searchText = [];
$scope.searchText = $scope[prs];
}
I'm trying to create a set of radio buttons that I want to exhibit toggling behavior (i.e., the selected button should be highlighted). I'm using the following partial template:
<h3>{{fullAddress()}}</h3>
<h4 class="control-label">Result of Visit</h4>
<pre>{{visit}}</pre>
<div class="btn-group" data-toggle="visitState">
<div ng-repeat="option in visitOptions()" class="btn btn-default" ng-value="option.Value" ng-model="$parent.visit">
{{option.Label}}
</div>
</div>
The controller is quite simple:
app.controller("visitCtrl", function($scope, dataContext) {
var _visit = "NotHome";
$scope.visit = "NotHome";
$scope.visitOptions = function() {
return dataContext.visitOptions();
};
$scope.fullAddress = function() {
var home = dataContext.home();
var pin = dataContext.pin();
if( home.Unit == "" ) return pin.StreetAddress;
return pin.StreetAddress + " #" + home.Unit;
};
});
dataContext.visitOptions() just returns an array of {Label, Value} objects.
As things stand, there is no toggling behavior. Then again, the model value (visit) doesn't update when you click any of the buttons, either :).
For the benefit of others, there were two problems with my code:
I was including jQuery, which conflicts with the toggling behavior. I had to remove jQuery and include ui-bootstrap to replace the event-driven functionality bootstrap depends uses.
Tying ng-model to a "root-level" property (i.e., $scope.visit) keeps the auto-updating/two-way data binding functionality of angular from working. I had to tie ng-model to visit.result instead:
$scope.visit = {
result: "NotHome",
hadQuestion: false,
notes: null,
};
The final markup was pretty simple:
<div class="btn-group" data-toggle="visitState">
<div ng-repeat="option in visitOptions()" class="btn btn-primary" btn-radio="option.Value" ng-model="visit.result">
{{option.Label}}
</div>
</div>
I'm getting the following error while typing into the field filtered by 'completeList'. Why is this happening?
JavaScript
angular.module('myApp', ['timer'])
.controller('AppCtrl',['$scope', function($scope){
$scope.gameOn = true;
$scope.leaders = true;
$scope.myScore = true;
}])
.filter('completeList', function() {
return function(items) {
var list = [];
if(items) {
list = items.split(',');
var last = list[list.length - 1];
if(items.charAt(items.length - 1) != ',' || last.length === 0)
list.pop();
}
return list;
};
});
HTML
<div ng-show="gameOn" ng-controller="LabelCtrl" class="row marketing">
<div class="col-lg-4">
<h4>Enter comma-separated labels for this image</h4>
<form role="form" class="form-inline" >
<input ng-list ng-model="labels" placeholder="Enter labels" class="form-control" type="text" >
<button class="form-control" class="btn btn-xs btn-success">Submit</button>
</form>
</div>
<div class="col-lg-2">
<h4>Labels</h4>
<div>
<ol>
<li ng-repeat="label in labels track by $index | completeList">
{{ label }}
</li>
</ol>
</div>
</div>
The good news is that you only have a minor Angular syntax error. It is actually mentioned in the documentation:
Filters should be applied to the expression, before specifying a
tracking expression.
...
For example: 'item in items | filter:searchText track by item.id' is a pattern that might be used to apply a filter to items in conjunction with a tracking expression.
Given this knowledge, just change your ngRepeat line to the following, it does work exactly as you intended and works perfectly on my side:
<li ng-repeat="label in labels | completeList track by $index">
I do not know what the data structure of labels so here is my best stab at it. Filters are applied onto the instance of your iteration of your loop. It looks like you may be trying to apply the filter to the entire collection instead of that index of the loop. The filter is applied to label not labels. In this case you cannot split it. Again I dont know your data structure so I am kind of guessing here. It would be helpful if you could reveal what labels is.
Thanks,
Jordan
I started using Firebase (AngularFire) for synchronizing my data for my application. It's a Card tool for Scrum that adds cards to an array. You can manipulate the input fields.
In the first place I used localStorage, which worked really well. Now that I basically implemented Firebase, I got the following problem: After typing a single key into one field, the application stops and the only way of resuming typing is to click in the input field again.
Do you know why this is? Thank you very much in advance!
That's my basic implementation in my Controller:
Card = (#color, #customer, #points, #number, #projectName, #story) ->
$scope.cards = []
reference = new Firebase("https://MYACCOUNT.firebaseio.com/list")
angularFire(reference, $scope, "cards")
$scope.reset = ->
$scope.cards = []
$scope.addCardRed = (customer) ->
$scope.cards.push new Card("red", customer)
That's my Markup:
<div class="card card-{{ card.color }}">
<header>
<input class="points" contenteditable ng-model="card.points"></input>
<input class="number" placeholder="#" contenteditable ng-model="card.number"></input>
<input class="customerName" contenteditable ng-model="card.customer.name"></input>
<input class="projectName" placeholder="Projekt" contenteditable ng-model="card.projectName"></input>
</header>
<article>
<input class="task" placeholder="Titel" contenteditable ng-model="card.task"></input>
<textarea class="story" placeholder="Story" contenteditable ng-model="card.story"></textarea>
</article>
<footer>
<div class="divisions">
<p class="division"></p>
<button ng-click="deleteCard()" class="delete">X</button>
</div>
</footer>
</div>
<div class="card card-{{ card.color }} backside">
<article>
<h2 class="requirement">Requirements</h2>
<textarea class="requirements" placeholder="Aspects" contenteditable ng-model="card.requirements"></textarea>
</article>
</div>
I ran into this as well. This is because it's recalculating the entire array. Here's how I fixed it:
Bind your input to an ng-model and also add this focus directive
<input class="list-group-item" type="text" ng-model="device.name" ng-change="update(device, $index)" ng-click="update(device, $index)" ng-repeat='device in devices' focus="{{$index == selectedDevice.index}}" />
I set the selectedDevice like this
$scope.update = function(device, index) {
$scope.selectedDevice = device
$scope.selectedDevice.index = index
}
Now create this directive.
angular.module('eio').directive("focus", function() {
return function(scope, element, attrs) {
return attrs.$observe("focus", function(newValue) {
return newValue === "true" && element[0].focus();
});
};
});
Update Sorry for the delay, had a few things to tend to.
The reason why this works is because it is constantly saving the index value of the item in the array you are currently selecting. Once focus is lost, focus is returned immediately by going to that index.
If we're talking about multiple arrays, however, you'll need to refactor the setSelected code to say which array it is.
So you'd want to change
focus="{{$index == selectedDevice.index}}"
to something like
focus="{{$index == selectedDevice.index && selectedDevice.kind == 'points'}}"
Where points is the category of the array where the code appears.
I sorted this one by downloading the most recent version of angularFire.js, seems like bower installed the on that didn't have this fix. now my contentEditable is!
I'm trying to create a form like below, this using ng-repeat directive in angular and it whenever I created a new row complains
"Duplicates in a repeater are not allowed.".
While I understand the solution for this is by putting "track by $index", however it causes another issue, which clicking delete on one row deletes the value of other field. So I suspect that track by index is OK for static text but not input form. So how to use ng-repeat correctly for my case?
My jsfiddle : demo.
Edit : I do aware that json array of object will solve my issue ( because for object angular create $$hashKey ) and already implemented this for most of my other module. But I am actually expecting some fix that can be done without really change my json array of string. Sorry for not being clear.
My current code :
HTML
<div class="row-fluid spacer10">
<a ng-click="addAKA()" class="btn btn-primary spacer5 left30"><i class="icon-plus icon-white"></i> Add New Alias</a>
</div>
<div class="row-fluid spacer10"></div>
<div class="row-fluid spacer5" ng-repeat="item in aliasList track by $index">
<input type="text" class="span6 left30" ng-model="item">
<button class="btn btn-danger" ng-click="deleteAKA($index)">delete</button>
<BR/>
</div>
Javascript
$scope.addAKA = function ()
{
if($scope.aliasList == null)
{
$scope.aliasList = [];
}
$scope.aliasList.push("");
$scope.aliasjson = JSON.stringify($scope.aliasList);
}
$scope.deleteAKA = function (idx)
{
var aka_to_delete = $scope.aliasList[idx];
$scope.aliasList.splice(idx, 1);
$scope.aliasjson = JSON.stringify($scope.aliasList);
}
I would guess this is caused when there are more than one empty strings in the list.
If this is the case, it is caused because any two empty strings are equals in JS and Angular repeater does not allow duplicate values (as clearly stated in the message). This is a valid decision as they have to relate an object in the list with its DOM tree to minimize DOM manipulation.
A solution would be to insert simple objects containing the string in the model:
$scope.addAKA = function () {
...
$scope.aliasList.push({value:""});
...
};
And adjust your template:
<input type="text" class="span6 left30" ng-model="item.value">
Since all new objects are different, your problem should be solved.
See a fiddle where a filter is implemented to transform the model back to a list of strings.
When you type in a new created input, your list stays the same. Angular on any list change will update the view (ng-repeat) and remove all new stored text. Therefore we need to add ng-change to update our list on any input change
Add ng-change="change(i, $index) to your item and it should work
HTML
<div ng-controller='ctrl'>
<ol>
<li ng-repeat='i in list track by $index'>
<input type='text' ng-model='i' ng-change="change(i, $index)"></input>
<button ng-click='deleteItem($index)'>Delete</button>
</li>
</ol>
<button ng-click='addItem()'>Add</button>
<div>ITEM: {{list | json}}</div>
</div>
Javascript
angular.module("app", []).controller("ctrl", function ($scope) {
$scope.list = ["one","two"];
$scope.addItem = function ()
{
$scope.list.push("");
};
$scope.deleteItem = function (idx)
{
var item_to_delete = $scope.list[idx];
$scope.list.splice(idx, 1);
};
$scope.change = function (item, idx)
{
$scope.list[idx] = item;
};
});
See fixed Demo in DEMO
Yes, pushing more than one empty string will result in ng-repeat complaining.
In addition, you can also try:
if ($scope.aliasList.indexOf(VALUE_TO_ADD) === -1) {
...
}