How should I handle Asp.net MVC URLs in javascript calls? - javascript

I am attempting to write a javascript heavy portion of my Asp.net MVC Web App (this portion of the website is a RIA using Extjs). However, I have come up to a standstill at the correct way to handle URLs in the javascript.
For example, right now I have an Ajax call to the List action in the ObjectsController, which resides in the Reading area. The List action takes a parameter of documentId (int). As of right now, this maps to /Reading/Objects/List since I have no changed routing yet (the site is too young at the moment to finalize routes). Normally in a view, to put this URL in a string I would do #Html.Action("List", "Objects", new { area = "Reading", documentId = 3).
However, this doesn't work when dealing with javascript, since javascript isn't parsed by a viewengine.
To get around this, I have a very small view that returns javascript constants, such as URLs, that is loaded prior to my main application's js files. The issue is that I can't call Html.Action for this action because at constant creation time I (obviously) do not know what documentId the ajax calls are going to be, and if you exclude documentId from the Html.Action call an exception occurs. The documentId could change during the normal workflow of the application.
How do I handle this? I don't want to hardcode the URL to /Reading/Objects/List because if I change my routing for this (for a more user friendly json API), or this web app isn't hosted on the root of the domain, the URL will no longer be valid.
How does everyone else handle MVC URLs in their javascript calls?

Here's a safe technique that I've been using. Even if your route changes, your JavaScript will automatically conform to the new route:
<script>
var url = '#Url.Action("List", "Objects", new { area = "Reading", documentId = "_documentId_")';
var id = 100;
var finalUrl = url.replace('_documentId_', id);
</script>
"_documentId_" is essentially a dummy placeholder. Then inside my JavaScript, I replace "_documentId_" with the proper id value once I know what it is. This way, regardless of how your route is configured, your URL will conform.
Update: Dec 20
I just saw this interesting blog post. The author built a library that allows you to build routes inside of your JavaScript file with intellisense support in VisualStudio.
http://weblogs.asp.net/zowens/archive/2010/12/20/asp-net-mvc-javascript-routing.aspx

Personally I use unobtrusive javascript and avoid mixing markup with javascript. AJAX calls are normally triggered by clicking on some buttons or links:
#Html.ActionLink("click me", "List", "Objects",
new { area = "Reading", documentId = 3 }, new { id = "foo" })
and then in a separate js file I would attach and handle the onclick event (example with jquery):
$(function() {
$('#foo').click(function() {
$('#resultDiv').load(this.href);
return false;
});
});
As you can I didn't need to use any hardcoded URL in my javascript file. URLs should always be handled by the routing engine and generated with html helpers.
If it was a <form> instead of a link I would simply handle the onsubmit event (the same way) and use the form's action attribute to get the URL.
UPDATE:
After pointing out in the comments section that the documentId is known only at client-side you could do this:
#Html.ActionLink("click me", "List", "Objects",
new { area = "Reading" }, new { id = "foo" })
And then:
$(function() {
$('#foo').click(function() {
$('#resultDiv').load(this.href, { documentId: '123' });
return false;
});
});

Turns out, this was all solved by using Url.Action() instead of Html.Action(). Url.Action() is (so far) allowing me to generate URLS without all of the parameters. I am assuming that this only works when the route does not specify the parameters in the target URL itself.

Related

update property in object loaded from js file - javascript

