I am writing my blog site with using angular.js and asp.net webapi. I did url routing with angular.js. when I click url but I get 404 not found error. How to fix this problem?
var blogSite = angular.module("blogSite", ["ngRoute"]);
blogSite.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when("post/:post", {
teplateUrl: 'blogpost.html',
controller: 'blogPostController'
})
.otherwise({
redirectTo: 'index.html'
});
}]);
blogSite.controller("mainPageController", function ($scope, $http, $routeParams) {
var url = baseUrl + "getpost";
$http.get(url)
.success(function (data, status, headers, config) {
if (data.length > 0) {
$scope.LastBlogPosts = data;
}
else {
$scope.LastBlogPosts = null;
}
})
.error(function (data, status, headers, config) {
console.log(status);
});
});
blogSite.controller("blogPostController", function ($scope, $http, $routeParams) {
$scope.TestMessage = "Test Message"
});
index.html page
div ng-controller="mainPageController">
<ul>
<li ng-repeat="blogPost in LastBlogPosts">
{{blogPost.PostTitle}}
</li>
</ul>
</div>
</div>
</div>
<div ng-view>
</div>
You anchor should be having # to restrict redirection, putting # at the start of URL will keep you in same page & will keep you in SPA
<a href="#post/{{blogPost.PostUrl}}" class="lastPostUrl mainPagePostTitle">
{{blogPost.PostTitle}}
</a>
Related
I am fairly new to Meteor.
I have a template helper function which requires to work with data I am publishing from the server side. When the page loads, the value for profile below is undefined. But after that when I execute the same code snippet from browser's console, it works just fine. From what I understand, the template helper is being executed before the data is published. How can I wait until the data is published to run the UI helper?
Here is relevant code.
Helper Function
Template.header.helpers({
me: function() {
var user = Meteor.users.findOne(Meteor.userId());
if (user) {
return Profiles.findOne({
spid: user.profile.spid
});
}
return null;
}
});
HTML Template
<template name="header">
<header class="ui fixed inverted menu">
{{> thumb user=me}}
</header>
</template>
Thumbnail Template
<template name="thumb">
{{#with user}}
<div class="thumb">
<div class="text" style="background-color:{{color}}">
{{initials name}}
</div>
<div class="pic" style="background-image:url('{{pic}}')"></div>
</div>
{{/with}}
</template>
Publications
Meteor.publish('profiles', function() {
return Profiles.all();
});
Meteor.publish('departments', function() {
return Departments.all();
});
Meteor.publish('requestServiceIds', function() {
return Requests.getServiceIds();
});
Meteor.publish('relevantServices', function() {
return Services.getRelevant(this.userId, 5);
});
Router
Router.configure({
layoutTemplate: 'main',
waitOn: function() {
Deps.autorun(function() {
Meteor.subscribe('profiles', Partitioner.group());
Meteor.subscribe('departments', Partitioner.group());
});
}
});
Router.onBeforeAction(function() {
if (this.route.getName() === 'not-authorized') return this.next();
if (!Meteor.userId() || !Cookie.get('TKN')) {
this.render('login');
} else {
this.next();
}
});
Router.route('/', {
name: 'home',
waitOn: function() {
Deps.autorun(function() {
Meteor.subscribe('requestServiceIds', Partitioner.group());
Meteor.subscribe('relevantServices', Partitioner.group());
});
}
});
---
UPDATE 1
I updated the the router a bit. But it did not seem to have had made any difference.
Router.configure({
layoutTemplate: 'main',
waitOn: function() {
// Deps.autorun(function() {
// Meteor.subscribe('profiles', Partitioner.group());
// Meteor.subscribe('departments', Partitioner.group());
// });
return [
Meteor.subscribe('profiles', Partitioner.group()),
Meteor.subscribe('departments', Partitioner.group())
];
}
});
Router.route('/', {
name: 'home',
waitOn: function() {
// Deps.autorun(function() {
// Meteor.subscribe('requestServiceIds', Partitioner.group());
// Meteor.subscribe('relevantServices', Partitioner.group());
// });
return [
Meteor.subscribe('requestServiceIds', Partitioner.group()),
Meteor.subscribe('relevantServices', Partitioner.group())
];
}
});
Create a dumb loading template such as this one (using font awesome):
<template name="loading">
<div class="loading">
<i class="fa fa-circle-o-notch fa-4x fa-spin"></i>
</div>
</template>
And try replacing your Router.configure() part with something like this:
Router.configure({
layoutTemplate: 'main',
action: function() {
if(this.isReady()) { this.render(); } else {this.render("loading");}
},
isReady: function() {
var subs = [
Meteor.subscribe('profiles', Partitioner.group());
Meteor.subscribe('departments', Partitioner.group());
];
var ready = true;
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
return {
params: this.params || {},
profiles: Profiles.find(),
departments: Departments.find()
};
}
}
});
I guess that could be achieved using waitOn, but since your way does not work, I give you a working recipe I use everyday. Code inspired from meteor Kitchen generated code.
To answer your comments:
Regarding the Action not being triggered, it might be because we tried to put in inside the Router.configure. I don't do it that way, here are some details on how I implement it.
First, I set a controller for each route inside a router.js file (where I have my Router.configure() function too. It looks like that:
Router.map(function () {
this.route("login", {path: "/login", controller: "LoginController"});
// other routes
}
Next, I create a controller that I store in the same folder than my client view template. It looks like that:
this.LoginController = RouteController.extend({
template: "Login",
yieldTemplates: {
/*YIELD_TEMPLATES*/
},
onBeforeAction: function() {
/*BEFORE_FUNCTION*/
this.next();
},
action: function() {
if(this.isReady()) { this.render(); } else { this.render("loading"); }
/*ACTION_FUNCTION*/
},
isReady: function() {
var subs = [
];
var ready = true;
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
return {
params: this.params || {}
};
/*DATA_FUNCTION*/
},
onAfterAction: function() {
}
});
So this action function is working when I extend a route using a controller. Maybe it will solve your issue.
For completeness sake, here is how my Router.configure() looks like:
Router.configure({
templateNameConverter: "upperCamelCase",
routeControllerNameConverter: "upperCamelCase",
layoutTemplate: "layout",
notFoundTemplate: "notFound",
loadingTemplate: "loading",
//I removed the rest because it is useless.
});
I'm making a simple Meteor app that can redirect to a page when user click a link.
On 'redirect' template, I try get the value of property 'url' from the template instance. But I only get right value at the first time I click the link. When I press F5 to refresh 'redirect' page, I keep getting this error message:
Exception from Tracker afterFlush function: Cannot read property 'url' of null
TypeError: Cannot read property 'url' of null
at Template.redirect.rendered (http://localhost:3000/client/redirect.js?abbe5acdbab2c487f7aa42f0d68cf612f472683b:2:17)
at null.
This is where debug.js points to: (line 2)
if (allArgumentsOfTypeString)
console.log.apply(console, [Array.prototype.join.call(arguments, " ")]);
else
console.log.apply(console, arguments);
} else if (typeof Function.prototype.bind === "function") {
// IE9
var log = Function.prototype.bind.call(console.log, console);
log.apply(console, arguments);
} else {
// IE8
Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));
}
Can you tell me why I can't read the value of 'url' property from template data context in template rendered callback?
This is my code (for more details, you can visit my repo):
HTML:
<template name="layout">
{{>yield}}
</template>
<template name="home">
<div id="input">
<input type="text" id="url">
<input type="text" id="description">
<button id="add">Add</button>
</div>
<div id="output">
{{#each urls}}
{{>urlItem}}
{{/each}}
</div>
</template>
<template name="redirect">
<h3>Redirecting to new...{{url}}</h3>
</template>
<template name="urlItem">
<p><a href="{{pathFor 'redirect'}}">
<strong>{{url}}: </strong>
</a>{{des}}</p>
</template>
home.js
Template.home.helpers({
urls: function(){
return UrlCollection.find();
}
});
Template.home.events({
'click #add': function() {
var urlItem = {
url: $('#url').val(),
des: $('#description').val()
};
Meteor.call('urlInsert', urlItem);
}
});
redirect.js
Template.redirect.rendered = function() {
if ( this.data.url ) {
console.log('New location: '+ this.data.url);
} else {
console.log('No where');
}
}
Template.redirect.helpers({
url: function() {
return this.url;
}
});
router.js
Router.configure({
layoutTemplate: 'layout'
})
Router.route('/', {
name: 'home',
waitOn: function() {
Meteor.subscribe('getUrl');
}
});
Router.route('/redirect/:_id', {
name: 'redirect',
waitOn: function() {
Meteor.subscribe('getUrl', this.params._id);
},
data: function() {
return UrlCollection.findOne({_id: this.params._id});
}
});
publication.js
Meteor.publish('getUrl', function(_id) {
if ( _id ) {
return UrlCollection.find({_id: _id});
} else {
return UrlCollection.find();
}
});
Add this
Router.route('/redirect/:_id', {
name: 'redirect',
waitOn: function() {
Meteor.subscribe('getUrl', this.params._id);
},
data: function() {
if(this.ready()){
return UrlCollection.findOne({_id: this.params._id});
}else{
console.log("Not yet");
}
}
});
Tell me if works.
With help from my colleague, I can solve the problem.
My problem comes from wrong Meteor.subscribe syntax. In my code, I forget "return" in waitOn function. This will make Meteor do not know when the data is fully loaded.
Here is the right syntax:
waitOn: function() {
return Meteor.subscribe('getUrl', this.params._id);
}
I am basically trying to upload a file using angular and a webmethod.
I took the code from this blog but it does not work. The request is successfull as seen from fiddler but the web method never gets invoked.
Am I doing something wrong?
Following is my code .
angular
.module('loader', ['ui.router', 'ui.bootstrap', 'ui.filters'])
.controller('loader-main', function($rootScope, $scope, WebServices) {
$scope.uploadNewFile = function() {
WebServices.uploadFile($scope.myfile);
}
}).factory('WebServices', function($rootScope, $http) {
return {
postFile: function(method, uploadData) {
var uploadUrl = "myASPXPAGE.aspx/" + method;
return $http.post(uploadUrl, uploadData, {
transformRequest: angular.identity,
headers: {
'Content-Type': undefined
}
}).success(function(data) {
///Control reaches here but never hits the server method
});
},
uploadFile: function(filedata) {
var fd = new FormData();
fd.append('file', filedata);
return this.postFile("UploadFile", fd);
}
};
}).directive('fileModel', ['$parse',
function($parse) {
return {
restrict: 'A',
link: function($scope, element, attr) {
var model = $parse(attr.fileModel);
var modelSetter = model.assign;
element.bind('change', function() {
$scope.$apply(function() {
modelSetter($scope, element[0].files[0]);
});
});
}
}
}
]);
<div class="row">
<div class="col-xs-5">
<div class="col-xs-4">Browse to File:</div>
<div class="col-xs-1">
<input type="file" id="uploadFile" class="btn btn-default" file-model="myfile" />
<input type="button" class="btn btn-primary" value="Load File" data-ng-click="uploadNewFile()" />
</div>
</div>
</div>
And here is my WebMethod
[WebMethod]
public static string UploadFile()
{
System.Diagnostics.Debugger.Break();
return "Done";
}
Figured it out. You cannot have Content-Type as multipart/form-data in webmethods. Instead created a HttpHandler to upload the file and everything works just fine.
Can anyone help with a working example of recaptcha in meteor without using iframes?
I cannot make the recaptcha scripts run even when I try to run them from the client.js using jquery append.
After doing some investigations I found that I had to manually integrate the reCaptcha.
The client side code:
HTML:
<form id="mySecuredForm" novalidate>
<!-- labels and inputs here -->
<div class="row">
<div id="captcha-container">
<div id="rendered-captcha-container">loading...</div>
</div>
</div>
<div class="row">
<button type="submit" id="submit" class="submit-button">Submit</button>
</div>
</form>
JS
if (Meteor.isClient) {
Template.myTemplate.rendered = function() {
$.getScript('http://www.google.com/recaptcha/api/js/recaptcha_ajax.js', function() {
Recaptcha.create('add_your_public_key_here', 'rendered-captcha-container', {
theme: 'red',
callback: Recaptcha.focus_response_field
});
});
}
Template['myTemplate'].events({
'submit form#mySecuredForm': function(event) {
event.preventDefault();
event.stopPropagation();
var formData = {
captcha_challenge_id: Recaptcha.get_challenge(),
captcha_solution: Recaptcha.get_response()
//add the data from form inputs here
};
Meteor.call('submitMySecuredForm', formData, function(error, result) {
if (result.success) {
//set session vars, redirect, etc
} else {
Recaptcha.reload();
// alert error message according to received code
switch (result.error) {
case 'captcha_verification_failed':
alert('captcha solution is wrong!');
break;
case 'other_error_on_form_submit':
alert('other error');
break;
default:
alert('error');
}
}
});
}
Server side code
function verifyCaptcha(clientIP, data) {
var captcha_data = {
privatekey: 'add_private_key_here',
remoteip: clientIP
challenge: data.captcha_challenge_id,
response: data.captcha_solution
};
var serialized_captcha_data =
'privatekey=' + captcha_data.privatekey +
'&remoteip=' + captcha_data.remoteip +
'&challenge=' + captcha_data.challenge +
'&response=' + captcha_data.response;
var captchaVerificationResult = null;
var success, parts; // used to process response string
try {
captchaVerificationResult = HTTP.call("POST", "http://www.google.com/recaptcha/api/verify", {
content: serialized_captcha_data.toString('utf8'),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': serialized_captcha_data.length
}
});
} catch(e) {
return {
'success': false,
'error': 'google_service_not_accessible'
};
}
parts = captchaVerificationResult.content.split('\n');
success = parts[0];
if (success !== 'true') {
return {
'success': false,
'error': 'captcha_verification_failed'
};
}
return {
'success': true
};
}
Meteor.methods({
"submitMySecuredForm": function(data) {
//!add code here to separate captcha data from form data.
var verifyCaptchaResponse = verifyCaptcha(this.connection.clientAddress, data);
if (!verifyCaptchaResponse.success) {
console.log('Captcha check failed! Responding with: ', verifyCaptchaResponse);
return verifyCaptchaResponse;
}
console.log('Captcha verification passed!');
//!add code here to process form data
return {success: true};
});
There is also the possibility to listen to the post event on the server side. The http calls can be done synchronous as above or asynchronous with fibers/futures.
Server side http call to google API was inspired from:
https://github.com/mirhampt/node-recaptcha/blob/master/lib/recaptcha.js
i have a PartialView in my MVC Appliction, which returns my View if there are any Errors in the ModelState. In the _Layout site are many javascript ( jQuery, JQuery.validate, ... ) references which i use in the partai view.
Here the Code:
Javascript submit:
$(function () {
$('form').submit(function (e) {
e.preventDefault();
if ($('form').valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (!result.Success) {
$('#formcontent').html(result); // Show PartailView with Validationmessages
}
else {
}
}
});
}
});
});
Parent Site:
<div id="formcontent" class="tc-form">
#{ Html.RenderPartial( "_ConfigurationPartial", Model ); }
</div>
Partial View:
#model SettingsViewModel
#{ Layout = null; }
#using( Html.BeginForm() )
{
#Html.ValidationSummary( false, SystemStrings.ValidationSummaryMessage )
<ol class="last">
<li class="row">
#Html.LabelFor( m => m.PasswordMinimumLength )
#Html.EditorFor( m => m.PasswordMinimumLength )
#Html.ValidationMessageFor( m => m.PasswordMinimumLength, "*" )
</li>
<li class="row">
#Html.LabelFor( m => m.PasswordNeverExpires )
#Html.EditorFor( m => m.PasswordNeverExpires )
#Html.ValidationMessageFor( m => m.PasswordNeverExpires, "*" )
</li>
<li class="row">
#Html.LabelFor( m => m.PasswordExpirationValue )
#Html.EditorFor( m => m.PasswordExpirationValue )
#Html.ValidationMessageFor( m => m.PasswordExpirationValue, "*" )
#Html.EditorFor( m => m.PasswordExpirationUnit )
#Html.ValidationMessageFor( m => m.PasswordExpirationUnit, "*" )
</li>
</ol>
<div class="tc-form-button">
<input type="submit" value="Save" title="Save" class="t-button t-state-default" />
#Html.ActionLink( "Cancel", "Configuration", "System", null, new { #class = "t-button" } )
</div>
}
<script type="text/javascript">
jQuery(document).ready(function () {
$('#PasswordNeverExpires').change(function () {
setState($(this).is(':checked'));
});
});
function setState(isDisabled) {
if (isDisabled) {
// ...
}
else {
// ...
}
}
Controller:
[HttpPost]
public ActionResult Configuration( SettingsViewModel model )
{
if( !ModelState.IsValid )
{
this.PopulateViewData();
return PartialView( "_ConfigurationPartial", model );
}
else
{
// ... do save
return Json( new { Success = true }, JsonRequestBehavior.AllowGet );
}
}
If the partialView is load via ajax all my Javascript are broken. There is no second ajax submit, it is a normal post. So the partialvew is rendered without any layout informations. It seems that all the javascript references are not found. Is there any way to refresh the DOM or something else? Must i have all the javascript in the PartailView? What is the correct way for this?
Regards
This code is outside of Partial View:
<script type="text/javascript">
jQuery(document).ready(function () {
var oldvalue=$('#PasswordNeverExpires').val();
$('#PasswordNeverExpires').change(function(){
oldvalue=$(this).val();
})
});
</script>
Then I change your code in PartialView a little:
jQuery(document).ready(function () {
if($('#PasswordNeverExpires').val()!=oldvalue){
//your scripts put here.
}
});
The above code was not tested but It should work.
Your problem is that you bind the submit event to the form when the page is loaded for the first time. When you reload the form through ajax you need to rebind the submit event to your new form.
You can also live bind the event, which you do only once
$("form").live("submit", function(e) {
e.preventDefault();
if ($('form').valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function(result) {
if (!result.Success) {
$('#formcontent').html(result); // Show PartailView with Validationmessages
}
else {}
}
});
}
});
read more here