Assume a situation where I've a Rails AR models as below
class User
has_one :profile
end
class Profile
belongs_to user
has_one :address
end
class Address
belongs_to :profile
end
And have a User Profile view to be constructed at the client-side. Now, how should my Backbone model look like? Should it replicate the way it is in Rails/server-side domain model? Do we have a clear reason for the way it has to be that way, or is it just subjective?
Your experience sharing is appreciated!
Usually your backbone models and collections should follow your REST API ( or any other client-side <-> server-side communication ).
For example if these ruby models are passed to the frontend with :
GET /api/user/:id
And what you got as a response is
[{ profile: { address: "21st str." } },{ profile: { address: "17th str." } }]
You would need one model
User = Backbone.Model
Users = Backbone.Collection.extend({
model: User,
url: "/api/user"
});
But if you do something more complicated in your API and have more urls in your API you could choose the structure that best fits your interaction with the client on your frontend. For example if your website doesn't need a user api at all and you pass data to the frontend with those urls:
GET /api/profile
You can have only one model
ProfileModel = Backbone.Model.extend({
url: "/api/profile"
})
And you can easily set the address like
profile = new ProfileModel();
profile.set('address', '21st str.');
Bottom line
Backbone usually should follow your URL structure of your REST API. If you do this you will enjoy the full benefits of using it's REST Ajax calls ( GET, POST, DELETE, PUT ) properly autoconfigured.
Usually what I don't do is to make my Backbone app structure to follow the database schema. That may cause a headache, because you will need to create a way your backend ( Ruby app ) to be able to provide almost the same database access as the ORM that you are using.
To keep things simple, I believe the model should represent both the server-side model and the client-side view state, distinguishing the view state attributes by a preceding _. The view state attributes are ignored by the server when saving the model.
Here's an simplified example of the workflow I use:
var animal = new View({
initialize: function(){
// define the model and default view state when view is initialized
this.model = new Model({id:3, _edit:false}, {url:'/animals'));
}
, render: function(){
var self = this;
this.undelegateEvents();
this.delegateEvents({
'click [data-trgger]': function(e){
self[$(e.currentTarget).attr('data-trigger')].apply(this);
}
});
var success = function(){
// pass the model to the template engine
self.$el.html( _.template('#edit-animals', {model: self.model}) );
}
// fetch the model from the server when view is rendered
// you could check if the model is already fetched
this.model.fetch({success:success});
}
, onSave: function(){
// save the model then:
this.model.set('_edit', false);
this.render();
}
, onEdit: function(){
this.model.set('_edit', true);
this.render();
}
});
And the template:
<% if(model.get('_edit')){ %>
<!-- the view is in 'edit' state, show input and save button -->
<div>
<input type="text" name="breed" class="form-control">
</div>
<button class="btn btn-primary" data-trigger="onSave">Save</button>
<% }else{ %>
<!-- the view is in 'read-only' state, show values and edit button -->
<button class="btn btn-default" data-trigger="onEdit">Edit</button>
<div>
<%= model.get('breed') %>
</div>
<% } %>
Related
My Rails app has different organization with their profile page with their own inventory stats table.
Written below is the part of app/views/organization/show.html.erb
<div ng-app="cc-app" class="card-body overflow-hidden">
//codes here
<inventory-stats orgid="<%= #organization.id %>"> </inventory-stats>
//codes here
</div>
I have made a directive to show inventory stats and pass orgid i.e Organization ID with scope isolation(one-way binding) to get organization's inventory stats. As far as I know I have two ways of defining scope object for one-way binding in app/assets/javascripts/angular/directives/inventoryStats.directive.js which are:
Either
scope: {
orgid: '#'
}
Or,
scope: {
orgid: '<'
}
But only # seems to work. With #, $scope.orgid gives the values of Organization ID in directives scope. And if I pass orgid with < its undefined. I have read directive's scope documentation and still don't know why < is not working.
My AngularJS:
AngularJS v1.6.10
angular.module('app', [])
.directive('myDirective', function() {
return {
scope: {
param1: '#',
param2: '<'
},
template: '<div><pre>param1= {{param1|json}}</pre><pre>param2= {{param2|json}}</pre></div>'
};
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.10/angular.js"></script>
<div ng-app="app" ng-init="scopeValue=[1,2,3]">
<my-directive param1="string-value" param2="scopeValue"></my-directive>
</div>
# is used to pass interpolations as a string value only.
< is one way data binding from parent to child, you can pass what ever data value you want with it.
In an effort to learn about realtime apps, I am trying to create a basic messageboard using Entangled.
I have an Angular app and a Rails backend with a Messages Controller that includes Entangled::Controller. This Rails controller successfully receives a request when a form is submitted from the Angular app - the form is submitted using Entangled. (On clicking submit, a function is triggered in an Angular controller which should create a new message in the backend and update all clients subscribed to that backend.)
I know the Angular function is being triggered on clicking submit, and I know the function receives the correct information from the form: console.log($scope.message) displays {socket: "ws://message-board-olliehm.c9users.io:8080/messages", username: "ggg", content: "gggggg"} where I submit "ggg" in the username field and "gggggg" in the content field.
The problem is that these fields are not arriving at the Rails controller. When I click submit, the correct action is triggered in the Rails controller, but the params don't contain the right information: p params in def create returns {"controller"=>"messages", "action"=>"create"}, with no "message" hash and no "username" or "content" keys.
I cannot work out what Entangled is doing with the username and content fields.
Redis is new to me so I'm not sure if the problem is there. I have Redis installed and the redis-server is running as required by Entangled. I have a redis initializer as below, which in the Rails console is successfully connecting and letting me put data in the database:
$redis = Redis.new(:host => $IP, :port => 6379)
Here's my Angular controller:
var controllers = angular.module('controllers');
controllers.controller('MessageboardController', ['$scope','Message', function($scope,Message){
$scope.message = Message.new();
$scope.post = function() {
console.log($scope.message);
$scope.message.$save(function() {
$scope.$apply(function() {
$scope.message = Message.new();
});
});
};
Message.all(function(err, messages) {
$scope.$apply(function() {
$scope.messages = messages;
});
});
}]);
Message here refers to this factory:
messageboard.factory('Message', function(Entangled){
return new Entangled('ws://message-board-olliehm.c9users.io:8080/messages');
});
And here's my Angular view:
<h1>Messageboard</h1>
<section class='row' ng-if='messages'>
<ul>
<li ng-repeat='message in messages'>
{{message.username}}
{{message.content}}
</li>
</ul>
</section>
<section class='row'>
<form ng-submit="post()">
<div class='form-group'>
<label for='username'>Message as</label>
<input ng-model='message.username' name='username' type='text'>
</div>
<div class='form-group'>
<input ng-model='message.content' name='message' type='text' placeholder='Write your message here'>
</div>
<div class='form-group'>
<input type="submit">
</div>
</form>
</section>
Advice would be hugely appreciated - this has caused prolonged frustration and I'm very keen to get stuck into creating something with realtime updates.
After much trial and error I realised the empty params apparently received by the backend controller were misleading. The params arrive properly and a new message object is created and broadcast as long as I do the following to the raw params the controller receives:
params.require(:message).permit(:content,:username)
i.e. exactly what you'd do with the form params arriving as you'd expect. The weird thing for me was that params doesn't include a 'message' hash until you require that hash as above. I guess this is something to do with it not being a regular http request, though a proper explanation would be appreciated...
I am fairly new to js and angular but I was trying to get a Rails app working after watching Ryan Bates's railscast on Rails+AngularJS (http://railscasts.com/episodes/405-angularjs?autoplay=true).
I believe what I want to do is fairly simple: I have a Place model that has many Phones and I want to dynamically be able to add and save phones when creating a new Place. This type of nested form is fairly easy in Rails alone (with js only creating a new field element when a user adds a Phone), but I want to do this in Angular.
I suppose my question then breaks down into two:
1) What is the best practice of creating nested forms in Rails+AngularJS (or even forms in general)? Right now I am passing every model to Angular via JSON but is this the best way?
My second question refers to the following code.
I've attempted to do this and I am able to save a Place in angular:
places_controller.rb
respond_to :json
def create
respond_with Place.create(params[:place])
end
_form.html.erb
<div class="container-fluid" ng-controller="PlaceCtrl">
<form ng-submit="addPlace()">
<div class="row-fluid">
<div class="span2"><label>Name</label></div>
<div class="span8"><input type="text" class="input-xlarge" ng-model="newPlace.name"></div>
</div>
<input type="submit" class="btn" value="Add">
</form>
</div>
place.js.coffee
app = angular.module("test", ["ngResource"])
app.factory "Place", ($resource) ->
$resource("/places/:id", {id: "#id"}, {update: {method: "PUT"}})
app.factory "Phone", ($resource) ->
$resource("/phones/:id", {id: "#id"}, {update: {method: "PUT"}})
#PlaceCtrl = ($scope, Place, Phone) ->
$scope.addPlace = ->
place = Place.save($scope.newPlace)
console.log place
$scope.newPlace = {}
In place.js.coffee I am able to save Place to the DB in the line (place = Place.save($scope.newPlace)). I want the id of place so I can append it to all Phones that are dynamically built (because Phones has a FK of place_id that points to a Place). However if I type in place.id in console, I get an undefined message. place.name will return the place's name, but id won't work. If I look at console.log place, however, I see that the id field is returned and populated.
2) How do I get the id field of the place JSON object? I am almost positive this is possible since Chrome's console returns the id field but place.id won't give me anything.
Thank you in advance for your help. I greatly appreciate it.
I would try something like this:
$scope.addPlace = ->
place = Place.save($scope.newPlace) ->
$scope.newPlaceId = place.id
console.log place
$scope.newPlace = {}
Have: Using ASP.NET MVC 2, DataAnnotationsModel based server validation, and client validation with jQuery. Anything in my model is validated perfectly on the client with jQuery based validation (jQuery.validate and MicrosoftMvcJQueryValidation.js).
Need: Adding an additional HTML <input type="checkbox" id="terms" /> to my form. I need jQuery validation to require that this checkbox is checked AND somehow hook it in with whatever jQuery client script MVC is automagically controlling. Yes, I know it won't validate on the server side, but I don't need or want it to.
Seems like it should be simple but I'm new to MVC, a total beginner at jQuery, and my searches have been coming up blank.
Any help would be appreciated!
Here's a solution. It mimics what mvc does to hook into jQuery validation. So there's a checkbox called Accept that doesn't belong to the model. The script must go after the form and it adds all the validation meta data for that field.
<%
Html.EnableClientValidation(); %>
<% using(Html.BeginForm("Show"))
{ %>
<%= Html.EditorForModel() %>
<div class="editor-field">
<%= Html.CheckBox("Accept", new { #class = "required" })%>
<span class="field-validation-valid" id="Accept_validationMessage"></span>
</div>
<input type="submit" value="Submit" />
<%} %>
<script type="text/javascript">
window.mvcClientValidationMetadata[0].Fields.push({
FieldName: "Accept",
ReplaceValidationMessageContents: true,
ValidationMessageId: "Accept_validationMessage",
ValidationRules: [{ ErrorMessage: "The Accept field is required.", ValidationType: "required", ValidationParameters: {}}]
});
</script>
Might I suggest using a ViewModel for every View (put all of your dataannotations in there). Then you can create a boolean model property for your checkbox and set it to required.
From there, if you're posting the model back to the controller, you can simply use AutoMapper to map the ViewModel to the needed model, or simply map the properties yourself.
Either way, it is good practice to use a ViewModel for every view. Remember a ViewModel's job is to try and contain everything required in the view. This definitely means that it can and will have other data that is not required in the Model.
Try this
$(document).ready(function() {
//// Assuming your form's ID is 'form0'
$("#form0").submit(function() {
if ($("#terms").attr('checked')) {
return true;
}
else
{
//// Error message if any
return false;
}
});
});
I'm new to web development and i'm starting with a MVC project.
I have a view to create a new Service.
In this view, i need to have a button to show a dialog with client names (i also would like to implement filters and paging in this dialog).
Once the user selects a client from the dialog, i need to populate some combo boxes in the Service View with info relative to that particular client.
How can i accomplish this? If there any demo code or tutorial i can get my hands on to learn this?
Thanks in advance for any tip.
Whoa, that's a lot to answer in a single question.
I think you need to go through the NerdDinner sample first to get yourself familier with the MVC framework.
After that jQuery will be your friend. Essentially you can create a dialog with a jQuery call and use jQuery Ajax calls to your controller to get and filter data.
A good reference for jQuery is at jQuery.com
I recommend reading Pro ASP.NET MVC Framework By Steven Sanderson.
Phil Haack's, Steven Sanderson's and Stephen Walther's blog are also good resources.
(griegs i couldn't comment on your answer because the post is too long)
I'm using TailSpin Travel as bible for now.
I have a doubt that maybe you can clarify.
Edit View
(...)
<div id="clientSearch">
<%= Html.DropDownList("clientId", Model.Clients, Model.Clients)%>
<div class="resultsWrapper">
<div class="results">
<% Html.RenderPartial("clientDetails", Model); %>
</div>
</div>
</div>
(...)
Client Details partial View
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<EyePeak.ViewModel.Service.EditServiceViewModel>" %>
<% if(Model.SelectedClient != null) { %>
<tr>
<%Html.LabelFor(model => model.SelectedClient.Name);%>
<%= Html.DropDownList("clientAddresses", Model.SelectedClient.Addresses.Select(i => new SelectListItem { Value = i.Id.ToString(), Text = i.Name}))%>
</tr>
<% } %>
Controler:
(...)
public ActionResult New()
{
var service = new EyePeak.Data.Model.Service();
return View("Edit", this.GetEditViewModel(service));
}
(...)
public ActionResult SearchClientAddresses(string clientID)
{
var selectedClient = this._clientService.GetClient(Convert.ToInt32(clientID));
var model = new EditServiceViewModel
{
SelectedClient=selectedClient
};
return PartialView("clientDetails", model);
}
jQuery:
Sys.Application.add_load(
function()
{
$("#clientId").bind("change", showClientInfo);
}
);
function showClientInfo()
{
var id = $("#clientId").val();
$("#clientSearch .results table").fadeOut();
$("#clientSearch .results").slideUp("medium", function() {
$.ajax(
{
type: "GET",
url: "/Service/SearchClientAddresses",
data: "clientID=" + escape(id),
dataType: "html",
success: function(result) {
var dom = $(result);
$("#clientSearch .results").empty().append(dom).slideDown("medium");
}
});
});
}
My question is: Do i have to create a new EditServiceViewModel only with the Client information to pass it to the partial view? Can't i update my current ViewModel and pass it to the Partial view?
I'll need to create more partial views along the way in this particular view, so I'll need to create a viewmodel for each?
Maybe i didn't understood the concept well.
Thanks again for your help.