Onclick/Javascript for div; Rails iterator - javascript

I have a directory page where the listings are rendered on the index page.
<% #listings.each do |listing| %>
# do some stuff
<% end %>
I've added a data-toggle to each listing - basically a button.
<a id="chat-menu-toggle" href="#dr" class="chat-menu-toggle" >
<div class="iconset top-chat-dark ">
<span class="badge badge-important hide" id="chat-message-count">1
</span>
</div>
</a>
This data-toggle opens a STATIC div. The div is a slider with content.
<div id="dr" class="chat-window-wrapper">
I want to use each button to pass the listing.id to a variable. Then, I can use that ID throughout the div.
For example:
Listing with ID:
Stack - 1
Overflow -2
Ruby - 3
Rails - 4
Onclick - 5
Let's say I click the button for "Ruby" which has an id of 3, I want the id to be passed around like so.
variable = 3
<div id= <%= variable %> class="chat-window-wrapper">

Not to sure what you mean by "use that ID throughout the div." but you could try this if it helps you?
<div data-id="<%=listing.id %>" class="chat-window-wrapper">
If you want to get the id's value from what ruby printed then you can try adding an event listener
$(".chat-window-wrapper").on("click",function() {
var x = parseInt($(this).attr('data-id');
}
To access listings outside iterator try
listings[index]
or
#allListings = listings. Then access #allListings[index]
but as far as my rusty ruby knowledge goes, i think listings should be accessible as long as the controller is passing it in the context to the view.

Related

Display an Array (from ViewModel) within Kendo View template

At work I'm just starting out with JavaScript, MVVM, and Kendo's JS framework, all at once, and I have a fairly simple problem.
I've created a View Model that allows Superheroes to be registered.
The JSBin I'm working in: http://jsbin.com/gewu/3/edit?html,js,output
Here's the HTML(view):
<div id="view">
Superhero: <input data-bind="value: name" /><br/>
Superpower: <input data-bind="value:power"type="text">
<label for="">from Earth?<input type="checkbox" data-bind="checked:fromEarth"></label>
<button data-bind="click: registerHero" >Display User Info</button>
<div id="array-display"></div>
<p>Entries: <span data-bind="text: knownHeroes.length"></span></p>
</div>
And here's the JS (viewModel):
var viewModel = kendo.observable({
knownHeroes : [],
name: "Hulk",
power:"Stength",
fromEarth: true,
registerHero: function() {
var name = this.get("name");
var power = this.get("power");
var fromEarth = this.get("fromEarth");
this.knownHeroes.push({"name":name,"power":power,"fromEarth":fromEarth});
}
});
kendo.bind($("#view"), viewModel);
Now, I'm trying to get the View to loop through and display the array of knownHeroes. But it won't render anything. I know the data is being pushed to the array, because I can see the array.length increasing, and I can look up specific values in the array. I'm assuming the problem has to do with how I'm referencing the array in the view. But I'm not sure. Here's the template I've written:
HTML:
<script id="registry-view" type="text/x-kendo-template">
<ul>
# for (var i=0; i < knownHeroes.length; i++) { #
<li>
<ul>
<li>#= knownHeroes[i].name #</li>
<li>#= knownHeroes[i].power #</li>
<li>#= knownHeroes[i].fromEarth #</li>
</ul>
</li>
# } #
</ul>
</script>
<script type="text/javascript">
var template = kendo.template($("#registry-view").html());
$("#array-display").html(template); //Append the result
</script>
You have got some mistakes.
First of all you got script wrote in html portion of this jsbin as well as in javascript section. Html part executes first so the viewModel isn't defined yet (check console for errors)
Also the object you pass to the template is stored always in "data" variable.
Last mistake is when using your desing, anytime you add any new data row, whole template needs to be reloaded (including all previously added data rows)
I corrected some of your mistakes in following jsbin: http://jsbin.com/jomemuko/1/edit (actually you need to hit the Run with JS button to make it work - some script loading issue I don't have time for)
Ideally you should use a listView widget and assign it a template for only one item. Also in your viewModel you should create a kendo dataSource and pass it as an option to newly created listView. Then in the viewModel you should refine your registerHero function to make it add the hero to the dataSource. Widget should automatically refresh.
Hope it helps

How to render different UJS actions based on the click event in Rails 4 with jQuery

I have a div and need to render different .js.erb actions based on the clickevent on that div.
For example:
on dblClick, I want to render the new.js.erb
on click, I want to render show.js.erb
In my asset/javascripts directory I have a file.js.coffee:
$('#myDiv').on "dblclick" ->
$.getScript('/projects/xy/plan/new')
This basically works. It shows the modal in my new.js.erb:
$("#myModal").modal("show");
How can I make the $.getScript URL dynamic and include an object for the show and edit action?
Edit:
Rich Pecks answer seems to be correct.
Because of simplicity I just wrote $('#myDiv').on "dblclick".
The full example is:
$zoomcontainer = $("section").find(".parent")
$panzoom = $zoomcontainer.find(".panzoom").panzoom(
$zoomIn: $zoomcontainer.find(".zoom-in")
$zoomOut: $zoomcontainer.find(".zoom-out")
$zoomRange: $zoomcontainer.find(".zoom-range")
$reset: $zoomcontainer.find(".reset")
).on("dblclick", (e) ->
How should I put my data attributes in the HTML?
This is my basic HTML structure:
<section>
<div class="parent" data-project_id="<%= #project.id %>" data-plan_id="<%= #plan.id %>">
<input type="range" class="zoom-range" />
<div id="plan" class="panzoom" style="background: url('<%= #plan.asset.url %>') no-repeat;>
</div>
</div>
</section>
From what I can see, you could use a different event handler:
var plan_url = "/projects/xy/plan";
$('#myDiv').on "dblclick" ->
$.getScript(plan_url + '/new')
$('#myDiv').on "click" ->
$.getScript(plan_url + '/show')
Although this seems too simple to be what you're asking for
Dynamic
If you're looking to handle dynamic routes, you'll need to be able to pass the various object variables through your HTML (JS can't read Rails variables)
You'd have to do something like this:
#app/views/posts/show.html.erb
<%= content_tag :div, class: "your_class", data: {project_id: #post.project_id, plan_id: #post.plan_id} do -%>
Your div
<% end -%>
#-> <div class="your_class" data-project_id="15" data-plan_id="6">Your Div</div>
This will allow you to do this:
#app/assets/javascripts/application.js
$('#myDiv').on "dblclick" ->
project = $(this).data("project_id")
plan = $(this).data("plan_id")
$.getScript('/projects/' + project + '/plan/' + plan)

Setting CSS classes with current_user

I'm having trouble arriving at an approach to this problem. tl;dr I want to be able to set the css class of an li to the name of the current_user that posted the chat. Then based on that class, I'll apply different styling.
This is for a chat window and I want all the current users messages to display on one side and I want all other messages to display on the other side with a different color, etc.
I have a file.js.coffee that basically reads in some user input and apends it to an ordered list and adds some elements and classes along the way
projectRef.on "child_added", (snapshot) ->
message = snapshot.val()
$("<li class='self'>").append($("<div class='message'>").append($("<p>").text(message.text))).prepend($("<b/>").text(message.name + ": ")).appendTo $("#messagesDiv")
$("#messagesDiv")[0].scrollTop = $("#messagesDiv")[0].scrollHeight
$("#messageInput").keypress (e) ->
if e.keyCode is 13
text = $("#messageInput").val()
projectRef.push
name: userName
text: text
$("#messageInput").val ""
The above would yield something like this in the browser
<li class="self">
<b>User : </b>
<div class="message">
<p>My chatt message from file.js.coffee!!</p>
</div>
</li>
That 'self' class in the li is what I have been trying to dynamically set based on the current_user. So I have 2 issues - 1. I'm trying to figure out who posted the li and 2. I'm trying to dynamically set the class of that li based on the user that chatted/posted it.
My thinking was something along the lines of in the file.js.coffee use JQuery to grab that li and add the <%= current_user.name %> as a class then I could have a file.js.erb where I would do something like
<% unless $('li').hasClass('<%= current_user.name %>'); %>
<%= $('li').toggleClass('others') %>
<% end %>
This way it checks if the class of the target li is from the current user and if it is keep the css class as self if not toggle it to others. Then I could style the classes appropriately (left, right, background-color:blue;, etc).
Is there a more correct way to approach this problem given what I am trying to accomplish? I think so..
It seems like you are saying you're trying to assign a class as the current user's name.
I'm wondering if going that far is necessary.
Assigning the list element with a class named "current_user" might be enough, then have separate CSS to control anything with class named "current_user".
Here's an example fiddle.
CSS
li {
list-style:none;
clear:both;
float:right;
text-align:right;
background-color:#A7A2A0;
border:1px solid #EEE;
width:200px;
margin:10px;
padding:5px;
}
li.current_user {
float:left;
text-align:left;
background-color:#24887F;
}
HTML
<li class="current_user">
<b>Current User:</b>
<div class="message">
<p>My message!</p>
</div>
</li>
<li>
<b>All Other Users:</b>
<div class="message">
<p>Other person's smessage.</p>
</div>
</li>
UPDATE:
Looking at the firebase example I altered it to add a class "current_user" if the username matched the the name of the user that wrote the message. Below is the part of the code that I altered in the "displayChatMessage" function, I also added CSS to the head section for the "current_user" class.
Here's a link to the working example, view it in to different web browsers using different usernames at the same time to see.
function displayChatMessage(name, text) {
if(name == $('#nameInput').val()){
$('<div/>').text(text).prepend($('<em/>').text(name+': ')).appendTo($('#messagesDiv')).addClass('current_user');
$('#messagesDiv')[0].scrollTop = $('#messagesDiv')[0].scrollHeight;
}
else{
$('<div/>').text(text).prepend($('<em/>').text(name+': ')).appendTo($('#messagesDiv'));
$('#messagesDiv')[0].scrollTop = $('#messagesDiv')[0].scrollHeight;
}
};
This is the CSS I added to the head.
<style type="text/css">
.current_user {
background-color:#24887F;
}
</style>
There are many users using the app concurrently. But for every user, there is only one current_user in session. Don't mix it up.
Since the message is to be processed by server, you can easily add a new attribute of message.klass in server side by judging the message comes from current_user. If it is, the class may be current, else blank or others.
Then, in JS
$('li').addClass('<%= message.klass %>')

Different view-options in Rails

What is the best practise for creating a view page in Rails with different view-options, such as a basic view with mostly text and an advanced view with graphical features? More specifically, I would like to have a view page where the the user can toggle between a basic/advanced show view. Clicking the toggle button renders one set of divs corresponding to selected view. Should this be done though a form_for/if-else statement in html markup or is it better do to do it in javascript? I guess turning the <div id="id"> on/off could be done in javascript through:
$("#id").show()
$("#id").hide()
I have a problem understanding how a rails implementation is done, where do I put the if-else statement (i.e. if user basic view is toggled render <div id="basic">, else <div id="advanced">)?
<%= form_for ??? do |f| %>
...
<%= f.submit ??? %>
<% end %>
Edit 2:
<div class="row">
<div class="span6">
<div class="btn-group" id="basic-advanced" data-toggle="buttons-radio">
<a class="btn btn-small" href="#" id="basicbutton">Basic</a>
<a class="btn btn-small" href="#" id="advancedbutton">Advanced</a>
</div>
</div>
</div>
<div class="container" id="basic">
This is the basic view
</div>
<div class="container" id="advanced">
This is the advanced view
</div>
Now in javascript I have the following:
$("#basic").toggle();
$("#basic-advanced").click(function (){
$("#basic").toggle();
$("#advanced").toggle();
});
I have added the above code, but how do I keep track of which viewing mode that the page is in? From the answers it seems like one could set an instance variable, #viewing_mode, to a value corresponding to the mode, but how should this be done? Through a form?
Update: I managed to achieve a toggle using a session variable, and an if/else statement, the procedure is described here: Session variable not persisting after switch.
If you are using jQuery.show() and jQuery.hide() that means you need both the basic and advanced div tags rendered. Rails needs to render them both. You can just have toggle display:hidden css, here I did that with variable #use_advanced.
<div id="advanced" <%= (#use_advanced) ? '' : ' style="display:hidden"' %>>...
<div id="basic" <%= (#use_advanced) ? ' style="display:hidden"' : '' %>>...
To toggle between the two you could have a button: <button>toggle</button>
$('button').click(function () {
$('#advanced').toggle();
$('#basic').toggle();
});
There are some different cases:
1) if you are able to change your view using CSS only - then you should enclose all the page inside classified div:
<div class='my-style'>
...
</div>
2) if first solution is not applicable then you may use if-else statement:
<% if #my_style_selected %>
<div>My style</div>
<% else %>
<div>Default</div>
<% end %>
3) finally, if neither solution suits you. You may write some JS to dynamically replace your divs. The best solution is to replace some parts of the page using AJAX. If you will render both variants and hide one of them - that will almost double your page load time.
Given that yiou are using rails I would look into using layout for different views rather than having conditional logic.
For more info see rails 3, how add a view that does not use same layout as rest of app?
Here's a JavaScript object (written in CoffeScript) that will allow you to switch between as many modes as you like using a convention:
class ModeSwitcher
constructor: ($area, modes) ->
$area = $ $area
me = this
bindToggle = (mode) ->
$area.find('a.toggle.' + mode).click ->
me.toggle mode
$ ->
bindToggle(mode) for mode in modes
toggle: (mode) ->
$('.mode').hide()
$('.' + mode + '.mode').show()
Here's an example of how you could construct the object (JavaScript/jQuery):
var switcher = new ModeSwitcher('body', ['basic', 'advanced']);
$(function() {
switcher.switch("<%= #mode %>");
});
The #mode instance variable is set in the Rails controller and says which mode the view should start out in:
#mode = params[:mode] || 'basic'
As for how it works by convention, you would have sections like this:
<div class="basic mode">...</div>
<div class="advanced mode">...</div>
And you would have links like this:
<a class="toggle advanced">Switch to advanced mode</a>
<a class="toggle basic">Switch to basic mode</a>
When constructed, the object will iterate through all the declared modes and look for the toggle links. It will then bind the toggling function to those links.

jQuery Mobile JSON parsing

I'm trying to make a cross platform mobile application using jQuery Mobile.
I have a JSON string that displays information about a specific object (check it out here: http://app.calvaryccm.com/mobile/web/teachings/json?callback=?) and I want it to turn it into a list view that connects to a single item view. The problem is, the listview isn't displaying like a list at all. It is almost like HTML without CSS. Check it out here: http://mbeta.calvaryccm.com/#teachings
This is the Javascript for parsing the JSON string:
<!-- Getting Teaching Data -->
<script type="text/javascript">
$(document).ready(function () {
$.getJSON("http://app.calvaryccm.com/mobile/web/teachings/json?callback=?",
function (data) {
//remove any characters from the query that might be unsafe to use as an ID for a page
//data.pageId = data.MessageNumber.replace(/[^\w]/, "");
//Feed the data to the template and add the new page to the body.
var res = $("#teachingTemplate").tmpl(data); //.appendTo(document.body);
$("#teachings").append(res);
//Grab a reference to that shiny new page
//var newpage = $("#" + data.pageId);
});
});
//Makes date readable
function GetDate(jsonDate) {
var value = new Date(parseInt(jsonDate.substr(6)));
return value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear();
}
This is supposed to be the list view:
<script id="teachingTemplate" type="text/x-jquery-tmpl">
<div id="${MessageNumber}">
<div data-role="header">
<a data-icon="arrow-l" href="#" data-rel="back">Back</a>
<h1>${Title}</h1>
</div>
<div data-role="content">
<div class="teachingsForm">
<ul data-role="listview">
<li><a href="singleTeachingView" class="tableImage">
<img src="" alt=""/>
<h3>${Title}</h3>
<p>${Speaker} - ${GetDate(MessageDate)} - ${MessageNumber} {{if Book != null}} - ${Book.BookName} ${ChapterVerse}{{/if}}</p>
</a>
</li>
</ul>
</div>
</div>
<div data-role="footer">
<h4>2011 Calvary Chapel Melbourne</h4>
</div>
I cannot figure out why my listview isn't displaying right. If you want to see it in action look here: http://mbeta.calvaryccm.com/#teachings . I need help getting my listview to display right and direct to the right page.
It looks exactly like what your template says it should look like. Note that each of your <ul> elements only has one <li> element in it.
Your template starts off with an outer <div>, and then there's a "header" <div> with the <h1> title. Then there's the "content" <div>, and ultimately the <ul>, and finally the "footer". That's what your result page looks like. If you want to do some sort of iteration, well, you'll have to explicitly do that in your template, because otherwise the template code will assume you just want it to re-apply the template to each object in the array you pass it.
There's nothing wrong with the "JSON parsing". Note that in your JSON, there's no need to quote "/" characters with "\" and in fact (though it doesn't matter for JSONP) it's not valid JSON.
edit — OK so now that I've pulled my head out from wherever it was I think I see what you're doing. The problem may be that you just need to call
$.mobile.changePage();
at the end of your JSONP callback function. However I note that your "$.mobile" doesn't have a "changePage()" function ... I don't know what that means. You're using a pretty old version of jQuery too.

Categories