Updating an ArrayAttribute with ReactJs - javascript

EDITED:
renamed shouldComponentUpdate to onClick
I'm using Ruby on Rails with the gem react-rails. The idea is to use a "PATCH" method to update an ArrayAttribute but using just JavaScript. To be clear, I'm still a newbie building a project while learning.
The story is, when a user checks a checkbox, it marks their post as complete. Their Post's table has a column for is_complete: :boolean, :default => false. Im using Postgres for the database.
var Foo = React.createClass({
getInitialState: function() {
return {
is_complete: this.props.data.is_complete, # shows "Empty object"
# is_complete: this.props.data[0].is_complete ..... shows "false"
# I could not use that because data[0] and data[1] etc...
}
},
onClick: function(){
var newVal = this.state.is_complete;
this.setState({
is_complete: React.addons.update(this.state.is_complete, { '$set': [ newVal ] })
})
},
render: function() {
var supports = (s) => (
<input type="checkbox" onChange={this.onClick}
/> Mark complete
);
return (
<div>
{this.props.data.map(supports)}
</div>
);
}
});
Index.html.erb:
<%= react_component('Foo', { data: #userlist }) %>
controller:
#userlist = #user.supports.as_json(:only => [:is_complete] # shortened for emample
When the checkbox is checked I get Uncaught TypeError: Cannot read property of is_complete of undefined
I'm looping through all of that user's posts (supports) and outputting the result on one page with a checkbox to mark as complete. I could achieve something similar by adding the chechbox inside of the edit page but I don't want that.
After the edit, I'm not getting the result I want. I want when checked, Array[1] and [2]'s is_complete is set to true or false.
Somehow I need to loop the getInitialState(), is that possible? I see not documentation on that.

Related

Checkboxes aren't filled in correctly when searching a list

A have a form where companies should be able to fill in information and then register to the site.
One of the things that they should be able to fill in is what category they belong to ie CRM, accounting software, ERP etc and they should be able to choose several options
in the list.
Though the list is quite long so they also have to be able to search it.
The foundation of this works but I realise that if I choose several of these categories (that are on the top of the list) and then start to search the list then (sometimes) I find that when I empty the search field some of the categories aren't clicked in anymore.
I have added an image 1 of when I have clicked in a couple of categories, then searched and then emptied the search field. Thereafter some categories are not checked but their checked value is still true.
Backend code (this is only run ones, i.e when loading the category list)
#app.route("/get_categories")
def get_categories():
search_item = request.args.get("search_item")
categories_query = Session.query(enjordplatformCategories ).all()
show_categories_list =[]
show_categories_dictionary=[]
i=0
for category in categories_query:
show_categories_dictionary = {
'i':i,
'id' : category.id,
'category' :category.subcategory_name_english,
'checked':False
}
show_categories_list.append(show_categories_dictionary)
i=i+1
response = json.dumps(show_categories_list)
return response
Frontend code
HTML
<input type="text" class="search_ruta_input" v-model="search_category" placeholder="Search Category" v-on:keyup="search_category_func(search_category)" >
<div v-for="(category,idx) in this.filtered_category_list" :key="idx">
<input type="checkbox" class="checkbox_new" :value="category.checked" v-if="!category.checked" #click="choose_category(category)">
<input type="checkbox" class="checkbox_filled" :value="category.checked" v-if="category.checked" #click="choose_category(category)">
{{category.category}} {{category.checked}}
</div>
</div>
</div>
Scripts
methods:{
load_category(search_category){
var self = this
axios({
method:'get',
url: API_URL+ '/get_categories'+'?search_item='+search_category,
})
.then(function(response){
self.category_list = response.data
})
},
search_category_func(search_category){
this.filtered_category_list = this.category_list.filter(
category_object => category_object.category.toLowerCase().includes(search_category.toLowerCase())
)
category => (category.category + " " + category.checked)
))
},
choose_category(category_object){
this.category_list = this.category_list.map(category => {
if (category.id == category_object.id){
category.checked = category.checked === false ? true : false
}
return category
})
},
}
mounted(){
this.load_category("")
setTimeout(() => {
this.search_category_func("")
}, 1000);
}
How can I resolve this?
thanks

AngularJS - Get printed value from scope inside an attribute?

