Rails or Ember object model breaking browser's pushState functionality? - ember.js

** I'm using Ember.Object instead of Ember Data, to pull data from an api and I believe that might be causing the issue.**
I have my resources nested as so:
Mdm.Router.map ->
#resource "groups", ->
#resource "group", path: ':group_id'
'/groups/ loads a list of all groups on the left side of the browser. Each group is linking to its specific group_id. When clicked, the 'group' template renders on the right side of the screen, showing details of one group while the list remains to the left.
However, when you click the back button, or manually enter the group_id into the url the individual groups dont render. The url will update in the browser window, but the content wont change to match it.
I have the singular 'group' template rendering inside of the 'groups' template with the {{outlet}}.
My groups_route.js.coffee looks like this:
Mdm.GroupsRoute = Ember.Route.extend(model: ->
Mdm.Group.all()
)
application.hbs:
<div class="container">
<div class="nav-bar">
<img src="assets/logo_loginbox.png" class="logo">
<ul class="nav-menu">
<li>GROUPS</li>
<li>USERS</li>
</ul>
</div><!-- nav-bar -->
<hr>
{{outlet}}
</div><!-- container -->
groups.hbs:
<h1>Groups</h1>
{{ partial groupsList }}
<div class="group">
{{outlet}}
</div><!-- group -->
group.hbs:
<h1>{{name}}</h1>
I'm getting the following error in the console when I use the back button or try to load the page with the group_id present:
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this);
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
delete m.proto;
finishChains(this);
this.init.apply(this, arguments);
} has no method 'find' application.js:51233
Mdm.GroupRoute.Ember.Route.extend.model application.js:51233
superWrapper application.js:12849
Ember.Route.Ember.Object.extend.deserialize application.js:36503
collectObjects application.js:35614
proceed application.js:35638
(anonymous function) application.js:1193
fire application.js:1038
self.fireWith application.js:1149
(anonymous function) application.js:1200
fire application.js:1038
self.fireWith application.js:1149
done application.js:8075
script.onload.script.onreadystatechange

Related

Finding text between tag <a> (regex with variable)

