I hope this is quite simple. I'm trying to add the last object in a array to the array again.
Like this:
arr = [1, 2]
result = [1, 2, 2]
Just with this code instead:
$scope.template = {
ressource: {
level: [{
gain: [],
cost: []
}]
}
};
$scope.addLevel = function() {
var last = $scope.template.ressource.level[$scope.template.ressource.level.length - 1];
$scope.template.ressource.level.push(last);
};
However I'm getting this error:
Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: level in template.ressource.level, Duplicate key: object:51, Duplicate value: {"gain":[],"cost":[]}
EDIT
So i've tried the track by but I don't seem to be getting anything new so here is the HTML part of my code where I use the gain and cost values:
<div ng-show="template.type === 'building'" class="vertical-spacing">
<label>Ressource gain per level</label>
<ul class="list-group">
<li ng-repeat="level in template.ressource.level track by $id(level)"
class="list-group-item"
ng-hide="template.ressource.level.indexOf(level) === 0">
<span>
Level: {{ template.ressource.level.indexOf(level) }}
</span>
<span class="pull-right">
<a href="" ng-click="removeLevel(level)">
<span class="glyphicon glyphicon-remove"></span>
</a>
</span>
<ul>
<li class="list-group-item" ng-repeat="gain in level.gain track by $id(gain)">
<div class="form-inline">
<div class="form-group">
<label>Amount</label>
<input type="number"
class="form-control"
ng-model="gain.amount"
min="1"
required>
</div>
<div class="form-group">
<label>Ressource</label>
<select required
ng-model="gain.ressource"
ng-init="gain.ressource = gain.ressource || ressources.basic[0]"
ng-options="ressource._id as ressource.name | capitalize for ressource in ressources.basic"
class="form-control selectWidth">
<option style="display:none" value="" disabled>select a ressource</option>
</select>
</div>
<span class="pull-right">
<a href="" ng-click="removeGain(level, gain)">
<span class="glyphicon glyphicon-remove"></span>
</a>
</span>
</div>
</li>
</ul>
<span>
<a href="" ng-click="addGain(level)" ng-hide="(level.gain.length + 1) > ressources.basic.length">
<span class="glyphicon glyphicon-plus"></span> Add ressource
</a>
</span>
</li>
</ul>
<span>
<a href="" ng-click="addLevel()">
<span class="glyphicon glyphicon-plus"></span> Add level
</a>
</span>
</div>
<div ng-show="template.type === 'building'" class="vertical-spacing">
<label>Ressource cost per level</label>
<ul class="list-group">
<li ng-repeat="level in template.ressource.level track by $id(level)"
class="list-group-item"
ng-hide="template.ressource.level.indexOf(level) === 0">
<span>
Level: {{ template.ressource.level.indexOf(level) }}
</span>
<span class="pull-right">
<a href="" ng-click="removeLevel(level)">
<span class="glyphicon glyphicon-remove"></span>
</a>
</span>
<ul>
<li class="list-group-item" ng-repeat="cost in level.cost track by $id(cost)">
<div class="form-inline">
<div class="form-group">
<label>Amount</label>
<input type="number"
class="form-control"
ng-model="cost.amount"
min="1"
required>
</div>
<div class="form-group">
<label>Ressource</label>
<select required
ng-model="cost.ressource"
ng-init="cost.ressource = cost.ressource || ressources.categories[0]"
ng-options="ressource._id as ressource.name | capitalize for ressource in ressources.categories"
class="form-control selectWidth">
<option style="display:none" value="" disabled>select a ressource</option>
</select>
</div>
<span class="pull-right">
<a href="" ng-click="removeCost(level, cost)">
<span class="glyphicon glyphicon-remove"></span>
</a>
</span>
</div>
</li>
</ul>
<span>
<a href="" ng-click="addCost(level)" ng-hide="(level.cost.length + 1) > ressources.categories.length">
<span class="glyphicon glyphicon-plus"></span> Add ressource
</a>
</span>
</li>
</ul>
<span>
<a href="" ng-click="addLevel()">
<span class="glyphicon glyphicon-plus"></span> Add level
</a>
</span>
</div>
The error now states:
Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: level in template.ressource.level track by $id(level), Duplicate key: object:51, Duplicate value: {"gain":[],"cost":[]}
You have an angular error. Use track by in your ng-repeat.
<div ng-repeat="n in template.ressource.level track by $index">
Reference
https://docs.angularjs.org/api/ng/directive/ngRepeat#tracking-and-duplicates
By default, ngRepeat does not allow duplicate items in arrays. This is because when there are duplicates, it is not possible to maintain a one-to-one mapping between collection items and DOM elements.
If you do need to repeat duplicate items, you can substitute the default tracking behavior with your own using the track by expression.
Related
this is my first question here, I'm really desperate and I hope you can help me.
I'm trying to build a post/comment/reply system like Facebook by using vuejs, I'm using v-for to loop all posts/comments/replies after I use Axios to fetch data in my database (using laravel API), the problem here that I have input with v-model attached to a form inside my v-for loops when I type in my of my input it appears in all other inputs that has been looped here is an image for better understanding -> duplicate input,
<div class="panel panel-white post panel-shadow" v-for="(post) in posts" >
<div class="post-heading">
<div class="pull-left image">
<img src="https://bootdey.com/img/Content/user_1.jpg" class="img-circle avatar" alt="user profile image">
</div>
<div class="pull-left meta">
<div class="title h5">
<b>{{post.user.name}} </b>
made a post.
</div>
<h6 class="text-muted time">(number) minute ago</h6>
</div>
</div>
<div class="post-description">
<p>{{post.content}}</p>
<div class="stats">
<a href="#" class="btn btn-default stat-item">
<i class="fa fa-thumbs-up icon"></i>2
</a>
<a href="#" class="btn btn-default stat-item">
<i class="fa fa-share icon"></i>12
</a>
</div>
</div>
<div class="post-footer">
<form>
<div class="input-group">
<input type="text" name="comment" class="form-control" v-model.lazy="comments.comment" :class="{ 'is-invalid': comments.errors.has('comment') }" required="required" autocomplete="off">
<has-error :form="comments" field="comment"></has-error>
<span class="input-group-addon">
<button type="submit" class="fa fa-send form-control" #click.prevent="addComment(post.id)" >Send</button>
</span>
</div>
</form>
<ul class="comments-list" v-for="(comment) in post.comments?post.comments:''">
<li class="comment" >
<a class="pull-left" href="#">
<img class="avatar" src="https://bootdey.com/img/Content/user_1.jpg" alt="avatar">
</a>
<div class="comment-body">
<div class="comment-heading">
<h4 class="user">{{comment.user?comment.user.name:"-"}}</h4>
<h5 class="time">(number) minutes ago</h5>
</div>
<p>{{comment.comment}}</p>
<form>
<div class="input-group">
<input type="text" name="reply" class="form-control" v-model="replies.reply" :class="{ 'is-invalid': replies.errors.has('reply') }" required="required" autocomplete="off">
<has-error :form="replies" field="reply"></has-error>
<span class="input-group-addon">
<button type="submit" class="fa fa-send form-control" #click.prevent="addReply(comment.id)" >Send</button>
</span>
</div>
</form>
</div>
<ul class="comments-list" v-for="reply in comment.reply?comment.reply:''">
<li class="comment">
<a class="pull-left" href="#">
<img class="avatar" src="https://bootdey.com/img/Content/user_3.jpg" alt="avatar">
</a>
<div class="comment-body">
<div class="comment-heading">
<h4 class="user">{{reply.user.name}}</h4>
<h5 class="time">(number) minutes ago</h5>
</div>
<p>{{reply.reply}}</p>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
the easy way is put every post, comment and reply in separate components remember that's components for, to have separate states(or data) that you can easily manipulate it this way you write not only hard too extends but also its unreadable.
but the only way too bind a state in loop the way i said you can try with computed
v-model="replies[index].reply"
to:
v-model="replyComputed(index)"
Your problem is that replies.reply is the same for every looped comment. You should bind the reply to the specific comment you currently are iterating over e.g. v-model="comment.reply".
I am looking at how to populate a hidden_field with the value of a f.select field however I am having difficulty. I was going to use javascript but the html created from my haml makes it difficult to assign an id to the select I wish to pass the value from.
= f.select :trial, [['Yes', 'true'], ['No', 'false']], {}, :id => "free-trial", class: 'selectpicker mandatory'
This generates the following html
<div class="col-sm-4">
<select class="selectpicker mandatory" id="free-trial" name="campaign[trial]" style="display: none;">
<option value="true">Yes</option>
<option value="false" selected="selected">No</option>
</select>
<div class="btn-group bootstrap-select mandatory">
<button type="button" class="btn dropdown-toggle" data-toggle="dropdown" data-id="free-trial" data-original-title="" title="">
<span class="filter-option pull-left">No</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" style="max-height: 133px; overflow-y: auto; min-height: 0px;">
<li rel="0">
<a tabindex="0" class="">
<span class="text">
Yes
</span>
<i class="icon-ok check-mark"></i>
</a>
</li>
<li rel="1" class="selected">
<a tabindex="0" class="">
<span class="text">No</span>
<i class="icon-ok check-mark"></i>
</a>
</li>
</ul>
</div>
</div>
This html when display is actually two selects one hidden with the ID assigned and a second which is displayed without the id associated. As a result my clicking on the visible select means the associated jquery doesnt fire.
If i understood you properly, you want catch value from select to hidden_input. You can use jquery to do that:
$('#free-trial').on('change', function(){
var val = $(this).val();
$('#your_hidden_input').attr('value', val);
}
I have a drop down that is populated witht eh following code:
<span class="bluedept">Department:</span>
<select class="selectpicker deptpicker" selectpicker ng-model="department" ng-controller="CaseListCtrl" ng-change="getCalendar();">
<option ng-repeat="department in departments track by $index">{{department.CourtRoom}}</option>
</select>
I then have this in my controller:
JBenchApp.controller('CaseListCtrl', ['$scope', '$http',
function ($scope, $http) {
// Case list stuff here
$scope.getCalendar = function () {
var e = document.getElementById("deptSelect");
var department = e.options[e.selectedIndex].text;
console.log(department);
$http.get('http://10.34.34.46/BenchViewServices/api/Calendar/LA/' + department + '/08-27-2015').success(function (response) {
$scope.cases = response;
});
}
$http.get('http://10.34.34.46/BenchViewServices/api/CourtDept/LA').success(function (response) {
$scope.departments = response;
});
}]);
The $scope.cases updates with new data, but the partial doesn't change to match that. What can I do to make the partial view refresh?
EDIT TO ADD PARTIAL VIEW CODE:
<div class="row" ng-show="$parent.loggedin">
<div class="col-sm-12 calselectrow">
<div class="inner-addon left-addon">
<span class="glyphicon glyphicon-calendar calicon"></span>
<input type="text" id="calpick" ng-model="date" jdatepicker />
<i class="glyphicon glyphicon-calendar calclick"></i>
>>
<span class="bluedept">Department:</span>
<select class="selectpicker deptpicker" id="deptSelect" selectpicker ng-model="department" ng-controller="CaseListCtrl" ng-change="getCalendar();">
<option ng-repeat="department in departments track by $index">{{department.CourtRoom}}</option>
</select>
</div>
</div>
</div>
<div class="row" ng-show="$parent.loggedin">
<div ng-controller="CaseListCtrl">
<div class="col-sm-8 col-sm-offset-2 caselist" ng-repeat-start="case in cases track by $index">
<div class="sequence">
<input type=text class="seq-box" size="1" value="{{case.sequence}}" />
</div>
<div class="casetitle">
<span class="caselink">{{case.Case_Number}}</span>
<a href="calendar" data-toggle="tooltip" data-placement="top" title="Calendar" class="btn btn-xs btn-danger calicon-view" tooltip>
<span class="glyphicon glyphicon-calendar"></span>
</a>
<a href="documents/{{case.Case_Number}}" data-toggle="tooltip" data-placement="top" title="Documents" class="btn btn-xs btn-danger calicon-view" tooltip>
<span class="glyphicon glyphicon-file"></span>
</a>
<a href="parties/{{case.Case_Number}}" data-toggle="tooltip" data-placement="top" title="Parties" class="btn btn-xs btn-danger calicon-view" tooltip>
<span class="glyphicon glyphicon-user"></span>
</a>
{{case.Case_Title}}
</div>
</div>
<div class="col-sm-8 col-sm-offset-2 caselist-bottom">
<div class="col-sm-9 col-sm-offset-1">
<div class="hearing-time">{{case.Sched_Time}} {{case.AmPm}}</div>
<div class="hearing-title">{{case.Event}}</div>
</div>
</div>
<div ng-repeat-end></div>
</div>
</div>
Try tracking the cases by their id or remove track by from the ng-repeat.
ng-repeat not update model with track by
UPDATE:
I think the issue is that your are using ng-controller twice within your html causing a second scope to be created. Wrap your html in a container div and apply the ng-controller attribute to that only.
I have following HTML structure:
<button class="dropdown-toggle selectpicker btn btn-primary" data-toggle="dropdown" type="button" data-id="strategic_reseller_country" title="United States">
<div class="dropdown-menu open" style="max-height: 245.6px; overflow: hidden; min-height: 40px;">
<ul class="dropdown-menu inner selectpicker" role="menu" style="max-height: 243.6px; overflow-y: auto; min-height: 38px;">
<li class="" rel="0">
<a class="" style="" tabindex="0">
<span class="text"/>
<i class="glyphicon glyphicon-ok icon-ok check-mark"/>
</a>
</li>
<li rel="1">
<a class="" style="" tabindex="0">
<span class="text">Andorra</span>
<i class="glyphicon glyphicon-ok icon-ok check-mark"/>
</a>
</li>
<li rel="2">
<a class="" style="" tabindex="0">
<span class="text">United Arab Emirates</span>
<i class="glyphicon glyphicon-ok icon-ok check-mark"/>
</a>
</li>
<li rel="3">
<a class="" style="" tabindex="0">
<span class="text">Afghanistan</span>
<i class="glyphicon glyphicon-ok icon-ok check-mark"/>
</a>
</li>
<li rel="4">
<li rel="5">
<li rel="6">
<li rel="7">
<li rel="8">
<li rel="9">
<a class="" style="" tabindex="0">
<span class="text">Netherlands Antilles</span>
<i class="glyphicon glyphicon-ok icon-ok check-mark"/>
</a>
</li>
</ul>
</div>
So how could I get the item from list?
I am new to Node.Js (JavaScript) so I don't know how to achieve it in node.Js but it can be achieved in java as follows :
Select dropdown = new Select(driver.findElement(By.class("dropdown-menu inner selectpicker")));
dropdown.selectByVisibleText("Andorra");
Just click the desired option.
driver.findElement(webdriver.By.css('#mySelection > option:nth-child(4)'))
.click();
or by value
driver.findElement(webdriver.By.css('#mySelection > option[value=apple]'))
.click();
Note that I've changed your 'By' to a css selector. I'm lazy and like to just drill into the option from developer tools and select Copy CSS Path (chrome) or Copy Unique Selector (firefox).
You can create a function that select the value you desire
like this...
driver.wait(
until.elementLocated(By.id("continents")), 20000
).then(element => {
selectByVisibleText(element, "Africa")
});
function selectByVisibleText(select, textDesired) {
select.findElements(By.tagName('option'))
.then(options => {
options.map(option => {
option.getText().then(text => {
if (text == textDesired)
option.click();
});
});
});
}
For anyone stuck on this, here is how I did it for a <select> option:
<select id='mySelection'>
<option value='0'>Hello</option>
<option value='1'>Everybody</option>
<option value='2'>Got</option>
<option value='3'>It</option>
</select>
In Selenium for NodeJS, you would grab the It <option>:
var webdriver = require('selenium-webdriver');
var driver = new webdriver.Builder().
withCapabilities(webdriver.Capabilities.chrome()).
build();
driver.findElement(webdriver.By.id('mySelection')).sendKeys('It');
When you .sendKeys() for the option it has to equal the text displayed to the user in the dropdown.
In your case just grab the parent element then sendKeys() for the child element.
I would use Cheerio for this.
module.exports = {
"login": function (browser) {
var cheerio = require("cheerio")
browser
.url("http://www.wikipedia.org")
.waitForElementVisible('body', 1000)
.waitForElementVisible('select#searchLanguage', 1000)
.source(function(result) { // .source() dump the page source in the variable
$ = cheerio.load(result.value)
var num_languages = $('select#searchLanguage option').length
console.log('Wikipedia.org Languages: ' + num_languages);
})
.end();
}
};
or simply using the .execute() command
If you have
<select id='mySelection'>
<option value='cat'>Cat!</option>
<option value='dog'>Dog!</option>
<option value='rat'>Rat!</option>
</select>
You can use the method sendKeys passing either the value or the text
driver.findElement(webdriver.By.id('mySelection')).sendKeys('Rat!');
or
driver.findElement(webdriver.By.id('mySelection')).sendKeys('rat');
If I have a list:
<div class="item active ui-sortable">
<li class="player ui-droppable" data-name="Kev" data-pick-id="534dd2424b65762a89020000">
Teddy Bridgewater
<div class="pull-right" id="player-badges">
<span id="points-holder">
10
</span>
<span class="remove-player player-badge">
<i class="fa fa-trash-o"></i>
</span>
</div>
</li>
<li class="player pick-blank ui-droppable" data-name="" data-pick-id="534dd2424b65762a89030000">
EMPTY
<div class="pull-right" id="player-badges">
<span id="points-holder">
7
</span>
</div>
</li>
<li class="player ui-droppable" data-name="kevin" data-pick-id="534dd2424b65762a89040000">
kevin
<div class="pull-right" id="player-badges">
<span id="points-holder">
5
</span>
<span class="remove-player player-badge">
<i class="fa fa-trash-o"></i>
</span>
</div>
</li>
</div>
And I'm making an array of data-pick-id, how would I then rewrite this list based on the array ["534dd2424b65762a89020000", "534dd2424b65762a89030000", "534dd2424b65762a89040000"]?
Context: I'm using jquery-sortable and want to preserve the dom structure of the data-pick-id only. That way I can change all the contents and update to server while retaining the actual dom position by ID.
Again, all I want to do is loop through and write that array to each data-pick-id. Would a loop be the best option?
A simple .each() would suffice:
var ids = [1, 2, 3];
$('.player').each(function(i) {
$(this).data('pick-id', ids[i]);
});
You may wish to make the selector more explicit.