I'm currently working on an AngularJS project and I got stuck in this specific requirement.
We have a service that has all the data, DataFactoryService. Then, I have a controller called DataFactoryController that is making the magic and then plot it in the view.
<div ng-repeat = "list in collection">
{{list.name}}
...
</div>
Now, we have a requirement that pass multiple data into one element. I thought an "ng-repeat" would do, but we need to have it inside an element attribute.
The scenarios are:
At one of the pages, we have multiple lists with multiple data.
Each data has a unique code or ID that should be passed when we do an execution or button click.
There are instances that we're passing multiple data.
Something like this (if we have 3 items in a list or lists, so we're passing the 3 item codes of the list):
<a href = "#" class = "btn btn-primary" data-factory = "code1;code2;code3;">
Submit
</a>
<a href = "#" class = "btn btn-default" data-factory = "code1;code2;code3;">
Cancel
</a>
In the example above, code1,code2,code3 came from the list data. I tried several approach like "ng-repeat", "angular.each", array, "ng-model" but I got no success.
From all I've tried, I knew that "ng-model" is the most possible way to resolve my problem but I didn't know where to start. the code below didn't work though.
<span ng-model = "dataFactorySet.code">{{list.code}}</span>
{{dataFactorySet.code}}
The data is coming from the service, then being called in the controller, and being plot on the HTML page.
// Controller
$scope.list = dataFactoryService.getAllServices();
The data on the list are being loaded upon initialization and hoping to have the data tags initialized as well together with the list data.
The unique code(s) is/are part of the $scope.list.
// Sample JSON structure
[
{ // list level
name: 'My Docs',
debug: false,
contents: [ // list contents level
{
code: 'AHDV3128',
text: 'Directory of documents',
...
},
{
code: 'AHDV3155',
text: 'Directory of pictures',
...
},
],
....
},
{ // list level
name: 'My Features',
debug: false,
contents: [ // list contents level
{
code: 'AHGE5161',
text: 'Directory of documents',
...
},
{
code: 'AHGE1727',
text: 'Directory of pictures',
...
},
],
....
}
]
How can I do this?
PLUNKER -> http://plnkr.co/edit/Hb6bNi7hHbcFa9RtoaMU?p=preview
The solution for this particular problem could be writing 2 functions which will return the baseId and code with respect to the list in loop.
I would suggest to do it like below
Submit
Cancel
//inside your controller write the methods -
$scope.getDataFactory = function(list){
var factory = list.map( (a) => a.code );
factory = factory.join(";");
return factory;
}
$scope.getDataBase= function(list){
var base= list.map( (a) => a.baseId);
base= base.join(";");
return base;
}
Let me know if you see any issue in doing this. This will definitely solve your problem.
You don't really have to pass multiple data from UI if you are using Angular.
Two-way data binding is like blessing which is provided by Angular.
check your updated plunker here [http://plnkr.co/edit/mTzAIiMmiVzQfSkHGgoU?p=preview]1
What I have done here :
I assumed that there must be some unique id (I added Id in the list) in the list.
Pass that Id on click (ng-click) of Submit button.
You already have list in your controller and got the Id which item has been clicked, so you can easily fetch all the data of that Id from the list.
Hope this will help you... cheers.
So basing from Ashvin777's post. I came up with this solution in the Controller.
$scope.getFactoryData = function(list) {
var listData = list.contents;
listData = listData.map(function(i,j) {
return i.code;
});
return listData.join(';');
}

How to toggle a class based on a variable passed from Laravel using a Vuejs component

This is the 3rd attempt for me to get some help on this issue, for so long I've been trying to toggle an id using Vuejs, I just want that id to change based on a boolean value passed on from Laravel, I managed to do it without components but I ran into a problem, there's multiple buttons on the page, when one gets their id updated the others do as well, so I thought maybe a component could solve this, I just can't get it to work.
Here's the blade template, this is inside a table inside a foreach loop that has access to a $courses variable:
courses.blade:
<form method="POST" action="{{ route('course.completed', $course->name) }}" id="form-submit">
{{ csrf_field() }}
#if ($course->pivot->completed == true)
<course-buttons id="this.greenClass.aClass">Done</course-buttons>
#else
<course-buttons id="this.redClass.aClass">Not Yet</course-buttons>
#endif
</form>
this is app.js:
require('./bootstrap');
Vue.component('course-buttons', require('./components/course-buttons.vue'))
var vm = new Vue({
el: '#app'
});
And this is the course-buttons.vue file:
<template>
<button #click.prevent="onSubmit({{ $course }})" type="button" class="btn btn-sm" :id=id><slot></slot></button>
</template>
<script>
export default {
props: ['id'],
data: function() {
return {
greenClass: {aClass: 'coursetrue', text: 'Done!'},
redClass: {aClass: 'coursefalse', text: 'Not Yet!'}
};
},
methods: {
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
.then(function (response){
console.log(response.data.course.pivot.completed)
if (response.data.course.pivot.completed == true){
return [
vm.redClass.aClass = 'coursetrue',
vm.redClass.text = 'Done!',
vm.greenClass.aClass = 'coursetrue',
vm.greenClass.text = 'Done!'
]
} else {
return [
vm.greenClass.aClass = 'coursefalse',
vm.greenClass.text = 'Not Yet',
vm.redClass.aClass = 'coursefalse',
vm.redClass.text = 'Not Yet'
]
}
});
}
}
}
</script>
First I know that my code isn't good, that's why I asked for help many times but with no answers at all, so if you have any tips that might help me get this code cleaner, even change it totally but just get the job done, I'm all ears.
The errors I'm getting right now is first the #click.prevent is an invalid expression, tried moving that to the tag, it doesn't do anything over there so I had that going before and now I lost it as well, also I get an error that the id is not defined on the instance, although I defined the props and data in the vue component.
if you're wondering why do I even assign the id on the tag itself and not the component, it's because of how my code is structured,e.g "If the value is true then load the tag with that id, otherwise load it with that id", once again if you can help me do this in a completely different way I'll be grateful.
One error I see is you have to use v-bind:id here, as you are assigning a vue variable:
<course-buttons v-bind:id="this.greenClass.aClass">Done</course-buttons>
One more is in onSubmit method, you dont have to return anything, and also you are using vm there, instead of that you can just do following:
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
.then((response) => {
console.log(response.data.course.pivot.completed)
if (response.data.course.pivot.completed == true){
this.redClass.aClass = 'coursetrue',
this.redClass.text = 'Done!',
this.greenClass.aClass = 'coursetrue',
this.greenClass.text = 'Done!'
} else {
this.greenClass.aClass = 'coursefalse',
this.greenClass.text = 'Not Yet',
this.redClass.aClass = 'coursefalse',
this.redClass.text = 'Not Yet'
}
});
}
Here I have also used arrow syntax, here is why.

