Getting Results in Ember Views - ruby-on-rails

So it has been a long couple of days to get all the Emberjs stuff to play nice together. I really like this journey, but from time to time am beginning to feel to old to do this.
So I have a app hits the /#/Records link. From their It needs to query Rails, return the results, and then grab the show view on the other pages.
When I had this as a single page app, with the views on the page, I had it working.... Now the last 2 days, confusion has crept in. (some extra bits in views and such are removed.
My hbs records/index view file which is partially showing up:
<table>
<tr>
<th>Name</th>
<tr>
<td colspan="3">{{counter}}</td>
</tr>
</tr>
{{#each record in controller}}
<tr>
<td>{{#linkTo "record" record}} {{record.fullName}} {{/linkTo}}</td>
</tr>
{{/each}}
</table>
My Ember App:
App = Ember.Application.create({
rootElement: '#ember'
});
App.RecordsController = Ember.ArrayController.extend({
});
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.RESTAdapter'
});
App.Record = DS.Model.extend({
firstName: DS.attr('string'),
middleName: DS.attr('string'),
surname: DS.attr('string'),
suffix: DS.attr('string'),
})
App.Router.map(function(){
this.resource('records');
this.resource('record', {path: 'records/:record_id'})
});
App.IndexRoute = Ember.Route.extend({
redirect: function(){
this.transitionTo('records')
}
});
App.RecordsRoute = Ember.Route.extend({
});

Modify your records route to look like the below. This model hook provides you a way to specify how a controller will get it's content / model. Now when you view the network tab you should see an ajax request to fetch all the records from your backend (via ember-data)
App.RecordsRoute = Ember.Route.extend({
model: function() {
return App.Record.find();
}
});

Related

How to Pass Data to a vuestrap Modal component

Im trying to pass some table data to its child vuestrap Modal component. The modal will be reused by all the Td's where the checkbox is calling the modal.
<div id="ordertbl" class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
...
</tr>
</thead>
<tbody>
<tr v-repeat="slides">
<td>#{ {NAME}}</td>
<td>#{ {MESSAGE}}</td>
<td> <input type="checkbox" v-on="click:showMod = true" v-model="published" > </td>
</tr>
</tbody>
</table>
<modal title="Modal" show="#{{#showMod}}" effect="fade" width="400">
<div class="modal-body">You will publish NAME, MESSAGE</div>
</modal>
</div>
when the checkbox is clicked. As you can see every row in the table has one checkbox so the data to be passed to the Modal will be unique to its row.
As Im new to Vue, besides Im trying to use Vuestrap to not reinvent things,
I dont know how to give that Data to the Modal when it pops.
new Vue({
el:'#ordertbl',
components: {
'modal':VueStrap.modal
},
data: {
showMod: false,
sortKey: '',
reverse:false,
slides: {
id: '',
name: '',
message: '',
published: ''
}
},
Basically I want to do the following
$('#exampleModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget) // Button that triggered the modal
var recipient = button.data('whatever') // Extract info from data-* attributes
// If necessary, you could initiate an AJAX request here (and then do the updating in a callback).
// Update the modal's content. We'll use jQuery here, but you could use a data binding library or other methods instead.
var modal = $(this)
modal.find('.modal-title').text('New message to ' + recipient)
modal.find('.modal-body input').val(recipient)
})
Pass the related data to the Modal, but with Vuestrap
You can use v-for="slide in slides". So every tr can get one of the slide object. Then you can pass it as a props to modal.
Extra ajax request is not required.
I manage to solve it with an ajax request depending on the Id clicked.

ng-table get data from rails api not working

I'm trying to make a simple table with pagination by using Angularjs ng-table.
I included the js and css files to my rails 3 layout. When page first loaded, it displays a table then fires a http get to rails controller to get json(it works fine until this point).
What I'm trying to do is add pagination to my table.
I received error "angular is not defined" and there is no request from http get to rails controller. Please show me what I'm doing wrong here.
Thank you
(function(){
var app = angular.module("apTags", ["ngTable"])
// call back after table finished ng-repeat
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
tableResponsiveAdjustHeight();
});
}
}
}
});
app.controller("apTagsController", [ "$http", function($scope, ngTableParams, $http){
// table has empty data when page first loaded
var apTags = this;
apTags.tags = [];
// Then it fires a http get to rails api to get data and append to table
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
total: data.length, // length of data
getData: function($defer, params) {
$http.get("ap_tags.json", {params: { api: true, page: params.page() } }).success(function(data){
apTags.tags = data;
$defer.resolve(data.slice((params.page() - 1) * params.count(), params.page() * params.count()));
});
}
});
}]);
})();
<div class="container" ng-app="apTags">
<div ng-controller="apTagsController as list">
<p><strong>Page:</strong> {{tableParams.page()}}</p>
<p><strong>Count per page:</strong> {{tableParams.count()}}</p>
<table ng-table="tableParams" id="table_tags" class="table table-condensed table-bordered table-hover">
<tr ng-repeat="tag in list.tags" on-finish-render="ngRepeatFinished">
<td data-title="'Tag'">{{tag.name}}</td>
<td data-title="'Share'">{{tag.share}}%</td>
<td data-title="'Direct Product Count'"></td>
</tr>
</table>
</div>
</div>

