My website allows users to video chat with one another, but currently the only way to change the video and audio source is the built in google method. I am trying to make it easier for users to change the video and audio source of the stream - is there an easy way to add this? i am showing the video source with :
<div class="show-model">
<!--<img src="/images/img1.jpg" class="img-response" ng-hide="isStreaming">-->
<div id="videos-container" room="{{$room}}" style="margin-top: 47px;"></div>
<div class="fullscreen-section" ng-show="isStreaming">
<div class="fullscreen-section__inner">
<div class="transparent-bg"></div>
<a class="cursor" title="full screen mode" ng-click="showFullScreen()" ng-show="!isFullScreenMode"><i class="fa fa-expand"></i></a>
<a class="cursor" title="compress screen mode" ng-click="notShowFullScreen()" ng-show="isFullScreenMode"><i class="fa fa-compress"></i></a>
</div>
</div>
</div>
And here is the directive for the stream.js
angular.module('matroshkiApp').directive('videoPlayer', ['$sce', function ($sce) {
return {
template: '<div><video ng-src="{{trustSrc()}}" id="streaming-{{videoId}}" autoplay class="img-responsive" height="130px"></video></div>',
restrict: 'E',
replace: true,
scope: {
vidSrc: '#',
showControl: '#',
vid: '#',
muted:'='
},
link: function link(scope, elem, attr) {
console.log('Initializing video-player');
scope.videoId = scope.vid;
scope.isMuted = scope.muted ? 'muted':'';
if(scope.isMuted){
jQuery(elem.context.firstChild).attr('muted',true);
elem.context.firstChild.muted = true;
}
scope.trustSrc = function () {
if (!scope.vidSrc) {
return undefined;
}
return $sce.trustAsResourceUrl(scope.vidSrc);
};
if (scope.showControl && elem.context && elem.context.firstChild) {
elem.context.firstChild.controls = true;
}
}
};
}]);
Related
I definitely know that something is wrong with this snippet but can't figure out the right way to get it done. I want to paginate the page where students are displayed. If I put it plainly like this return SchoolStudents.find();, it works perfectly by returning all the students but this defeats the main purpose of pagination. I'm either not sure where the problem is, either in the publish function or the helper function. What I want to achieve is that the records in SchoolStudents colleciton should be paginated to display 2 records on a page.
This is the autorun
Session.setDefault('skip', 0);
Template.view.onCreated(function () {
Session.setPersistent('ReceivedSlug', FlowRouter.getParam('myslug'));
this.autorun(function () {
Meteor.subscribe('SchoolStudents', Session.get('skip'));
});
});
this is the helper method
students(){
let myslug = trimInput(Session.get('ReceivedSlug'));
if (myslug) {
let mySchoolDoc = SchoolDb.findOne({slug: myslug});
if (mySchoolDoc) {
let arrayModuleSchool = StudentSchool.find({schoolId: mySchoolDoc._id});
if (arrayModuleSchool) {
var arrayStudentIds = [];
arrayModuleSchool.forEach(function(studentSchool){
arrayStudentIds.push(studentSchool.studentId);
});
let subReadiness = SchoolStudents.find({_id: {$in: arrayStudentIds}}).fetch();
if (subReadiness) {
return subReadiness;
}
}
}
}
}
This is the publish method
Meteor.publish('SchoolStudents', function (skipCount) {
check(skipCount, Number);
user = Meteor.users.findOne({_id:this.userId})
if(user) {
if(user.emails[0].verified) {
return SchoolStudents.find({userId: this.userId}, {limit: 2, skip: skipCount});
} else {
throw new Meteor.Error('Not authorized');
return false;
}
}
});
Blaze template
<section class="tab-section" id="content4">
{{#each student in students}}
<div class="row" style="margin-top: -20px;">
<!-- Begin Listing: 609 W GRAVERS LN-->
<div class="brdr bgc-fff pad-10 box-shad btm-mrg-20 property-listing card-1">
<div class="media">
<div class="media-body fnt-smaller">
<h4 class="media-heading">{{student.firstname}} {{student.lastname}}</h4>
<p class="hidden-xs" style="margin-bottom: 5px; margin-top: -10px;">{{trimString student.useremail 0 110}}</p><span class="fnt-smaller fnt-lighter fnt-arial">{{course.createdAt}}</span>
</div>
</div>
</div><!-- End Listing-->
</div>
{{/each}}
<ul class="pager">
<li class="studentprevious">Previous </li>
<li class="studentnext">Next </li>
</ul>
</section>
the pagination event
'click .studentprevious': function () {
if (Session.get('skip') > 0 ) {
Session.set('skip', Session.get('skip') - 2 );
}
},
'click .studentnext': function () {
Session.set('skip', Session.get('skip') + 2 );
}
I have a repeater that out puts panels for a slide show plugin, slick.js. So far, things are working as planned.
The user, when creating the custom page, enters in copy, then either and image, video from the media library, or a link to you tube.
What i'm trying to do is write the JS function that will fire when the user clicks play on the youtube video.
The webpart injects the youtube video via the iFrame method.
Here's my transformation:
<section class="slide">
<div class="copy">
<%# Eval("SlideContent") %>
</div>
<asp:PlaceHolder runat='server' id='slideImage' visible='<%# IfEmpty( Eval("SlideImage"), false, true ) %>'>
<div class="img">
<img class="img-responsive" src="<%# Eval(" SlideImage ") %>" alt="<%# Eval(" SlideContent ") %>">
</div>
</asp:PlaceHolder>
<asp:PlaceHolder runat='server' id='slideVideo' visible='<%# IfEmpty( Eval("SlideVideo"), false, true ) %>'>
<div class='videoHolder html5'>
<video id='video' class='html5Video' controls>
<source src='<%# Eval("SlideVideo") %>'>
</video>
</div>
</asp:PlaceHolder>
<asp:PlaceHolder runat='server' id='youTubeVideo' visible='<%# IfEmpty( Eval("YouTubeVideo"), false, true ) %>'>
<%# Register Src="~/CMSWebParts/Media/YouTubeVideo.ascx" TagName="YoutubeVideo" TagPrefix="webPart" %>
<div class='videoHolder yt'>
<webPart:YoutubeVideo runat="server" id="YouTubeVideoWebpart" CssClass="ytVideo" VideoURL='<%# ResolveMacros(Eval("YouTubeVideo").ToString())%>' FullScreen='true' />
</div>
</asp:PlaceHolder>
</section>
And here is my JS (this also includes the code to pause videos if the slider changes)
$(function () {
'use strict';
var $slider = $('.slider'),
$slickJS = '/kffIntranet/ui/bower_components/slick-carousel/slick/slick.min.js';
// we check for a slider on the page
if ($slider.length !== 0) {
// if there is a slider, we load the slick.js plugin
$.getScript($slickJS, function () {
// init the slider
$slider.slick({
dots: true,
infinite: true,
speed: 300,
slidesToShow: 1,
slidesToScroll: 1,
fade: false,
lazyLoad: 'ondemand',
adaptiveHeight: true,
autoplay: true,
autoplaySpeed: 5000,
responsive: [{
breakpoint: 1024,
settings: {}
}, {
breakpoint: 600,
settings: {}
}, {
breakpoint: 480,
settings: {
arrows: false
}
}
// You can unslick at a given breakpoint now by adding:
// settings: "unslick"
// instead of a settings object
]
});
});
//
// video control. If a slide has video, we need to pause
//bind our event here, it gets the current slide and pauses the video before each slide changes.
$slider.on('beforeChange', function (event, slick) {
var currentSlide, player, command, videoType;
//find the current slide element and decide which player API we need to use.
currentSlide = $(slick.$slider).find('.slick-current');
//determine which type of slide this by looking for the video holder than getting the video type class
if (currentSlide.find('.videoHolder').length) {
videoType = $('.videoHolder', currentSlide).attr('class').split(' ')[1];
//get the iframe inside this slide.
player = currentSlide.find('iframe').get(0);
}
// pause videos
if (videoType === 'yt') {
command = {
'event': 'command',
'func': 'pauseVideo'
};
player.contentWindow.postMessage(JSON.stringify(command), '*');
} else if (videoType === 'html5') {
document.getElementById('video').pause();
}
});
// pause slider if a video is playing
// html 5 video click
$('.html5Video').on('click', function () {
var $video = $(this).get(0);
// control pause play state of video
if ($video.paused) {
$video.play();
} else {
$video.pause();
}
// call slide pause function
pauseSlide();
});
// youtube play
$('.ytVideo iframe').on('click', function () {
// call slide pause function
pauseSlide();
});
}
// puse slider function
function pauseSlide() {
$slider.slick('slickPause');
console.log('pause');
}
});
So i've created a function pauseSlide that will pause the slider, but i'm struggling with capturing the youtube play click.
Check this answer out. It suggests using the YouTube iframe API.
<!DOCTYPE html> <html> <head> <script src="https://www.youtube.com/iframe_api"></script> </head> <body> <div id='vidWrapper'> <!-- The <iframe> (and video player) will replace this <div> tag. --> <div id="ytplayer"></div> </div> <script> var player; function onYouTubeIframeAPIReady() { player = new YT.Player('ytplayer', { height: '390', width: '640', videoId: 'M7lc1UVf-VE', events: { 'onStateChange': function(event) { if (event.data == YT.PlayerState.PLAYING) { pauseAudio(); } } } }); } function pauseAudio() { ... } </script> </body> </html>
Ok, so i took a step back in this. The transformation now looks for a YouTube ID and then i have a jQuery plugin take over.
So the slider auto playes, and if a video (either youtube, or mp4) is started, the slider stops. Also, if either of the slider direction arrows, or one of the pips are clicked, the video will pause.
I still need to refactor and add some logic, but it's working.
Here's the updated transformation:
<section class="slide">
<div class="copy">
<%# Eval("SlideContent") %>
</div>
<asp:PlaceHolder runat='server' id='slideImage' visible='<%# IfEmpty( Eval("SlideImage"), false, true ) %>'>
<div class="img">
<img class="img-responsive" src="<%# Eval(" SlideImage ") %>" alt="<%# Eval(" SlideContent ") %>">
</div>
</asp:PlaceHolder>
<asp:PlaceHolder runat='server' id='slideVideo' visible='<%# IfEmpty( Eval("SlideVideo"), false, true ) %>'>
<div class='videoHolder html5'>
<video id='video' class='html5Video' controls>
<source src='<%# Eval("SlideVideo") %>'>
</video>
</div>
</asp:PlaceHolder>
<asp:PlaceHolder runat='server' id='youTubeVideoID' visible='<%# IfEmpty( Eval("YouTubeVideoID"), false, true ) %>'>
<div class='videoHolder yt' data-vID='<%# Eval("YouTubeVideoID") %>'>
<div class="vh"></div>
</div>
</asp:PlaceHolder>
</section>
my video.js file
$(function () {
'use strict';
var vHolder = $('.videoHolder'),
vtPlugin = '/kffIntranet/ui/scripts/plugins/tubePlayer/jQuery.tubeplayer.min.js',
vID;
// check for a youtube video, and if there is one, load the youtube pluging
if ($('.yt').length) {
// load plugin
$.getScript(vtPlugin, function () {
// once plugin is loaded, loop through each YT and call the plugin
$('.yt').each(function (i, v) {
var $this = $(this);
vID = $this.attr('data-vid');
$('.vh', $this).tubeplayer({
initialVideo: vID,
onPlayerPlaying: function () {
$('.slider').slick('slickPause');
}
});
});
});
} else if ($('.html5').length) {
$('.html5 .html5video').bind('pause', function () {
$('.slider').slick('slickPause');
});
}
});
and my slider js file
$(function () {
'use strict';
var $slider = $('.slider'),
$slickJS = '/kffIntranet/ui/bower_components/slick-carousel/slick/slick.min.js',
videoType;
// we check for a slider on the page
if ($slider.length !== 0) {
// if there is a slider, we load the slick.js plugin
$.getScript($slickJS, function () {
// init the slider
$slider.slick({
dots: true,
infinite: true,
speed: 300,
slidesToShow: 1,
slidesToScroll: 1,
fade: false,
lazyLoad: 'ondemand',
adaptiveHeight: true,
autoplay: true,
autoplaySpeed: 5000,
responsive: [{
breakpoint: 1024,
settings: {}
}, {
breakpoint: 600,
settings: {}
}, {
breakpoint: 480,
settings: {
arrows: false
}
}
// You can unslick at a given breakpoint now by adding:
// settings: "unslick"
// instead of a settings object
]
});
});
// on slide change
$slider.on('beforeChange', function (event, slick) {
var currentSlide;
//find the current slide element and decide which player API we need to use.
currentSlide = $(slick.$slider).find('.slick-current');
//determine which type of slide this by looking for the video holder than getting the video type class
if (currentSlide.find('.videoHolder').length) {
videoType = $('.videoHolder', currentSlide).attr('class').split(' ')[1];
}
// pause videos
if (videoType === 'html5') {
document.getElementById('video').pause();
} else if (videoType === 'yt') {
$('.vh', currentSlide).tubeplayer('pause');
}
});
}
});
So I am using Meteor/React, but I used Blaze's login template for its convenience. It works great on the homepage, but when I try to login from any other page on the site, the page reloads and the login appears to have been unsuccessful.
This is my implementation.
AccountsUI.jsx
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
export class AccountsUI extends React.Component {
componentDidMount(){
this.view = Blaze.render(Template.loginButtons, this.refs.loginContainer);
}
componentWillUnmount(){
Blaze.remove(this.view);
}
render(){
return(
<span ref="loginContainer" />
)
}
}
mainLayout.jsx
<div className="container-fluid">
<a className="navbar-btn pull-left panel-body"><b>FAQ</b></a>
<a className="navbar-btn pull-right panel-body"><b>Category</b></a>
<a className="navbar-btn pull-right panel-body"><b>Notifications</b></a>
<a className="navbar-btn pull-right panel-body"><b><AccountsUI /></b></a>
</div>
</div>
Why would this work only on certain pages?
Blaze
Your code looks ok, are you importing all components correctly?
Try: https://atmospherejs.com/gadicc/blaze-react-component
and do:
import Blaze from 'meteor/gadicc:blaze-react-component';
....
<a className="navbar-btn pull-right panel-body"><b><Blaze template="loginButtons" /></b></a>
....
Without trying to change your choice of tools too much, I have been exploring React, Meteor and Authentication for a little while, often getting stuck in state management and other dark holes. Below is a overview of some options:
React Accounts-UI package
Personally as a quick tool I am a big fan of the React Accounts-UI package https://atmospherejs.com/std/accounts-ui
It's easy to implement and has many React specific config options.
Check out 'Create your own styled version' to implement in Navbar at https://github.com/studiointeract/accounts-ui/blob/master/README.md
React with Kadira FlowRouter and ReactLayout
For something within the Navbar, here is a stab with flow router.
From the Meteor Guide User/Authentication section:
While a router is optional and the basic functionality will work without it, it’s also a good idea to pick a router integration:
For Navbar login (Not React Accounts-UI).
You need Flowrouter and Reactlayout
Routes
We create 2 route groups which allow us to build auth logic into Flow router easily:
const publicRoutes = FlowRouter.group( { name: 'public' } );
publicRoutes.route( '/login', {
name: 'login',
action() {
ReactLayout.render( App, {
yield: <Login /> }
);
}
}
);
const authenticatedRoutes = FlowRouter.group( { name: 'authenticated' } );
authenticatedRoutes.route( '/hidden', {
name: 'hidden',
action() {
ReactLayout.render( App, {
yield: <Hidden /> }
);
}
}
);
App:
You can modify this to suit your own setup. The approach here is to grab the reactmeteordata mixing which allows us to test if the user is logged or logging in. The isPublic function allows us to test if the user should be allowed on the current route. The rest should be self explanatory.
App = React.createClass({
mixins: [ ReactMeteorData ],
getMeteorData() {
return {
loggingIn: Meteor.loggingIn(),
hasUser: !!Meteor.user(),
isPublic( route ) {
let publicRoutes = [
'login'
];
return publicRoutes.indexOf( route ) > -1;
},
canView() {
return this.isPublic( FlowRouter.current().route.name ) || !!Meteor.user();
}
};
},
loading() {
return <div className="loading"></div>;
},
getView() {
return this.data.canView() ? this.props.yield : <Login />;
},
render() {
return <div className="app-root">
<AppHeader hasUser={this.data.hasUser} />
<div className="container">
{this.data.loggingIn ? this.loading() : this.getView()}
</div>
</div>;
}
}
);
Header:
Nothing cosmic, we change the brandLink depending on user state. We then check hasUser (passed as a prop from the App component) to change which nav component to display.
AppHeader = React.createClass({
mixins: [ ReactMeteorData ],
getMeteorData() {
return { brandLink: !!Meteor.user() ? '/hidden' : '/login' }; },
render() {
return ( <nav className="navbar navbar-default" role="navigation">
<div className="container">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse"><span className="sr-only">Toggle navigation</span><span className="icon-bar"></span> <span className="icon-bar"></span><span className="icon-bar"></span>
</button>
<a className="navbar-brand" href={this.data.brandLink}>AuthExample</a>
</div>
{this.props.hasUser ? <AuthenticatedNavigation /> : <PublicNavigation />}
</div>
</nav> );
}
});
AuthenticatedNavigation component :
AuthenticatedNavigation = React.createClass({
currentUserEmail() {
return Meteor.user().emails[0].address;
},
logout( event ) {
event.preventDefault();
return Meteor.logout( () =>
FlowRouter.go( '/login' ) );
},
render() {
return <div id="navbar-collapse" className="collapse navbar-collapse">
<ul className="nav navbar-nav">
<li className={FlowHelpers.currentRoute( 'hidden' )}>Hidden
</li>
</ul>
<ul className="nav navbar-nav navbar-right">
<li className="dropdown">
<a href="#" className="user-profile-toggle dropdown-toggle clearfix" data-toggle="dropdown">{this.currentUserEmail()} <span className="caret"></span>
</a>
<ul className="dropdown-menu" role="menu">
<li>Account Preferences</li>
<li className="logout" onClick={this.logout}>Logout</li>
</ul>
</li>
</ul>
</div>;
}
});
PublicNavigation Component:
PublicNavigation = React.createClass({
render() {
return (
<div id="navbar-collapse" className="collapse navbar-collapse">
<ul className="nav navbar-nav navbar-right">
<li className={FlowHelpers.currentRoute( 'login' )}>
<a href={FlowHelpers.pathFor( 'login' )}>Login</a>
</li>
</ul>
</div> );
}
}
);
Look at https://themeteorchef.com/snippets/authentication-with-react-and-flow-router/ for more details.
Im building a custom Minecraft Server Status and hit a problem. The first version of this was successful but the code was rather long and I decided to make it better and shorter. The script is supposed to fill the elements of each .server but it doesn't work.
<div class="server_status">
<div class="container servers_info">
<h1>My Network</h1>
<div id="of the server" class="server" title="of the server" server-ip="0.0.0.0">
<div class="name"></div>
<div class="count"><i class="fa fa-spinner fa-spin"></i></div>
<div class="players">Loading player data <i class="fa fa-spinner fa-spin"></i></div>
<div class="status"></div>
</div>
<div id="of the server" class="server" title="of the server" server-ip="0.0.0.0">
<div class="name"></div>
<div class="count"><i class="fa fa-spinner fa-spin"></i></div>
<div class="players">Loading player data <i class="fa fa-spinner fa-spin"></i></div>
<div class="status"></div>
</div>
<!-- ..... more servers -->
<span class="total"><i class="fa fa-spinner fa-spin"></i></span>
</div>
$(document).ready(function ping() {
$( ".servers_info .server" ).each( function() {
var name = $(this).attr( "title" );
var ip = $(this).attr( "server-ip" );
var id = $(this).attr( "id" );
var total = 0;
var call = "Get Avatar List adress";
//Set the name:
$(".name",this).html(name);
//Gets the data:
$.getJSON("http://mcapi.ca/v2/query/info/?ip=" + ip, function (json) {
//Checks The status and applies visual effects:
if (json.status !== "false") {
$(".status",this).html("<span class=\"l-online\">" + json.ping + " ms</span>");
$(this).removeClass('blur');
} else {
$(".status",this).html("<span class=\"l-offline\">0 ms</span>");
$(this).addClass('blur');
};
});
});
//Sets Refresh rate of 10s
setTimeout(ping, 10000);
});
I narrowed down the problem to the $.getJSON part. The data is retrieved correctly but cannot be placed in its respective DIVs. The only difference with the first version of the script is that I used 4 getJSON separately for each of the servers I wanted to display. Now using .each to combine it for all 4 of them and also $(this) to use relative objects.
I suspect the problem is in th usage of $(this) in .get but I'm nnot sure and don't know how to fix it.
As you suspect, the issue is the $(this). part. Inside the $.getJSON callback this no longer refers to the DOM object that triggered the event.
To fix this you can either:
Add a .bind(this) to the callback function. No changes required inside the function itself.
$.getJSON(url, function(json) {
/* all your code here */
}.bind(this)
);
Or save the reference to this before $.getJSON and use it inside the callback.
var _this = this;
$.getJSON(url, function(json) {
/* replace all references of this to _this for example*/
$(_this).removeClass('blur');
});
Hope that helps
I am an absolute beginner in node.js and geddy. I've followed a few tutorials and now I try to write something similar for my purposes.
When I try to create a new item, though, I get the following message:
/arithmetic_problem_types/function%20(id)%20%7B%20%20%20%20%20%20options.id%20=%20id;%20%20%20%20%20%20return%20helpersBase.urlFor.action(options);%20%20%20%20%7D not found.
I have no idea where this could come from. I've looked through the code and found nothing.
Controller:
var ArithmeticProblemTypes = function () {
this.respondsWith =[ 'html', 'json', 'xml', 'js', 'txt'];
this.index = function (req, resp, params) {
var self = this;
geddy.model.ArithmeticProblemType.all(function (err, arithmetic_problem_types) {
self.respond({
params: params, arithmetic_problem_types: arithmetic_problem_types
});
});
};
this.add = function (req, resp, params) {
this.respond({
params: params
});
};
this.create = function (req, resp, params) {
var self = this, arithmetic_problem_type = geddy.model.ArithmeticProblemType.create({
name: '1', title: 'open', numberType: '1', numberRanges: '1', operators: '1', askedFor: '1', specialProblemCategory: '1', askedForNumDenomOrBoth: '1',
reducedFractions:'1', mixedFractions: '1'
});
arithmetic_problem_type.save(function (err, data) {
if (err) {
params.errors = err;
self.transfer('add');
} else {
self.redirect({
controller: self.name
});
}
});
};
....................................................................
};
exports.ArithmeticProblemTypes = ArithmeticProblemTypes;
add.html.ejs
<div class="hero-unit">
<%= partial('_form', {params: params}); %>
</div>
index.html.ejs
<div class="hero-unit">
<h2>Arithmetic Problem Types List</h2>
<%- linkTo('Create a new Aritmetic Problem Type', addArithmeticProblemTypePath, {class: 'btn pull-right'}) %>
</div>
<% if (arithmetic_problem_types && arithmetic_problem_types.length) { %>
<% for (var i in arithmetic_problem_types) { %>
<div class="row todo-item">
<div class="span8">
<h3><%- linkTo(arithmetic_problem_types[i].title, arithmeticProblemTypePath(arithmetic_problem_types[i].id)) %></h3>
</div>
<div class="span4"><h3><i class="icon-list-alt"></i><%= arithmetic_problem_types[i].status; %></h3></div>
</div>
<% } %>
<% } %>
How can I get rid of that message and make it work?
EDIT:
This is the beginning of the _form.html.ejs file:
<%
var isUpdate = params.action == 'edit'
, formTitle = isUpdate ? 'Update this Arithmetic Problem Type' : 'Create a new Arithmetic Problem Type'
, action = isUpdate ? arithmeticProblemTypePath(params.id) + '?_method=PUT' : arithmeticProblemTypePath
, deleteAction = isUpdate ? arithmeticProblemTypePath(params.id) + '?_method=DELETE' : ''
, btnText = isUpdate ? 'Update' : 'Add'
, nameValue = isUpdate ? arithmeticProblemTypePath.name : ''
, errors = params.errors;
%>
<form id="arithmetic-problem-type-form" class="form-horizontal" action="<%= action %>" method="POST">
....
</form>
EDIT2:
Inspecting the page where I should write the name of the item and click the add button, I've found this
<div class="hero-unit">
<form id="arithmetic-problem-type-form" class="form-horizontal" action="function (id) {
options.id = id;
return helpersBase.urlFor.action(options);
}" method="POST">
<fieldset>
<legend>Create a new Arithmetic Problem Type</legend>
<div class="control-group">
<label for="title" class="control-label">Title</label>
<div class="controls">
<input class="span6" name="name" placeholder="enter name" type="text">
</div>
</div>
<div class="form-actions">
<input class="btn btn-primary" type="submit" value="Add">
</div>
</fieldset>
</form>
</div>
Indeed the message comes from the action attribute of the form element, but how can I solve it?
The message is telling you that the requested URL could not be found. AKA 404
/arithmetic_problem_types/function%20(id)%20%7B%20%20%20%20%20%20options.id%20=%20id;%20%20%20%20%20%20return%20helpersBase.urlFor.action(options);%20%20%20%20%7D
is definitely not a nice looking url. So i'm assuming there's something wrong with your form's action attribute. If that's what happened when you validate the form.
If that's what happened when you click the link to "Create a new arithmetic problem type" then you should probably put parenthesis after addArithmeticProblemTypePath