I am using Firebase in a pretty simple chat application. One of the features i'm working on now is the ability to update messages in the chat room, but only if you were the original author. For the editing i have an icon that, when clicked, will do some javascript to hide the message div and display a previously hidden div that has the update form in it. But i only want this to be available if you were the author of the message. Here's what the code looks like:
<li class="message phm pvm pull-left" ng-repeat="(msgId,msg) in room.messages">
<div class="message-current" data-message-id="{{msgId}}">
<span class="message-author pull-left">{{msg.author}}</span>
<span class="message-body pull-left pls">{{msg.body}}</span>
<span class="message-timestamp pull-right prs">
{{msg.timestamp | date:'MMM d, yyyy h:mm a'}}
<a class="message-update-link" href="#" ng-click="showMessageUpdate(msgId)"><span class="glyphicon glyphicon-pencil"></span></a>
</span>
</div>
<div class="message-update hidden" data-message-id="{{msgId}}">
<form class="form-inline" ng-submit="updateMessage(roomId, msgId, '<%= current_user.full_name %>')">
<textarea class="form-control update-message" ng-model="msg.body" ng-trim="false">{{msg.body}}</textarea>
<input class="button button-large button-primary" type="submit" value="Update">
</form>
</div>
</li>
Right now, i can only get it to have updatable message for ALL message or no messages. Basically, the part i am stuck on is the check of whether or not the angularjs value of {{msg.author}} is equal to the rails value of <%= current_user.id %>. Any thoughts on how this can be achieved? Thanks for any help!
SOLVED:
Thanks to Jiten for the step in the right direction. I'm new to learning AngularJS and it's pretty complex. Sometimes you just need to know where to start.
I ended up using a combination of the ngIf directive and the angular.equals function. In my rails view i used ng-if="isAuthor(msg.author, '<%= current_user.full_name %>')" on anything i wanted available to authors of that message only. This will evaluate the response of isAuthor() and only render to the dom if it evaluates to true. The code above now looks like this:
<li class="message phm pvm pull-left" ng-repeat="(msgId,msg) in room.messages">
<div class="message-current" data-message-id="{{msgId}}">
<span class="message-author pull-left">{{msg.author}}</span>
<span class="message-body pull-left pls">{{msg.body}}</span>
<span class="message-timestamp pull-right prs">
{{msg.timestamp | date:'MMM d, yyyy h:mm a'}}
<a ng-if="isAuthor(msg.author, '<%= current_user.full_name %>')" class="message-update-link" href="#" ng-click="showMessageUpdate(msgId)"><span class="glyphicon glyphicon-pencil"></span></a>
</span>
</div>
<div ng-if="isAuthor(msg.author, '<%= current_user.full_name %>')" class="message-update hidden" data-message-id="{{msgId}}">
<form class="form-inline" ng-submit="updateMessage(roomId, msgId, '<%= current_user.full_name %>')">
<textarea class="form-control update-message" ng-model="msg.body" ng-trim="false">{{msg.body}}</textarea>
<input class="button button-large button-primary" type="submit" value="Update">
</form>
</div>
</li>
Here is what isAuthor() looks like in my AngularJS controller:
$scope.isAuthor = function(msgAuthor, currentUser) {
return angular.equals(msgAuthor, currentUser);
}
Pretty simple really!
To compare two objects you can use:
angular.equals({{msg.authorId}}, <%= current_user.full_name %>)
Related
I am learning rails online and have got as far as creating a simple site with some products in a products table with fields:
id
name
price
description
I have gone to the getbootstrap site and started pinching the code from this example -
https://getbootstrap.com/docs/4.0/examples/jumbotron/
This page has a nice search box in the navbar at the top right. I now have it in my simple site and the container HTML currently looks like this:
<div class="container" >
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<div class="container">
<a class="navbar-brand" href="#">My Shop</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="/">Products</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</div>
</nav>
</div>
My problem is that all of the tutorials I see are creating simple forms using ERB to implement some sort of search functionality. like so:
<% form_tag projects_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
While I want to utilise this nicely rendered HTML form instead.
I don't know how to capture the search term and pass it to a method from my form. The ERB example uses the params hash to store the search term.
What I would like to implement is -
1 - Capture the name of a product entered in the search box
2 - Pass that string to a method in my model that queries the DB for first matching name
3 - gets the record id and uses it to redirect to the Show page with the id to display the item
I have found that setting the form action to "/" causes a click on the submit button to jump to the index method in my controller. From there I am sure I could utilise the model to query the underlying DB but I cannot seem to pass across the search term.
I do have a params hash at that point but it just seems to have a "controller" and an "action" member. Is there an attribute I can set that would add the value to this hash?
Thanks in advance
Brad
You must set an action to the form and a name to the field
<form class="form-inline my-2 my-lg-0" action="/search" method="get">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search", name: "search>
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
Then define the method in a controller.
StaticPagesController
def search
#search_term = params[:search]
end
And define your route in routes.rb
match '/search', to: 'static_pages#search', via: 'get'
OP Update
I've figured out the problem and this issue is now closed. Would one day like to find the time to post the solution as an answer here.
I'm having a bit of an issue working with ngMessages inside an ng-form on an Angular Bootstrap tab. Further complicating things, the thing I am validating is a Angular Bootstrap datepicker. So the code is as follows, and as you can see, I've got a datepicker on an ng-form which is on a Tab.
<uib-tab index="5" heading="{{tabs[5].title}}" select="changeCheck($event)">
<ng-form name="{{tabs[5].form}}">
<div class="col-md-9">
...
<div class="col-md-6">
<h4>Add Followup</h4>
<div class="form-group">
<label for="addFollowupDate" class="control-label">Date:</label>
<div class="input-group">
<input type="text" id="addFollowupDate"
class="form-control"
uib-datepicker-popup="{{format}}"
ng-required="true"
ng-model="newFollowup.date"
is-open="addFollowupPopup.opened"
datepicker-options="dateOptions"
close-text="Close"
alt-input-formats="altInputFormats" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="openAddFollowupPopup()"><i class="fa fa-calendar"></i></button>
</span>
<div class="help-block" ng-messages="followupsForm.newFollowup.date.$error" ng-if="followupsForm.newFollowup.date.$invalid && followupsForm.newFollowup.date.$touched">
<div ng-message="required">This field is required</div>
</div>
</div>
</div>
I've assigned that form to the scope using the following line:
$scope.followupsForm = $('ng-form[name="followupsForm"]').data('$formController');
And I have successfully set the control to touched on a submit which does make the border go red:
if ($scope.followupsForm.$invalid) {
angular.forEach($scope.followupsForm.$error, function (formErrorField) {
angular.forEach(formErrorField, function (errorField) {
errorField.$setTouched(); // by setting to touched, the relevant message will display if the field is invalid.
});
});
}
But the ng-message for required does not display.
Any ideas why? Could be a tough one with nested scopes etc. (of the tab and the picker).
I am trying to create multiple divs on the fly using a template which is called for a number of times and each time i create a new div ... which seems to be working fine below is the code. I am passing a map tempMap from the controller to the gsp page which is of the below format
tempMap = [key_1:v1,key_2:v2,key_3:v3] //from the controller
//this is the gsp part
<g:set var="counter" value="${1}" />
<g:while test="${counter <= tempMap.counter}">
<g:render template="travelDetailsToShow" />
<g:set var="counter" value="${counter + 1}" />
</g:while>
I need to set the ids of the elements based on the counter which also is working ... But i am unable to set the values of the fields.
<div class="col-sm-6" id="key_${counter}"> // This sets the id to key_1, key_2 depending on the counter value
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
<g:set var="temp" value="kep_${counter}" />
**<input type="text" name="key_${counter}" class="form-control" value="${tempMap?.key_${counter}}" readonly></input> // this does not work**
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
I searched a lot but with no luck ... can anyone tell me where am i going wrong with this .. Any help is appreciated ... Thanks in advance people
The render tag has a model attribute. It works like the model passed from controller to view. You can use it to pass data into the template.
view
<g:each in="${1..tempMap.counter}" var="counter">
<g:set var="key" value="key_${counter}">
<g:render template="travelDetailsToShow" model="${[id: key, value: tempMap[key]]}"/>
</g:each>
template
<div class="col-sm-6" id="${id}">
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
<input type="text" name="${id}" class="form-control" value="${value}" readonly></input>
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
Thanks Emmanuel for your answer ... but i think i wasnt clear enough and misled you to think that the "key" would be static ... As your logic would expect the static value "key" ..... The proper structure of the map is below ...
tempMap = [key_1:v1,anotherkey_1:v2,yetanotherkey_1:v3,key_2:v4,anotherkey_2:v5,yetanotherkey_2:v6]
I havent tried out the solution posted by you but got another solution to work.... code for which is below ...
<div class="col-sm-6" id="key_${counter}">
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
<input type="text" name="key_${counter}" class="form-control" value='${tempMap?."${'key_' + counter}"}' readonly></input>
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
<div class="col-sm-6" id="anotherkey_${counter}">
<div class="form-group">
<label>Return Date</label>
<span class="input-icon-right input-group">
<input type="text" name="anotherkey_${counter}" class="form-control" value='${tempMap?."${'another_' + counter}"}' readonly></input>
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
</div>
The problem was that i was not referencing the value in the Map properly... The code that fails is stated below.
<div class="col-sm-6" id="departureDate_${counter}">
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
**<input type="text" name="departureDate_${counter}" class="form-control" value="${tempMap?.${departureDate_counter}"} readonly></input>** // THIS CODE DOES NOT WORK. This will look for a key ${departureDate_counter} and will through a gsp error that tags have not been closed
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
Although i have found a solution to my problem it would be nice if we posted more solutions as i feel that not a lot of solutions are present on the net for grails and gsp ...
Hi I encountered a problem with ng-model. I want to create an edit page for admin user to edit the rest of the user's permission level.
So, I listed out all the user's attributes in the edit page, and these attributes should display the current values.
I can update all the attributes correctly, however, the problem is that when visiting the edit page, only certain attributes are displayed while some are not. I think it is the problem with ng-model.
code snippets from my _form.html.erb
<div class="col-md-12 space-1" ng-class="{ 'has-error' : form['email'].$invalid }">
<label class="control-label">Email</label>
<div class="input-icon right">
<i ng-show="form['email'].$invalid" class="fa fa-warning tooltips" data-original-title="{{errors['email']}}" data-container="body"></i>
<input ng-model= "user.email" name="email" type="text" class="form-control">
</div>
</div>
<br>
<div class="col-md-12 space-1" ng-class="{ 'has-error' : form['events'].$invalid }">
<label class="control-label">Event Permission Level : {{user.activities.events}}</label>
<div class="input-icon right">
<i ng-show="form['events'].$invalid" class="fa fa-warning tooltips" data-original-title="{{errors['events']}}" data-container="body"></i>
<input ng-model= "user.activities.events" name="activities.events" type="range" max="2" min="0" class="form-control">
</div>
</div>
<br>
<div class="col-md-12 space-1" ng-class="{ 'has-error' : form['admin_events'].$invalid }">
<label class="control-label">Admin Event Permission Level : {{user.activities.admin_events}}</label>
<div class="input-icon right">
<i ng-show="form['admin_events'].$invalid" class="fa fa-warning tooltips" data-original-title="{{errors['admin_events']}}" data-container="body"></i>
<input ng-model= "user.activities.admin_events" name="activities.admin_events" type="range" max="2" min="0" class="form-control">
</div>
</div>
<br>
The code above shows 3 different ng-models, 1.user.email, 2.user.activities.events 3.user.activities.admin_events
The way I process them is the same, but when visiting the edit page, only email and events attributes displayed the current value, but not admin_events. I can update their values alright, but I cannot get admin_events to display its current value when visiting the edit page. This is weird isn't it. I mean if I can see the value of events, why can't I see the value of admin_event since they all belong to the user variable.
Can anyone explain why is it like this? It is very confusing for me. Thank you very much.
As it solved the problem, I quote myself from the comments :
If only user.activities.admin_events is not working, it may be related to the _ notation. Try camel case user.activities.adminEvents or user.activities['admin_events'].
I am using token inputs in a text field and would like to also make these tokens sortable (JQuery UI). I am using Ryan Bates screencast to get started however I am unsure how to apply content_for to my code. Here is what I have...
<fieldset id="presenters">
<p>
<%= event_form.label :presenter_tokens, "Presenters" %>
<%= event_form.text_field :presenter_tokens, "data-pre" => #event.presenter_tokens_tokeninput.to_json %>
</p>
</fieldset>
Here is the HTML I get...
<fieldset id="presenters">
<p>
<label for="event_presenter_tokens">Presenters</label>
<ul class="token-input-list-facebook">
<li class="token-input-token-facebook">
<p>Mark</p>
<span class="token-input-delete-token-facebook">×</span>
</li>
<li class="token-input-token-facebook">
<p>Laurie</p>
<span class="token-input-delete-token-facebook">×</span>
</li>
<li class="token-input-input-token-facebook">
</ul>
<input data-pre="[{"id":131,"name":"Mark"},{"id":1311,"name":"Laurie"}]" id="event_presenter_tokens" name="event[presenter_tokens]" size="30" type="text" value="131,1311" style="display: none;"/>
I ended up solving this problem by using https://github.com/swanandp/acts_as_list