I came across this whilst building sort buttons for a data table. I wanted to have each sort button to toggle between a DESC (sort down) and ASC (sort up) and inactive - the default sort.
So I created two models one was going to be the one that changed as the program progressed and one was a default to reset the values
var model = {
order: {
field: 'creationDate',
direction: 'DESC'
},
orderDefault: {
field: 'creationDate',
direction: 'DESC'
}
}
$('button').click(function() {
if (model.order.field == "creationDate") {
model.order.field = 'newField'
} else if (model.order.direction == 'DESC') {
model.order.direction = 'ASC'
} else {
// This casuses the orderDefault object to be a clone of the order object
// so assignment of values in model.order are refelected in model.orderDefault GOTCHA!!!!
model.order = model.orderDefault
}
$('#result').html('field:' + model.order.field + " direction:" + model.order.direction);
}
)
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<button class="btn btn-primary">
click me
</button>
<div id="result">
</div>
So you will see that it all works as expected until the point where we reset the order object. the second time we get to this point in the code you will see that it never resets. It seems that the orderDefault obj becomes an alias of order obj.
the answer is that instead of assigning orderDefault you need to assign the values of orderDefault
so NOT
model.order = model.orderDefault
BUT
model.order.direction = model.orderDefault.direction
model.order.field = model.orderDefault.field
I hope this helps someone.
Related
I'm trying to show items in a table every time the list of item changes. I've got a function and a computed but I'm not quiet sure if I'm doing it right.
Function(){
Every time a new value comes in: changeList.push(item);
}
Then I have this computed
Computed:
changedData: function(){
return this.changeList;
}
HTML
<tr v-for="change in changedData">
<td>change.value</td>
<tr>
It does print out every item in the list when it is changed, but the thing is I don't want it to print out the items that already printed out, only new items.
EDIT: Found the problem (push function)
for(index in question.Answers){
if(question.Answers[index].Selected === "selected" &&
question.Answers[index].Value === savedQuestion.Value){
this.myList.push({newValue: question.Answers[index].Value, oldValue:
savedQuestion.Value});
}
}
This will add all the questions with their value regardless if the list already contains the same question with the exact same values.
So to achieve this you can check in the array if the object with same values is present or not.If not than only push the values.
Below is the updated code.
const search = (newVal,oldVal) => array.find(element => (element.newValue === newVal && element.oldValue === oldVal));
for(index in question.Answers){
if(!search(question.Answers[index].Value,savedQuestion.Value))
if(question.Answers[index].Selected === "selected" &&
question.Answers[index].Value === savedQuestion.Value){
this.myList.push({newValue: question.Answers[index].Value, oldValue:
savedQuestion.Value});
}
}
In your code computed is actually not needed.Here is the basic example of adding dynamic values into the list at runtime.
https://codepen.io/anon/pen/LqKjMM?editors=1111
Template code =>
<script src="//vuejs.org/js/vue.js"></script>
<h1>Example of managing a Vue.js list</h1>
<div id="products">
<vue-products></vue-products>
</div>
<script type="text/template" id='vue-products-template'>
<div>
<form v-on:submit.prevent="addProduct">
<input type="text" v-model="productName">
<input type="submit" value="Add"></input>
</form>
<ul>
<li v-for="name in productNames">{{name}}</li>
</ul>
</div>
</script>
Script code =>
Vue.component('vue-products', {
template: '#vue-products-template',
data: function() {
return {
productName: "",
productNames: ['booze', 'tums', 'peas', 'short wave radio'],
}
},
methods: {
addProduct: function() {
this.productNames.push(this.productName)
this.productName = ""
}
}
})
$(function() {
new Vue({el: '#products', data: {}})
});
I use WordPress. I have an audio player on my site. When a user clicks the play button on an individual song, it will play the song. The post ID is how the player determines which song to play.
So in the DIV that wraps the button tag, I assign an attribute: data-id which is the post id of the song. My app then grabs the MP3 file that is associated with that post ID. Simple.
My play button tag is essentially this:
<div class="item item-action" data-id="1161">
<button class="btn-playpause"></button>
</div>
The important parts of my player.js file that handles the click event and plays the song is below:
Simulate the play button
// simulate the play btn
$(document).on('click.btn', '.btn-playpause, .btn-queque', function(e){
e.stopPropagation();
var self = $(this),
item = $(this).closest('.item'),
id = item.attr('data-id'),
type = item.data('user-id') ? 'user' : 'post',
play = true;
if(!player){
getItem(id, type).done(function(obj){
if(obj.status == 'success'){
mep.mepPlaylistTracks = obj.tracks;
initPlayer();
player && player.mepSelect(0, true);
}
});
return;
}
if(self.is('.btn-queque')){
play = false;
self.parent().dropdown('toggle');
}
if( self.hasClass('is-playing') ){
self.removeClass('is-playing');
player.pause();
}else{
var index = player.find(id);
if( index !== -1){
var track = player.mepGetCurrentTrack();
if(track && track.id == id && !play) return;
player.mepSelect(index, true);
}else{
getItem(id, type).done(function(obj){
if(obj.status == 'success'){
addToPlay(obj.tracks, play);
}
});
}
}
});
getItem() function
function getItem(id, type){
return $.ajax({
type : "post",
dataType : "json",
url : ajax.ajax_url,
data : {action: "ajax_music", id : id, type: type, nonce: ajax.nonce}
});
}
addToPlay() function
function addToPlay(obj, play){
if(obj.length == 1){
player.mepAdd( obj[0], play );
}else if(obj.length > 1){
if(play){
player.options.mepPlaylistTracks = obj;
player.updatemepList();
player.mepSelect(0, true);
}else{
for(var i=0; i<obj.length; i++){
player.mepAdd( obj[i] );
}
}
}
}
You can see that once I click the play button, depending on a condition, it fires the getItem() function. getItem() takes the data-id attribute defined earlier as the id variable and begins to play the song.
The addToPlay() function will add another song to the playlist if there is already an instance of the player open. And when a user clicks another play button, it will add that new song to the list and begin to play that song.
Is it possible to modify this code so that I can add multiple data-id values to my button code so that my script will recognize there is more than one song id and add those to my playlist, using the addToPlay() function?
I'd like to do something like this
<div class="item item-action" data-id="1161, 1288, 1456">
<button class="btn-playpause"></button>
</div>
The reason I want to do this, is I would like to add a feature that will allow my users to Play All Songs on the current page they're viewing. Some of our pages, like the catalog page, displays dozens of songs that fit a certain criteria and I'd like the option to play all songs on the page.
Can anyone help push me in the right direction?
Thanks.
You could store a JSON encoded array in the data-id tag. Like:
<div class="item item-action" data-id='[1161, 1288, 1456]'>
<button class="btn-playpause"></button>
</div>
Then you can parse the array, and loop over its contents, adding each id to the queue.
See an example of parsing here: https://gist.github.com/charliepark/4266921
Here I present a alternative method.
In your markup, you can set the data-id='[1234] attribute as an encoded array. You can then collect those properties and set them as the data id property on a given element using jQuery as I illustrate here:
let someElement = $('.item');
let setItems = someElement.data('id');
let thisItem = someElement.attr('data-id');
IMPORTANT: the someElement.data('id') returns the same as the someElement.attr('data-id') (except as a string) IF nothing for that element has been set with someElement.data('id',[567,890]); which sets the property id for someElement. So, once set that way
let x = someElement.data('id'); // returns the array 567,890
let org = someElement.attr('data-id'); // still returns the [1234]
This code illustrates:
let someElement = $('.item').first();
let setItems = someElement.data('id');
let thisItemS = someElement.attr('data-id');
// make an array of the string
let thisItem = JSON.parse(thisItemS);
console.log(typeof setItems, setItems, typeof thisItemS, thisItemS, typeof thisItem, thisItem);
someElement.data('id', [567, 890]);
let x = someElement.data('id'); // returns and gets the array 567,890
let org = JSON.parse(someElement.attr('data-id')); // still returns the string [1234] made into an array
// merge with no duplicates, in the other code I used .map()
let z = Array.from(new Set(x.concat(org)));
console.log(x, org, z);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class='item' data-id='[1234]'>me the item</div>
This code show a set of elements using the concepts introduced above plus:
Set some check boxes for selection
Get the values (all)
Get the values (checkbox checked)
You can combine any of this for your original purpose, but you will need to manage that addition of new items values (and subsequently how to remove perhaps) on a given element. You might even add to a <select> element so the user then can pick some to remove perhaps...
function showPicks(choicedata) {
let ul = $('#choices').append('<ul />');
//ul.remove('li');
ul.html('empty');
ul.text('choices:');
$.each(choicedata, function(index, choice) {
let c = $('<li/>').html(choice);
ul.append(c);
});
}
$(function() {
let mydata = $('.item').map(function() {
return $(this).data('id');
}).get();
$('.all-items').data("id", mydata);
showPicks(mydata); //now do what is desired
});
$('item.item-action').filter(':not(".some-items")')
.on('click', '.btn-playpause', function() {
let setItems = $(this).closest('.item').data('id');
let thisItem = $(this).closest('.item').attr('data-id');
// do what you wish here, I show them
showPicks(setItems);
});
$('.some-items').on('click', '.btn-playpause', function(event) {
let mydata = $('.item-select').filter(':checked')
.add(this) // set an additional one from the button
.closest('.item')
.map(function() {
return $(this).data('id');
}).get();
$(event.delegatedTarget).data("id", mydata);
let setItems = $(event.delegatedTarget).data('id');
// the original pick
let thisItem = $(event.delegatedTarget).attr('data-id');
// do what you wish here
showPicks(mydata);
});
.btn-playpause {
height: 2em;
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="item item-action" data-id="[1161]">
<button class="btn-playpause" type="button"></button>
<input type="checkbox" class="item-select" />
</div>
<div class="item item-action" data-id="[1163]">
<button class="btn-playpause"></button>
<input type="checkbox" class="item-select" />
</div>
<div class="item item-action" data-id="[9961,7744]">
<button class="btn-playpause" type="button"></button>
<input type="checkbox" class="item-select" />
</div>
<div class="item item-action all-items" data-id="[42]">
<button class="btn-playpause" type="button">Play All</button>
</div>
<div class="item item-action some-items" data-id="[95]">
<button class="btn-playpause" type="button">Play selected</button>
</div>
<div id="choices">none</div>
I am trying to filter through an array and display data based on the click event. There are three buttons that control different actions.
1.Button rollers display all the individuals who are rolling
2.Buttons non-rollers display lazy peeps who don't roll.
3.Buttons All display all the rollers and non-rollers.
I am trying to figure out if there is any way to display all rollers with the same filterRollers() function. I understand just resetting the state of rollingData back to the original value in a different function would do the trick but I am trying to limit to using one function. I will appreciate Any suggestion regarding the best practices . Thank you
var data =[
{ name="adam", task="roll" ,isrolling=true},
{ name="jack", task="roll" ,isrolling=false},
{ name="r2d2", task="roll" ,isrolling=true},
{ name="spidy", task="roll" ,isrolling=false}
]
this.state={
rollingState=data,
rollingData=data
}
filterRollers(status) {
let list = this.state.rollingState.filter(roll => {
return roll.isrolling === status
})
this.setState({
rollingData:list
})
}
render(){
return(
<div>
<button onClick={this.filterRollers(true)}>rollers </button>
<button onClick={this.filterRollers(false)}>non-rollers </button>
<button onClick={this.filterRollers(something)}> All </button>
{this.state.rollingData.map(roll=>{
return <Something roll={roll}/>
}
)}
</div>
)
}
When you want to show all the rollers, you can just call the function without any parameters. And then at the beginning of filterRollers function you can check if the parameter is undefined. If it is, just return all the data. If not, filter:
filterRollers(status) {
let list;
if(typeof status !== 'undefined'){
list = this.state.rollingState.filter(roll => {
return roll.isrolling === status
})
} else{
list = data.slice();
}
this.setState({
rollingData:list
})
}
Call the function like this, when you want to show all the rollers:
<button onClick={this.filterRollers()}> All </button>
I am starting with a simple TODO app with Aurelia, RethinkDB & Socket.IO. I seem to have problem with re-rendering or re-evaluating an object that is changed through Socket.IO. So basically, everything works good on the first browser but doesn't get re-rendered in the second browser while displaying the object in the console does show differences in my object. The problem is only on updating an object, it works perfectly on creating/deleting object from the array of todo items.
HTML
<ul>
<li repeat.for="item of items">
<div show.bind="!item.isEditing">
<input type="checkbox" checked.two-way="item.completed" click.delegate="toggleComplete(item)" />
<label class="${item.completed ? 'done': ''} ${item.archived ? 'archived' : ''}" click.delegate="$parent.editBegin(item)">
${item.title}
</label>
<i class="glyphicon glyphicon-trash"></i>
</div>
<div show.bind="item.isEditing">
<form submit.delegate="$parent.editEnd(item)">
<input type="text" value.bind="item.title" blur.delegate="$parent.editEnd(item)" />
</form>
</div>
</li>
</ul>
NodeJS with RethinkDB changefeeds
// attach a RethinkDB changefeeds to watch any changes
r.table(config.table)
.changes()
.run()
.then(function(cursor) {
//cursor.each(console.log);
cursor.each(function(err, item) {
if (!!item && !!item.new_val && item.old_val == null) {
io.sockets.emit("todo_create", item.new_val);
}else if (!!item && !!item.new_val && !!item.old_val) {
io.sockets.emit("todo_update", item.new_val);
}else if(!!item && item.new_val == null && !!item.old_val) {
io.sockets.emit("todo_delete", item.old_val);
}
});
})
.error(function(err){
console.log("Changefeeds Failure: ", err);
});
Aurelia code watching Socket.on
// update item
socket.on("todo_update", data => {
let pos = arrayFindObjectIndex(this.items, 'id', data.id);
if(pos >= 0) {
console.log('before update');
console.log(this.items[pos]);
this.items[pos] = data;
this.items[pos].title = this.items[pos].title + ' [updated]';
console.log('after update');
console.log(this.items[pos]);
}
});
// create item, only add the item if we don't have it already in the items list to avoid dupes
socket.on("todo_create", data => {
if (!_.some(this.items, function (p) {
return p.id === data.id;
})) {
this.items.unshift(data);
}
});
// delete item, only delete item if found in items list
socket.on("todo_delete", data => {
let pos = arrayFindObjectIndex(this.items, 'id', data.id);
if(pos >= 0) {
this.items.splice(pos, 1);
}
});
The socket.on("todo_update", ...){} is not making the second browser re-render but showing the object in the console before/after update does show differences in the object itself. I even changed the todo title property and that too doesn't get re-rendered.
How can I get Aurelia to re-render in my second browser with the new object properties? Don't be too hard on me, I'm learning Aurelia/RethinkDB/NodeJS/Socket.IO all the same time...
Aurelia observes changes to the contents of an array by overriding the array's mutator methods (push, pop, splice, shift, etc). This works well for most use-cases and performs really well (no dirty-checking, extremely lightweight in terms of memory and cpu). Unfortunately this leaves one way of mutating an array that aurelia can't "see": indexed assignment... eg myArray[6] = 'foo'. Since no array methods were called, the binding system doesn't know the array changed.
In your case, try changing this:
// update item
socket.on("todo_update", data => {
let pos = arrayFindObjectIndex(this.items, 'id', data.id);
if(pos >= 0) {
console.log('before update');
console.log(this.items[pos]);
this.items[pos] = data; // <-- change this to: this.items.splice(pos, 1, data);
this.items[pos].title = this.items[pos].title + ' [updated]';
console.log('after update');
console.log(this.items[pos]);
}
});
I am using https://github.com/localytics/angular-chosen to allow for select tags with search capability for many options.
The problem I'm having is with preselecting an option on an already saved vendor object. When creating a new one there is now issue, but if we're viewing an existing vendor, I want to show the vendor's name in the select box, rather than the placeholder.
<select chosen
ng-model="myVendor"
ng-options="vendor['public-id'] as vendor.name for vendor in vendors"
data-placeholder="Nah">
</select>
And in my controller, I'm setting the model by hand $scope.myVendor = "Some value"
The problem is that I'm populating the options with an object, instead of a key/value. I found an example of it working with a key/value, but haven't had success adapting this to objects as options.
I've even tried setting myVendor to the matching object that I want selected, with no luck.
Plunker of issue
I updated the plunker and change my previous changes on the plugin. this was not the issue. I don't understand how it was giving me errors there.
The solution is to track with an object and two functions the id and the name:
// Controller
$scope.vendors = [
{
"public-id": "1234",
"name": "stugg"
},
{
"public-id": "4321",
"name": "pugg"
}
];
$scope.myVendor = {name: "pugg", id:""};
$scope.updateMyVendorName = function () {
var found = false,
i = 0;
while (!found && i < $scope.vendors.length) {
found = $scope.vendors[i]['public-id'] === $scope.myVendor.id;
if (found) {
$scope.myVendor.name = $scope.vendors[i].name;
}
i++;
}
}
findVendorByName();
function findVendorByName () {
var found = false,
i = 0;
while (!found && i < $scope.vendors.length) {
found = $scope.vendors[i]['name'] === $scope.myVendor.name;
if (found) {
$scope.myVendor.id = $scope.vendors[i]['public-id'];
}
i++;
}
}
// template
<select chosen class="form-control span6" ng-options="vendor['public-id'] as vendor.name for vendor in vendors" ng-model="myVendor.id" ng-change="updateMyVendorName()">
{{myVendor.name}}