Ember local Variables in views

So in Ember if I have a model called "foos" - it will load a template with the data-template-name="foos", however I am not sure how to wipe "foos" from the screen when I load 1 "foo" as well as I am not sure how to can the instance variable in the controller such as #foos to do a #foos.length. for some other stuff I want to do.
Currently my view doesn't load if I call foos.isLoaded as well as if I call foo #4 It will appendTo the page the view. but not redraw the view.
I just don't know how to figure out what the default stuff looks like I guess.
My controller stuff-
Router
App.Router.map(function(){
this.resource('records', function(){
this.resource('record', {path: ':record_id'});
});
});
App.FoosRoute = Ember.Route.extend({
model: function() {
return App.Foo.find();
}
});
// Controller
App.FoosController = Ember.ArrayController.extend({
itemController: 'record'
});
App.FooController = Ember.ObjectController.extend({
fullName: function() {
return this.get('firstName') + ' ' + this.get('middleName') + ' ' + this.get('surname') + ' ' + this.get('suffix')
}.property('firstName', 'middleName', 'surname', 'suffix')
})
// Model
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.RESTAdapter'
});
App.Foo = DS.Model.extend({
firstName: DS.attr('string'),
middleName: DS.attr('string')
.....
})
My views:
<script type="text/x-handlebars" data-template-name="application">
{{ outlet }}
</script>
<script type="text/x-handlebars" data-template-name="foos">
<div class="one_half">
<h2>Search</h2>
form here....
</div>
<div class="one_half">
<div id="smallMap">
map
</div>
</div>
<div id="foos">
<table>
<tr>
<th>Name</th>
<th>Birth</th>
<th>Death</th>
</tr>
{{#each foo in controller}}
{{#if foo.isLoaded}}
<tr>
<td>{{#linkTo "foo" foo}} {{foo.fullName}} {{/linkTo}}</td>
<td>{{#linkTo "foo" foo}} {{foo.birthMonth}} {{#if foo.birthMonth}}/{{/if}}{{foo.birthDay}} {{#if foo.birthDay}}/{{/if}}{{foo.birthYear}} {{/linkTo}}</td>
<td>{{#linkTo "foo" foo}}{{foo.deathMonth}}{{#if foo.deathMonth}}/{{/if}}{{foo.deathDay}}{{#if foo.deathDay}}/{{/if}}{{foo.deathYear}}{{/linkTo}} {{foo.trAlt}}</td>
</tr>
{{else}}
<tr>
<td colspan="3" class="loading">Records are loading</td>
</tr>
{{/if}}
{{/each}}
</table>
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="foo">
<h3>A Record</h3>
{{id}}
{{firstName}}
</script>
Currently it doesn't kill the view and bring in the new ones.
Hmmm - there is a lot going on here, going to try and point you in the right direction.
So in Ember if I have a model called "foos" - it will load a template with the data-template-name="foos"
Not exactly. Convention is to use the same name, but the ember does not load a template based on the model. If anything it's the other way around. Your best bet is usually to start with templates then work your way backwards to the model layer. So in this case let's start with 3 templates - application, foo and foos:
<script type="text/x-handlebars" data-template-name="application">
<h1>Foo App</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="foos">
<h3>All Foos:</h3>
<ul>
{{#each controller}}<li>{{fullName}}</li>{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="foo">
<h3>Foo Details:</h3>
<p>{{fullName}}</p>
</script>
however I am not sure how to wipe "foos" from the screen when I load 1 "foo"
Ember will take care of rendering the appropriate view when the route changes. One way to make this possible is by adding links to your application. For example, modify foos template so that each record is a link, and add a Show All link to the detail page.
<script type="text/x-handlebars" data-template-name="foos">
<h3>All Foos:</h3>
<ul>
{{#each controller}}
<li>{{#linkTo "foo" this}}{{fullName}}{{/linkTo}}</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="foo">
<h3>Foo Details:</h3>
<p>{{fullName}}</p>
<p>{{#linkTo foos}}Show all{{/linkTo}}</p>
</script>
as well as I am not sure how to can the instance variable in the controller such as #foos to do a #foos.length for some other stuff I want to do
Not sure what your getting at. #foos is not an instance variable, unless you are using coffeescript and really mean this.foos.
Currently my view doesn't load if I call foos.isLoaded as well as if I call foo #4 It will appendTo the page the view. but not redraw the view.
Right. Calling foos.isLoaded just tells you if the model is loaded, it has nothing to do with the view. What do you mean call foo #4? Seems there might be code you are referencing that didn't get included in your SO post.
I just don't know how to figure out what the default stuff looks like I guess.
OK. I've made some guesses about what you're trying to do and created a working example. Code below, or see this working example on jsbin
First, I've added an application definition. Then changed your routes to be foos instead of records. Also no need for a nested route in this case.
App = Em.Application.create({});
App.Router.map(function(){
this.route('foos', {path: '/'});
this.resource('foo',{path: '/foos/:foo_id'});
});
FooRoute, FoosController and FooController were ok as-is.
App.FoosRoute = Ember.Route.extend({
model: function() {
return App.Foo.find();
}
});
// Controller
App.FoosController = Ember.ArrayController.extend({
itemController: 'foo'
});
App.FooController = Ember.ObjectController.extend({
fullName: function() {
return this.get('firstName') + ' ' + this.get('middleName') + ' ' + this.get('surname') + ' ' + this.get('suffix');
}.property('firstName', 'middleName', 'surname', 'suffix')
});
Added missing properties to App.Foo
App.Foo = DS.Model.extend({
firstName: DS.attr('string'),
middleName: DS.attr('string'),
surname: DS.attr('string'),
suffix: DS.attr('string')
});
Switching to DS.FixtureAdapter and added 4 fixture records to simulate what might be returned by your API
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.FixtureAdapter
});
App.Foo.FIXTURES = [
{id: 1, firstName: 'Michael', middleName: 'Paul'},
{id: 2, firstName: 'Jennifer', middleName: 'Lyn'},
{id: 3, firstName: 'Sophia', middleName: 'Marie'},
{id: 4, firstName: 'Greta', middleName: 'Fae'}
];

Using Knockout bindings in MVC ActionLink

I am attempting to utilise KnockoutJS and MVC 4 in order to display a table with ActionLink definitions in the first column of the table. Displaying the data itself is extremely straight-forward and I'm not having any problem there. The problem I have is in the generation of the ActionLink's.
I have taken a look at Use MVC helpers inside jquery.tmpl templates, but the solution there does not utilise knockout templates and inserting the Url into the model object is not feasible (the app domain model objects used to create the view model will be used extensively through out the application).
The table definition:
<table>
<tbody data-bind="template: { name: 'dmuTableDetail', foreach: tables() }"></tbody>
</table>
(tables is an observable array, hence the parens).
The knockout template definition:
<script id="dmuTableDetail" type="text/html">
<tr>
<td>#Html.ActionLink("Details", "Details", "DMUTableCategory", new { #Id = ??? } )</td>
<td data-bind="text:TableId"></td>
<td data-bind="text:TableName"></td>
</tr>
</script>​
The View Model definition:
var PageViewModel = function () {
self = this;
self.tables = ko.observableArray([]);
self.readItems = function () {
self.tables(jQuery.parseJSON('[{"TableId":1001, "TableName":"Table#1"},{"TableId":1002, "TableName":"Table#2"}]'));
}
}
$(document).ready(function () {
vm = new PageViewModel();
self.readItems('');
ko.applyBindings(vm);
});
(the actual code performs an Ajax call to retrieve the data, but the code above also demonstrates the issue).
Regardless of what I replace the ??? with, I am unable to get the value of the TableId field to be inserted into the href.
Any help would be greatly appreciated.
Thankyou.
Thankyou Eric, you got me thinking about an anchor element and binding the href attribute.
It seems the answer is a little easier than expected (it usually is!).
The table definition: (same as original question)
<table>
<tbody data-bind="template: { name: 'dmuTableDetail', foreach: tables() }"></tbody>
</table>
The knockout template definition: (change to the binding of the href attribute).
<script id="dmuTableDetail" type="text/html">
<tr>
<td><a data-bind="attr: { 'href': '#Url.Action("Details", new RouteValueDictionary() { { "Controller", "DMUTableCategory" } } )/' + TableId }">Details</a></td>
<td data-bind="text:TableId"></td>
<td data-bind="text:TableName"></td>
</tr>
</script>?
The View Model definition: (same as original question)
var PageViewModel = function () {
self = this;
self.tables = ko.observableArray([]);
self.readItems = function () {
self.tables(jQuery.parseJSON('[{"TableId":1001, "TableName":"Table#1"},{"TableId":1002, "TableName":"Table#2"}]'));
}
}
$(document).ready(function () {
vm = new PageViewModel();
self.readItems('');
ko.applyBindings(vm);
});
You dont actually need to RootValueDictionary but I've included it so people can see how to change the controller the request is sent to.
Knockout binds completely on the client side, which is after MVC has rendered the HTML for your page and sent it back to the original browser.
If you want your Knockout template to be able to use a URL that is generated on the server, then you'll have to employ some clever strategy similar to the following:
CSHTML:
#{
// create a dummy URL that you can use as a template
string hrefFormat = Url.Action("Details", "DMUTableCategory", new { id = "{ID}" });
}
<script type="javascript">
// a global string (but you can put it where ever you need to)
var _hrefFormat = #Html.Raw(hrefFormat)
<script>
JS:
self.readItems = function () {
self.tables(jQuery.parseJSON('[{"TableId":1001, "TableName":"Table#1"},{"TableId":1002, "TableName":"Table#2"}]'));
// loop through the 'tables' and add a new 'href' property to each for binding
ko.utils.arrayForEach(self.tables(), function(table){
table.href = _hrefFormat.replace("{ID}", table.TableId);
});
}
Your KO Tmpl where you bind the 'href' property of each table object to the a tag's href attribute:
<script id="dmuTableDetail" type="text/html">
<tr>
<td><a data-bind="attr: { 'href': href }">Details</a></td>
<td data-bind="text:TableId"></td>
<td data-bind="text:TableName"></td>
</tr>
</script>​

Post an array of complex objects with JSON, JQuery to ASP.NET MVC Controller

I know this issue has been touched on before, e.g. here:
How to post an array of complex objects with JSON, jQuery to ASP.NET MVC Controller?
But the solutions doesn't seem to fit my problem.
Here is my HTML. The number of rows are variable:
<table id="workPlanTable">
<tr>
<th>
Begin
</th>
<th>
End
</th>
</tr>
<tr itemId="1">
<td><input class="begin" id="begin_1" name="begin_1" type="text" value="5:30" /></td>
<td><input class="end" id="end_1" name="end_1" type="text" value="11:30" /></td>
</tr>
<tr itemId="3">
<td><input class="begin" id="begin_3" name="begin_3" type="text" value="5:30" /></td>
<td><input class="end" id="end_3" name="end_3" type="text" value="7:30" /></td>
</tr>
</table>
The JS builds an array of objects and posts them to a control method:
<script type="text/javascript">
$(function() {
submitForm = function() {
var items = new Array();
$("#workPlanTable tr").each(function(i) {
var end = $(this).find(".end").val();
var begin = $(this).find(".begin").val();
var item = {
"Begin": begin,
"End": end
};
items.push(item);
});
var postData = {
myItems: items
};
$.ajax({
url: '~/WorkPlan/AjaxUpdate',
type: 'POST',
dataType: 'json',
data: postData,
contentType: 'application/json; charset=utf-8',
success: function(result) {
alert(result.Result);
}
});
}
})
</script>
Each row represent a WorkPlanItem that.
My goal is to post them all to my controller to update them.
I can't seem to figure out how to access the array in my controller method (AjaxUpdate).
You can serialize the form as Vikas posted, or you could use a stringify function if you'd prefer to walk the page (as you are currently doing) and use the postData array.
On the controller, you'll need to handle the json string. You can use the System.Web.Script.Serialization.JavaScriptSerializer class to deserialize it. If you have an object that maps to the data you're passing, you can use the Deserialize method. If you don't, you can still use DeserializeObject, however that gives you a Dictionary<string, string> collection that you'll have to walk through to get your data. Not exactly fun (trust me), but it works.

Resources