Rails datatables select filter

I have an ajax datatable for my SKUs. For this I am using the ajax-datatables-rails gem. Searcing and sorting works perfectly, but now I'm trying to add a filtering function to my table and it doesn't seem to do anything. I used this example for the filter function: https://datatables.net/examples/api/multi_filter_select.html.
In the example, select boxes are drawn in the footer, but for me the footer is empty. Like the code doesn't run at all. I also don't get any errors.
I initialize my datatable in my coffeescrip file (assets/javascripts/vendor_skus.js.coffee) so I had to translate it to coffeescript. I'm not experienced with coffeescript or using ajax with rails so I'm kind of lost as to what is going wrong.
How I solved my problem:
The standard select boxes were problematic for my situation, as I am using AJAX for my table and the select boxes seemed to only work properly on client side tables. Instead of going with the standard select boxes, I decided to make my own custom filters. These are regular select boxes like so:
<%= select_tag "store-id", options_from_collection_for_select(#stores, "id", "name"), include_blank: true, class:"store-id form-control" %>
<%= select_tag "status", options_for_select([ "Open", "On Hold", "Cancelled", "Closed", "Error" ]), include_blank: true, class:"form-control", multiple:true %>
This is my coffeescript to make jQuery submit the parameters to the server and reload the table onchange:
$ ->
$('#orders-table').DataTable
processing: true
serverSide: true
retrieve: true
pageLength: 50
title: 'orders'
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "All"]]
ajax: data: (d) ->
d.store_id = $('#store-id').val();
d.status = $('#status').val();
return
$ ->
$('#store-id').on 'change', ->
$('#orders-table').DataTable().ajax.reload()
return
$ ->
$('#status').on 'change', ->
$('#orders-table').DataTable().ajax.reload()
return
In your controller, make sure to pass the parameters along to Datatables like so:
respond_to do |format|
format.html
format.json { render json: OrderDatatable.new(view_context, { store_id: params[:store_id], status: params[:status] }) }
end
And then in your Datatable file, use the parameters to filter your results. In this case I am using a multi select for status, so when the blank value is selected, params[:status].present? results in true. That's why I added a check to see if the first item is an empty string.
def get_raw_records
# insert query here
query = Order.all
query = query.status(params[:status]) if params[:status].present? && (params[:status].count == 1 && params[:status][0] == "") == false
query = query.store(params[:store_id]) if params[:store_id].present?
query.joins(:store)
end
I ran into the same issue when implementing this. I found out that the issue was with this line:
column.search((if val then '^' + val + '$' else ''), true, false).draw()
where coffee script did not like the following bit:
, true, false
After removing it like so:
column.search(if val then '^' + val + '$' else '').draw()
everything worked fine. The caveat to this is, I am not a javascript/coffeescript guy, so what negative impact the result does is beyond me. But like you I am currently battling to get all results to appear in the selectable drop down filter. It only shows any unique values from the current page of data - which is not helpful.
FYI, to get pagination working on this, go to your datatable.rb file and uncomment the correct line toward the top that refers to the pagination you're using. I am using "will_paginate" for bootstrap, so mine looked like this:
include AjaxDatatablesRails::Extensions::WillPaginate
Hope that helps. By chance, did you find a way to show all results in the select filter?
My working code for an ajax datatable filter using the ajax-datatables-rails gem.
in the datatable view I created a table above the datatable to input the range variables, then add some javascript to reload the datatable on change:
<table>
<tbody><tr>
<td>Minimum CWD:</td>
<td><input type="text" id="minCWD" name="minCWD"></td>
</tr>
<tr>
<td>Maximum CWD:</td>
<td><input type="text" id="maxCWD" name="maxCWD"></td>
</tr>
</tbody></table>
<script>
$(document).ready(function () {
// other options
var table = $('#example').DataTable()
$("#minCWD").change(function () {
table.ajax.reload();
});
$("#maxCWD").change(function() {
table.ajax.reload();
});
});
</script>
then to add the filter variables to the ajax call (in the coffee file):
ajax: {
url: $('#example').data('source'),
beforeSend: (xhr) => xhr.setRequestHeader('Content-Type', 'application/json'),
data: (d) ->
$.extend {}, d, 'minCWD': $('#minCWD').val(),
$.extend {}, d, 'maxCWD': $('#maxCWD').val()
}
// note: the beforeSend may not be required
then add a filter in the model_datatable.rb:
def get_raw_records
#YOUR TYPICAL SELECTION...note: I'm using AREL and joining schools with pstats
#now filter by your max min variables
if params['minCWD'].present?
schools = schools.where(pstats[:cwd_percent].gteq(params['minCWD']))
end
if params['maxCWD'].present?
schools = schools.where(pstats[:cwd_percent].lteq(params['maxCWD']))
end
return schools
end
My controller looks like this:
respond_to do |format|
format.html
format.json { render json: ExampleDatatable.new(params, view_context: view_context) }
end
working example here: https://schoolsparrow.com/arizona/schools