function Selection () {
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
var result = html.indexOf("href");
if (result != -1) {
window.getSelection().empty();
window.getSelection().removeAllRanges();
} else {
var textofclassText = $('.text').html();
//var patt1 = /id=\"load([\s\S]*?)+[selectedTexthtml]/g;
var result_2;
//var result_2 = textofclassText.search(patt1);
if(result_2 != "null") {
window.getSelection().empty();
window.getSelection().removeAllRanges();
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<div class="text" onmouseup="Selection()">
You
<br><a class="good" data-id="3" href="#load" id="load3">are very </a> important for us.
<br>So we're offering you
<br><a class="good" data-id="4" href="#load" id="load4"> win-win</a> option.
<br>You will get 2 refrigerators if you buy one
</div>
</body>
</html>
I need to remove selection if the selected text contains "a" tag. If I start to select link-text from left I will get only text "are very", so I need to find first match in all class ".text". I can't get how to use variable in that regex, because when I try to build string from variables it doesn't work.
var patt1 = new RegExp("id=\"load([\s\S]*?)" + html, "g");
var result = textofclassText.search(patt1);

Simple boolean conditional from AJAX (ember.js)

I'm trying to do something which must be really simple to accomplish in Ember.
I want to show a button in my template based on the boolean state of a property:
{{#if canFavoriteTag}}
{{d-button action="favoriteTag" label="tagging.favorite" icon="star-o" class="admin-tag favorite-tag"}}
{{else}}
{{d-button action="unFavoriteTag" label="tagging.unfavorite" icon="star-o" class="admin-tag favorite-tag tag-unfavorite"}}
{{/if}}
I have created a property called canFavoriteTag with a function which I want to return true or false to the template based on whether the user can favorite the tag or not:
export default Ember.Controller.extend(BulkTopicSelection, {
canFavoriteTag: function() {
const self = this;
var ticker = this.get('tag.id');
console.log('checking can fav stock:' + ticker);
Discourse.ajax("/stock/get_users_favorite_stocks", {
type: "GET",
}).then(function(data) {
var favable = true;
for (var i = data.stock.length - 1; i >= 0; i--) {
var stock = jQuery.parseJSON(data.stock[i]);
if(ticker.toLowerCase() == stock.symbol.toLowerCase()) { console.log(ticker + ' is a favorite stock: ' + stock.symbol.toLowerCase()); favable = false; }
}
console.log(favable);
return favable;
});
}.property('canFavoriteTag') <-- unsure about this?
...
When the page loads, the wrong button shows (always the "false" one).. I see in the console that the favable variable gets set to false when the ajax call completes, but the button never changes. How do I get it to show the right button based on the function? Do I need to use a promise? If so, how?

How do I return data to a template with Knockout and Requirejs modules?

I'm having a difficult time returning data from a module using RequireJS and Knockout to populate my markup for my single page app. Knockout can't seem to find my data binding observables.
I'm trying to keep each view in a separate js file, but I'm failing to identify where I've gone wrong. Here's what I have so far:
/app/app.js
define(function(require) {
require('simrou');
var $ = require('jQuery'),
ko = require('knockout'),
videoView = require('videoView');
var init = function() {
var viewModel = function() {
var self = this;
self.currentPage = ko.observable();
self.videoView = new videoView();
}
var view = new viewModel();
ko.applyBindings( view );
_router = new Simrou({
'/video/:id': [ view.videoView.getVideo ]
});
_router.start();
};
return {
init: init
};
});
/app/videoView.js
define(function(require) {
"use strict";
var $ = require('jQuery'),
ko = require('knockout');
return function() {
var self = this;
self.currentPage = ko.observable( 'showVideo' );
self.currentVideo = ko.observable();
self.videoData = ko.observableArray([]);
self.videoList = ko.observableArray([]);
var getVideo = function( event, params ) {
// ajax pseudo code
$.ajax({});
self.videoData( dataFromAjaxCall );
}
return {
getVideo: getVideo
};
};
});
index.html
When I browse to /#/video/14 I receive the following error:
Uncaught ReferenceError: Unable to parse bindings.
Bindings value: attr: { 'data-video-id': videoData().id }
Message: videoData is not defined
Here's the markup:
<section id="showVideo" data-bind="fadeVisible: currentPage()=='showVideo', with: $root">
<div class="video" data-bind="attr: { 'data-video-id': videoData().id }></div>
</section>
Like I said, I'm trying to keep each view separated, but I would love some enlightenment on what I'm doing wrong, or if this is even possible? Is there a better more efficient way?
videoData is a property of $root.videoView, not of the root model (the one you passed to applyBindings). It's also an observableArray, so videoData() is just a plain array and even if you get the context right, you won't be able to access its id property, since, being an array, it doesn't have.named properties.

#each iteration number in Ember.js or {{#index}}

According to this question, it was possible to do something like this with Handlebars rc1:
{{#each links}}
<li>{{#index}} - {{url}}</li>
{{/each}}
{{#index}} would basically give you the iteration index, which is really useful when creating tables.
When I try this with Ember.js rc3, I get an unexpected token error. Does this not work anymore? Did it ever work? Is there another way to get the iteration index?
It looks like it was possible. Can't get it to work with HBS RC3. Probably, is deprecated.
Here's a "hand written" HBS helper.
This can help you gettin the index with {{index}} and side by side you can know if the iteration in on first or last object of the Array with {{first}} and {{last}} respectively.
Ember.Handlebars.registerHelper("foreach", function(path, options) {
var ctx;
var helperName = 'foreach';
if (arguments.length === 4) {
Ember.assert("If you pass more than one argument to the foreach helper, it must be in the form #foreach foo in bar", arguments[1] === "in");
var keywordName = arguments[0];
options = arguments[3];
path = arguments[2];
helperName += ' ' + keywordName + ' in ' + path;
if (path === '') {
path = "this";
}
options.hash.keyword = keywordName;
} else if (arguments.length === 1) {
options = path;
path = 'this';
} else {
helperName += ' ' + path;
}
options.hash.dataSourceBinding = path;
// Set up emptyView as a metamorph with no tag
//options.hash.emptyViewClass = Ember._MetamorphView;
// can't rely on this default behavior when use strict
ctx = this || window;
var len = options.contexts[0][path].length;
options.helperName = options.helperName || helperName;
options.contexts[0][path].map(function(item, index) {
item.index = index;
item.first = index === 0;
item.last = index === len - 1;
})
if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) {
new GroupedEach(ctx, path, options).render();
} else {
return Ember.Handlebars.helpers.collection.call(ctx, Ember.Handlebars.EachView, options);
}
});
and this can be tested like
{{#foreach array}}
{{log index first last}}
{{/foreach}}
i had the same problem recently i finish by writing a bound helper and passing them objects via Binding for example item here is an ember DS.Store object and content is a 'content' of the controller. hope it
Ember.Handlebars.registerBoundHelper 'isPair', (content, options)->
item = options.hash.item
content_name = options.hash.content || 'content'
if #get(content_name).indexOf(item) % 2 == 0 then 'is-pair' else 'is-unpair'
and in you view you call it
{{isPair content itemBinding='order'}}
i don't know if it is what you looking for but it might give you some ideas how to use it in your project.
btw. Ember overwrites #each helper that's why there it no #index i suppose

CheckBoxList multiple selections: how to model bind back and get all selections?

This code:
Html.CheckBoxList(ViewData.TemplateInfo.HtmlFieldPrefix, myList)
Produces this mark-up:
<ul><li><input name="Header.h_dist_cd" type="checkbox" value="BD" />
<span>BD - Dist BD Name</span></li>
<li><input name="Header.h_dist_cd" type="checkbox" value="SS" />
<span>SS - Dist SS Name</span></li>
<li><input name="Header.h_dist_cd" type="checkbox" value="DS" />
<span>DS - Dist DS Name</span></li>
<li><input name="Header.h_dist_cd" type="checkbox" value="SW" />
<span>SW - Dist SW Name </span></li>
</ul>
You can check multiple selections. The return string parameter Header.h_dist_cd only contains the first value selected. What do I need to do to get the other checked values?
The post method parameter looks like this:
public ActionResult Edit(Header header)
I'm assuming that Html.CheckBoxList is your extension and that's markup that you generated.
Based on what you're showing, two things to check:
The model binder is going to look for an object named Header with string property h_dist_cd to bind to. Your action method looks like Header is the root view model and not a child object of your model.
I don't know how you are handling the case where the checkboxes are cleared. The normal trick is to render a hidden field with the same name.
Also a nit, but you want to use 'label for="..."' so they can click the text to check/uncheck and for accessibility.
I've found that using extensions for this problem is error prone. You might want to consider a child view model instead. It fits in better with the EditorFor template system of MVC2.
Here's an example from our system...
In the view model, embed a reusable child model...
[AtLeastOneRequired(ErrorMessage = "(required)")]
public MultiSelectModel Cofamilies { get; set; }
You can initialize it with a standard list of SelectListItem...
MyViewModel(...)
{
List<SelectListItem> initialSelections = ...from controller or domain layer...;
Cofamilies = new MultiSelectModel(initialSelections);
...
The MultiSelectModel child model. Note the setter override on Value...
public class MultiSelectModel : ICountable
{
public MultiSelectModel(IEnumerable<SelectListItem> items)
{
Items = new List<SelectListItem>(items);
_value = new List<string>(Items.Count);
}
public int Count { get { return Items.Count(x => x.Selected); } }
public List<SelectListItem> Items { get; private set; }
private void _Select()
{
for (int i = 0; i < Items.Count; i++)
Items[i].Selected = Value[i] != "false";
}
public List<SelectListItem> SelectedItems
{
get { return Items.Where(x => x.Selected).ToList(); }
}
private void _SetSelectedValues(IEnumerable<string> values)
{
foreach (var item in Items)
{
var tmp = item;
item.Selected = values.Any(x => x == tmp.Value);
}
}
public List<string> SelectedValues
{
get { return SelectedItems.Select(x => x.Value).ToList(); }
set { _SetSelectedValues(value); }
}
public List<string> Value
{
get { return _value; }
set { _value = value; _Select(); }
}
private List<string> _value;
}
Now you can place your editor template in Views/Shared/MultiSelectModel.ascx...
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<WebUI.Cofamilies.Models.Shared.MultiSelectModel>" %>
<div class="set">
<%=Html.LabelFor(model => model)%>
<ul>
<% for (int i = 0; i < Model.Items.Count; i++)
{
var item = Model.Items[i];
string name = ViewData.ModelMetadata.PropertyName + ".Value[" + i + "]";
string id = ViewData.ModelMetadata.PropertyName + "_Value[" + i + "]";
string selected = item.Selected ? "checked=\"checked\"" : "";
%>
<li>
<input type="checkbox" name="<%= name %>" id="<%= id %>" <%= selected %> value="true" />
<label for="<%= id %>"><%= item.Text %></label>
<input type="hidden" name="<%= name %>" value="false" />
</li>
<% } %>
</ul>
<%= Html.ValidationMessageFor(model => model) %>
Two advantages to this approach:
You don't have to treat the list of items separate from the selection value. You can put attributes on the single property (e.g., AtLeastOneRequired is a custom attribute in our system)
you separate model and view (editor template). We have a horizontal and a vertical layout of checkboxes for example. You could also render "multiple selection" as two listboxes with back and forth buttons, multi-select list box, etc.
I think what you need is how gather selected values from CheckBoxList that user selected and here is my solution for that:
1- Download Jquery.json.js and add it to your view as reference:
2- I've added a ".cssMyClass" to all checkboxlist items so I grab the values by their css class:
<script type="text/javascript" >
$(document).ready(function () {
$("#btnSubmit").click(sendValues);
});
function populateValues()
{
var data = new Array();
$('.myCssClas').each(function () {
if ($(this).attr('checked')) {
var x = $(this).attr("value");
data.push(x);
}
});
return data;
}
function sendValues() {
var data = populateValues();
$.ajax({
type: 'POST',
url: '#Url.Content("~/Home/Save")',
data: $.json.encode(data),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function () { alert("1"); }
});
}
</script>
3- As you can see I've added all selected values to an Array and I've passed it to "Save" action of "Home" controller by ajax 4- in Controller you can receive the values by adding an array as argument:
[HttpPost]
public ActionResult Save(int[] val)
{
I've searched too much but apparently this is the only solution. Please let me know if you find a better solution for it.
when you have multiple items with the same name you will get their values separated with coma