In .NET MVC there is #Url.Action() and in RoR there is url_for()
I could not find similar url building helper in angularjs.
I'm already providing everything that is needed to build url to $routeProvider so something like: $routeProvider.urlFor("MyCtrl", {id: 5}) could be nice to have.
My main goal here is to avoid hardcoded urls in viewes and other places and to avoid repeating url/routes patterns twice.
UPDATE:
Seems like it's hard to explain what i want so here is exact example of what i want:
Instead of writing this in viewes:
<a ng-href="/product/5">foo</a>
I want to write this:
<a ng-href="urlFor("ProductCtrl", {id:5})">foo</a>
So if later i decide to change path of ProductCtrl I would not have to update url in this a element.
What would be good solution for my goals?
You could try with something like the following (just came up with it) inside your main module's run() block:
app.run(function($route, $rootScope)
{
$rootScope.path = function(controller, params)
{
// Iterate over all available routes
for(var path in $route.routes)
{
var pathController = $route.routes[path].controller;
if(pathController == controller) // Route found
{
var result = path;
// Construct the path with given parameters in it
for(var param in params)
{
result = result.replace(':' + param, params[param]);
}
return result;
}
}
// No such controller in route definitions
return undefined;
};
});
This will extend the root scope of the application with the new path() function - so it can be used anywhere in the application. It uses the $route service to get the controller names and the corresponding paths, so you won't have to repeat yourself.
Example usage:
{{ path('LibraryController', { bookId : 'x', chapterId : 'y' }) }}
Live example at: http://plnkr.co/edit/54DfhPK2ZDr9keCZFPhk?p=preview
There are numerous approaches...a custom directive or ng-click to modify $location, or using a function in ng-href to parse the url from object and have it placed as href in an <a> tag.
Example using ng-href:
HTML:
<li ng-repeat="item in items">
<a ng-href="{{url(item)}}">{{item.txt}}</a>
</li>
JS:
function Ctrl($scope){
$scope.items=[
{id:1,txt:'foo'},
{id:2,txt:'bar'}
];
$scope.url=function(item){
return '#/'+item.id
}
}
Example using ng-click and $location
HTML:
<a ng-click="newPath(item)">{{item.txt}}</a>
JS:
function Ctrl($scope){
$scope.items=[
{id:1,txt:'foo'},
{id:2,txt:'bar'}
];
$scope.newPath=function(item){
$location.path('/'+item.id)
}
}
DEMO: http://jsbin.com/ovemaq/3
In one of recent project I came up to this solution it helped me to solve my needs right in time. It will be interesting to help you to improve it to fit your usecase.
1. Move routes definition to config:
...
ROUTES: {
PAGE1: '/page1/:id',
PAGE2: '/page2/:id/:name'
},
...
2. Define routes, using values from config:
app.config(['$routeProvider', 'config', function ($routeProvider, config) {
$routeProvider.when('/', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
});
$routeProvider.when(config.ROUTES.PAGE1, {
templateUrl: 'partials/page1.html',
controller: 'PageOneCtrl'
});
...
$routeProvider.otherwise({
redirectTo: '/'
});
}]);
3. Set up service to provide functionality to create urls:
services.factory('routes', function (config) {
// populates `:param` placeholder in string with hash value
var parseString = function (string, parameters) {
if (!string) return '';
if (!parameters) return string;
for (var index in parameters) {
if (!parameters.hasOwnProperty(index)) continue;
string = string.replace(':' + index, parameters[index]);
}
return string;
};
return {
getPage1Link: function (urlParams) {
return '#' + parseString(config.ROUTES.PAGE1, urlParams);
}
};
});
== Drawbacks ==
With this approach I had to define getter for each route (there were less then 5, and development speed was vital)
It appears that what you want is the ui-route directive which has just been recently added to the angularui project. There is a nice set of out-of-the-box options.
Related
I am using angularJS in my MVC application and loading a partial using ajax call in angularJS. I am using $compile to compile the html. Everything is working fine on local but it is not working on production. Unexpected error is showing. Below is the code I am using.
Angular Controller:
app.controller("mproductController", ["$scope", "mproductService", "$compile", function ($scope, mproductService, $compile) {
$scope.ShowProdUnitPop = function (val) {
mproductService.ShowProdUnitPop(val).success(function (result) {
debugger;
var snippet = angular.element(result);
$compile(snippet)($scope);
$("#dvAddProd").html(snippet);
$("#dvPopup").modal('show');
});
}
}
Angular Service:
app.service("mproductService", ["$http", function ($http) {
this.ShowProdUnitPop = function () {
var request = $http({
method: "post",
url: "/Home/GetActiveCat"
});
return request;
}}
MVC Controller:
[HttpPost]
public ActionResult AddProduct(int id)
{
ViewBag.ProductCategory = Utility.GetProd(id);
return PartialView("_AddProduct",new Product());
}
Error is throwing on this line:
var snippet = angular.element(result);
$compile(snippet)($scope);
$("#dvAddProd").html(snippet);
$("#dvPopup").modal('show');
It is working on local but not on production. Please help.
Seems like you are using bundling and minification enabled on production server. That is breaking you controller code while you are utilizing that controller and its necessary to have inline array annotation of DI while minifying JS files.
Most possible answer would be you are using $compile by injecting it to controller and using it, That's fine. It seems like you had not followed inline array dependency injection inside your controller.
Also you need to attach compile element to your dvAddProd element, rather than updating html, appending html to DOM will never make angular angular binding on that element. Looks like this code shouldn't be working on any of the environment if it has angular bindings on it.
Controller
app.controller('someCtrl', ['$scope', '$http', '$compile',
function($scope, $http, $compile) {
//you should have controller in this way that would fix your issue
//note I'm using inline array notation of DI
$scope.ShowProdUnitPop = function(val) {
mproductService.ShowProdUnitPop(val).success(function(result) {
debugger;
var snippet = angular.element(result);
var compiledSnippet = $compile(snippet)($scope);
$("#dvAddProd").append(compiledSnippet); //should attach compile element
});
}
}
]);
Visit this link for more Information
i have content listed in a div and i have a dropdown with various options to order and filter that content.
I'm using ajax to filter/order that content and is working but i use other php page with the content i want on the div that has the content, like this
function order(str){
$.post("order_products.php",
{
q: str,
},
function(data, status){
document.getElementById("txtHint").innerHTML = data;
});
}
What i wanted was to instead of putting the code (data) to change in another page just for that, i could put that code inside a class php function that i have.
<?php
class products{
function list(){
blablabla
}
That way i would "save space" and organize everything, considering that i have many other things to order/filter but i don't know to to make the ajax request to that function, or if it's possible without having a page in between and then get the response from the function and put it on the div.
You can do this using Laravel by setting up a route to a function that will do the ordering. Please note I've made a lot of assumptions in the following answer as I can't see all your code and have made it quite general, please adjust the code to your project or provide more details of your code if you don't understand the answer fully.
routes.php
Route::post('products/order', [
'as' => 'products.order',
'uses' => 'ProductsController#orderProducts'
]);
Your view (assuming you're using blade)
$txtHint = $('#txtHint'); // grab the reference to txtHint dropdown
$.post( '{{ route("products.order") }}', // hit our route
{
q: str,
},
function(data, status){
$txtHint.empty(); // clear the dropdown
// loop through the data and assign each element to the dropdown
$.each(data, function(value, key) {
$txtHint.append($("<option></option>")
.attr("value", value)
.text(key));
});
});
ProductsController.php
public function orderProducts()
{
$orderBy = \Input::get('q');
return \Products::lists('name', 'id')->orderBy($orderBy);
}
For outside of a framework just change the url to your php file and add in a data attribute for the method you require to be fired from the file.
$.post( 'products.php', // hit our route
{
action: 'order',
q: str,
},
...
Then in products.php you'd do something like this
if(isset($_POST['action']) && !empty($_POST['action'])) {
$action = $_POST['action'];
switch($action) {
case 'order' : order();break;
case 'otherFunction' : otherFunction();break;
}
}
function order()
{
// order logic here
// get $_POST['q']
// do your ordering
// return data as json
}
See here for similar question: using jquery $.ajax to call a PHP function
I am trying to create the name of a function dynamically:
I set the name in razor code:
# { name="bob"; }
Then I try to create the javascript function, but the syntax is wrong.
function #name#:_onActivate() {
.....
}
How can I use #name in the name of the function?
I know im late to the party, but i had the same question and came up with this..
Use the #() Razor syntax:
function #(Model.Name)_Init()
{
.....some code...
}
Works like a charm
(http://haacked.com/archive/2011/01/06/razor-syntax-quick-reference.aspx/)
This was found to work in writing Javascript function names dynamically
#{
#:function
#name<text>_onActivate() {
....
}
</text>
}
There is also another option:
#{
var functionStart = "function " + name + "_onActivate()";
}
#functionStart
{
...
}
This method doesn't make so much mess in Visual Studio in some cases.
This worked well for me:
#{
var someFuncName = "someFuncName";
var someFuncName2 = "someFuncName" + Model.type;
var someDynamicFunc = Model.funcName;
var someFuncNameComplete = "functionHelper()";
}
<script type="text/javascript">
function #someFuncName#{<text>()</text>}{
...
}
function #someFuncName2#{<text>(data)</text>}{
...
}
function #someDynamicFunc#{<text>()</text>}{
...
}
function #someFuncNameComplete{
...
}
</script>
If your JavaScript is in the view (rather than its own js file - though I'm not saying I think this is advisable), you can try wrapping your JS code in <text> tags, so that the parser switches back to HTML mode (vs C# mode).
Another alternative may be not naming the function dynamically but instead passing in the name as a parameter - but whether that would work may depend on your design/implementation.
First you can set the function name in a string with below structure:
#{
string name = "bob";
string funcTxt = name + "_onActivate()";
}
Then you can use this string into your function declaration:
<script>
function #funcTxt {
...
}
</script>
I am trying to extend the default jQuery UI 1.8.16 autocomplete plugin to do a few things:
Check an array of values and not just the label/value when searching for a match
Render the menu item in a different pattern based on a category on the item
So to do this I began to work through the code. I am very new to writing a widget/plugin and so I am wondering if someone can help me understand this part. I am currently trying to do the following:
$.widget("custom.advautocomplete", $.ui.autocomplete, {
filter: function (array, term) {
var matcher = new RegExp($.ui.autocomplete.escapeRegex(term), "i");
return $.grep(array, function (value) {
return matcher.test(value.label || value.alldata || value);
});
},
_renderMenu: function (ul, items) {
var self = this,
currentCat = "";
$.each(items, function (index, item) {
if (currentCat != item.category) {
ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
currentCat = item.category;
}
self._renderItem(ul, item);
});
},
_renderItem: function (ul, item) {
return $("<li></li>")
.data("item.autocomplete", item)
.append($("<a></a>").text(item.alldata))
.appendTo(ul);
}
});
The _renderMenu code is pretty much taken directly from the example in the documentation. The _renderItem code is working in this example as well. What is not working is the filter code. I did notice that the filter code is defined in the jQuery library like so:
$.extend($.ui.autocomplete, {
escapeRegex: function (value) {
return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
},
filter: function (array, term) {
var matcher = new RegExp($.ui.autocomplete.escapeRegex(term), "i");
return $.grep(array, function (value) {
return matcher.test(value.label || value.value || value);
});
}
});
From what I've gathered so far, this is extending the jQuery autocomplete which is defined earlier by a call to $.widget("ui.autocomplete"... But why is this handled this way? Why are these two functions not handled inside the widget definition like everything else? I noticed that if I extend ui.autocomplete in the same manner as it is done by the base widget, then I can get the filter override to work. I would like to extend the control and include the new filter at the same time so I can keep my code a bit cleaner but I don't understand why it is being done like this. Is there a benefit that I'm missing?
CakePHP URL query parameters are not done in a standard fashion e.g. the params are /param1:value1/param2:value2 instead of ?param1=value1¶m2=value2
This means that the javascript location.search does not return a value.
There is a getQueryParams JQuery plugin that does what I want using location.search
I have had to modify this to use
var pairs = location.pathname.split('/');
instead of
var pairs = location.search.substring(1).split('&');
However this now includes everything except the host in the variable pairs. So I have to check for a ':' to see if it is a parameter.
This works - but is there a better (more Cake like) way of doing it? I don't want to improve on the JQuery plugin (e.g. Regex), I want to find a better way to integrate the plugin with CakePHP.
Upddate: I've removed the rest of the JQuery code as I'm happy with the jquery code, my issue is with fitting it more with cake
Is there some 'Cake like' way of removing the path to your app, the model and the controller from location.pathname so that you end up what you would normally get from location.search?
Since you're searching for a particular parameter, you can use a regular expression:
$.getQueryParam = function (param) {
var re = new RegExp(param+':([^\/]+)');
var matches = location.pathname.match(re);
if (matches.length) {
return matches[1];
}
return undefined;
}
So it appears there isn't a better way of doing it. Here is the javascript for reference:
// jQuery getQueryParam Plugin 1.0.1 (20100429)
// By John Terenzio | http://plugins.jquery.com/project/getqueryparam | MIT License
// Modified by ICC to work with cakephp
(function ($) {
// jQuery method, this will work like PHP's $_GET[]
$.getQueryParam = function (param) {
// get the pairs of params fist
// we can't use the javascript 'location.search' because the cakephp URL doesn't use standard URL params
// e.g. the params are /param1:value1/param2:value2 instead of ?param1=value1¶m2=value2
var pairs = location.pathname.split('/');
// now iterate each pair
for (var i = 0; i < pairs.length; i++) {
// cakephp query params all contain ':'
if (pairs[i].indexOf(':') > 0) {
var params = pairs[i].split(':');
if (params[0] == param) {
// if the param doesn't have a value, like ?photos&videos, then return an empty srting
return params[1] || '';
}
}
}
//otherwise return undefined to signify that the param does not exist
return undefined;
};
})(jQuery);