How to add content via ajax using the popover boostrap

I tried to view different sources and also looked into the forums posting similar question, but it didnt quite help me with the issue that im facing.
I have a text input filed to which I'm adding a popover to show similar a list of names in the database. The inout field checks for validation, to see if the name entered is unique, if not it displays similar names available in the database that could be re-used.
here is the popover snippet:
$("#account_name_create").popover({
title: 'Twitter Bootstrap Popover',
content: function (process) {
this.accountCollection = new ipiadmin.collections.AccountCollection();
var newName = $("#new-account-form #account_name_create").val();
var userFilter = "accountName~'" + newName + "'";
this.accountCollection.fetch({
data: { "f": userFilter,
"sortby": null,
"type":"ipi",
"pageno":0,
"pagesize":2,
"reversesort" : true
},
cache: false,
success: function(model, response, options) {
var states = [];
map = {};
$.each(model.aDataSet, function (i, state) {
map[state.accountName] = state;
states.push(state.accountName);
});
process(states); //gives an error saying 'undefined is not a function (says process is undefined)'
},
error: function(model, response, options) {
console.log('error');
}
});
},
});
here is the html:
<input type="text" id="account_name_create" name="account_name" class="" size="40" />
I'm not sure how why it says 'process' as undefined. Also not sure if this would be the correct way of displaying the data in the popover.
Any ideas??
Thanks!
process doesn't have scope in the success function, only in the content function. If you want to call the process function from within the success function, you could define it somewhere outside of the jQuery call.

Categories