im very new in javascript and im trying to implement new function into existing project.
The background -
In html file i can see:
<script src="js/users.js" type="text/javascript"></script>
<script src="js/seating-plan.js"></script>
File seating-plan.js access object stored in users.js that contains:
var users = [
{"id": 1, "name":"first", "surname":"surname"},
{"id": 2, "name":"second", "surname":"surname"}
]
My question is: Can I change a property loaded from such object so the file will be changed? If so how to do it? I read some posts how to change object property but such approach will only changed already loaded object, not rewrite js file so i would be stuck with same result after refresh. Im guessing I will need to change how I initially load array object but if you know how to do it with my original aproach or how to do the second option in easy way please assist.
EDIT: localhost internal purpose (shared folder)
The approach I mentioned:
// Start of jQuery ready function
$(function () {
//can already access object
for (var i = 0; i < users.length; i++) {
//example of how i tried to rewrite property
if (users[i].id === 1){
users[i].name = "NewName";
}
}
.
.
.
If you want to change data on the server, then you must send an instruction to the server to change the data.
It would be a serious security risk if, by default, any web browser could change data on any HTTP server. The Google homepage would be vandalised on a second-by-second basis if that were possible!
Typically this will be a POST request (which you could make with a <form> or with XMLHttpRequest/fetch).
You then need server-side code (written in the language of your choice) which will update the data.
Typically you will want to store the data in a database and generate users.js (although changing it to JSON would probably be a better idea) on demand.

Writing handlebars in Ember

I have a small app which is multilingual. The text of the app is coming from the server. The text can be in different language. For now lets stick to Swedish and English. User can switch between languages. What I am trying to do is when the app loads get the text in both languages and store it in models in Ember.
Then I am trying to write a Handlebars helper which will allow me to switch between the languages. For example the data from the server I am getting is in this form.
langData: {
sv_SE: {
start_page: "..."
},
en_US: {
start_page: "..."
}
My model for this is language.js which is like this
App.Language = DS.Model.extend({
language: DS.attr('string'),
start_page: DS.attr('string'),
});
In my handles bar I am trying to do something like this
Ember.Handlebars.helper('__', function(key, lang, options) {
// get the text specified for the language. A function which will check the language model and
// fetch data for the particular key from it
});
I am calling the handles bar like this
{{__ start_page en_US}}
What I am thinking that, this will return me start page text in English language.
Is this the right way? If not what will be the right way? Also, I will have different text strings which will be used in different hbs files.
I've used Ember-I18n for a project successfully. It gives you a place to put your translations outside a model. I then load the current language based on a cookie or a setting on the user model. Switching to a different language does require a full-page reload for me, but you might be able to get it to work without that.

base link and search api

I am attempting to query a database through an API which I don't fully understand. I have been sent an example of the API being used with a keyword search form. The form is an html file and uses jquery return JSON documents, format items into an array array, and display.
I tried to build the design of my application and manipulate the form to work within my pages. The file the uses the API requires that the a base link be used.
<base href="{{app_root}}">
If I remove this base link my functionality of the search is lost. If I use the base link all of presentation and CSS is lost.
I thought maybe I could change the base link dynamically when I needed to call the search file with:
<script type="text/javascript">
function setbasehref(basehref) {
var thebase = document.getElementsByTagName("base");
thebase[0].href = basehref;
}
//setbasehref("{{app_root}}");
setbasehref("{{app_root}}");
</script>
Then use setbasehref() to change it back to my original base link, but that didn't work.
I'm new to javascript and JSON, and I'm not entirely sure what app_root is doing. Any thoughts?

Expression Engine Extension Development - Add custom javascript to rendered entry

I'm working on an extension and one of the options available in the settings needs a custom javascript to be added to the document head when rendered. The problem I am having is with the parsing order. (There may also be a better way of doing the include too)
I am using the channel_entries_tagdata hook.
Inside this, once the settings are processed, I am doing the following:
// Add the required javascript
$jscript = "
<script type="text/javascript">
/*! etc......
</script></head>
";
// Add js
$tagdata = str_replace("</head>", $jscript, $tagdata);
I would like to be able to just keep my javascript in a separate file and include it somehow by reference, but I don't know how to do that at this stage.
The other issue I am running into is the parsing order of the EE variables. Inside the javascript, I am using the variables from the $tagdata. Something like this:
$.post("URL", { channel: "{channel}", entryId: "{entry_id}", urlTitle: "{url_title}", lastSegment: "{last_segment}", editDate: eo.editDate, field: eo.eleName }, function(data){...
How would I call/use the EE variables in this case?
Elaborated...
This extension is for the following:
In the Addons -> Extensions from the control panel, they will activate the extension. In the 'Settings' for that extension, they will be able to authorize, by Channel, the members or groups that can 'edit' entries in that channel.
The extension, after checking permissions, edits each custom field type before it is rendered and wraps it in a class element. The JavaScript file is for this functionality next. When that element is clicked, a modal is opened which will contain the custom field type as well as the channel/entry information, so it can save the field once edited.
Could you let the script in the <head> be a generic function and pass variables to it by calling it from inside your channel entries?
<head>
...
<script>
function W3bGuy_function(channel, entry_id, last_segment) {
...whatever...
}
</script>
</head>
<body>
...
{exp:channel:entries}
some action triggers: W3bGuy_function('{channel}', '{entry_id}', '{segment_3}');
{/exp:channel:entries}
...
channel_entries_tagdata contains the raw template code pulled from within each {exp:channel:entries} loop, and then has another variable ($row) which is an array of the actual data for that entry. (As per the docs.)
So first, you'll have to make sure your entire page template is within your Channel Entries loop if you want to add JS to the <head> in this manner - and that may not work if your <head> is inside an embed.
Second, I'd suggest dumping the $row data that's passed via that hook, to see if you can extract your data in your returned JS from there.
Hope that helps.

webOS/Ares : read JSON from URL, assign to label

I've used the webOS Ares tool to create a relatively simple App. It displays an image and underneath the image are two labels. One is static, and the other label should be updated with new information by tapping the image.
When I tap the image, I wish to obtain a JSON object via a URL (http://jonathanstark.com/card/api/latest). The typcial JSON that is returned looks like this:
{"balance":{"amount":"0","amount_formatted":"$0.00","balance_id":"28087","created_at":"2011-08-09T12:17:02-0700","message":"My balance is $0.00 as of Aug 9th at 3:17pm EDT (America\/New_York)"}}
I want to parse the JSON's "amount_formatted" field and assign the result to the dynamic label (called cardBalance in main-chrome.js). I know that the JSON should return a single object, per the API.
If that goes well, I will create an additional label and convert/assign the "created_at" field to an additional label, but I want to walk before I run.
I'm having some trouble using AJAX to get the JSON, parse the JSON, and assign a string to one of the labels.
After I get this working, I plan to see if I can load this result on the application's load instead of first requiring the user to tap.
So far, this is my code in the main-assistant.js file. jCard is the image.
Code:
function MainAssistant(argFromPusher) {}
MainAssistant.prototype = {
setup: function() {
Ares.setupSceneAssistant(this);
},
cleanup: function() {
Ares.cleanupSceneAssistant(this);
},
giveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
},
jcardImageTap: function(inSender, event) {
//get "amount_formatted" in JSON from http://jonathanstark.com/card/api/latest
//and assign it to the "updatedBalance" label.
// I need to use Ajax.Request here.
Mojo.Log.info("Requesting latest card balance from Jonathan's Card");
var balanceRequest = new Ajax.Request("http://jonathanstark.com/card/api/latest", {
method: 'get',
evalJSON: 'false',
onSuccess: this.balanceRequestSuccess.bind(this),
onFailure: this.balanceRequestFailure.bind(this)
});
//After I can get the balance working, also get "created_at", parse it, and reformat it in the local time prefs.
},
//Test
balanceRequestSuccess: function(balanceResponse) {
//Chrome says that the page is returning X-JSON.
balanceJSON = balanceResponse.headerJSON;
var balanceAmtFromWeb = balanceJSON.getElementsByTagName("amount_formatted");
Mojo.Log.info(balanceAmtFromWeb[0]);
//The label I wish to update is named "updatedBalance" in main-chrome.js
updatedBalance.label = balanceAmtFromWeb[0];
},
balanceRequestFailure: function(balanceResponse) {
Mojo.Log.info("Failed to get the card balance: " + balanceResponse.getAllHeaders());
Mojo.Log.info(balanceResponse.responseText);
Mojo.Controller.errorDialog("Failed to load the latest card balance.");
},
//End test
btnGiveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
}
};
Here is a screenshot of the application running in the Chrome browser:
In the browser, I get some additional errors that weren't present in the Ares log viewer:
XMLHttpRequest cannot load http://jonathanstark.com/card/api/latest. Origin https://ares.palm.com is not allowed by Access-Control-Allow-Origin.
and
Refused to get unsafe header "X-JSON"
Any assistance is appreciated.
Ajax is the right tool for the job. Since webOS comes packaged with the Prototype library, try using it's Ajax.Request function to do the job. To see some examples of it, you can check out the source code to a webOS app I wrote, Plogger, that accesses Blogger on webOS using Ajax calls. In particular, the source for my post-list-assistant is probably the cleanest to look at to get the idea.
Ajax is pretty much the way you want to get data, even if it sometimes feels like overkill, since it's one of the few ways you can get asynchronous behavior in JavaScript. Otherwise you'd end up with code that hangs the interface while waiting on a response from a server (JavaScript is single threaded).

Categories

Resources