BackboneJS - pushState and Images - backbone.js

I'm running a backbone project on my localhost. I have set pushState: true on my main router. On my index.html i have multiple views which contain images. Those views render a html-template in which the path to the image is defined:
<div>
<img src="images/image1.jpg">
</div>
Now, when clicking a specific link I have defined in my menu view:
secondpage: function(event) {
event.preventDefault();
var newRouter = new Backbone.Router();
var route = '/secondpage';
newRouter.navigate(route, {trigger: true});
},
After this, my URL gets changed into localhost/secondpage, so far so good, but now, my view on the second page, which also contains an image (lets say image2) is not getting displayed and I get the error http://localhost/images/image2.jpg not found...
how to solve this?

the path to your image should be something like /images/image2.jpg
<div>
<img src="/images/image1.jpg">
</div>

Related

How ng-include builds url in Angular 1.5?

Am using Angular1.5 with asp.net mvc.
By default when my application loads it points to http://domainname/components/Webui/Home/Instructions
Home - controller,
Instructions - Action
public class HomeController : Controller
{
public ActionResult Instructions()
{
return View();
}
}
above url returns an html
which contains below
<div>
<div ng-include src="app/Layout/shell/Shell.html" class="height100">
</div>
</div>
But it is not able to download the shell.html.
it is generating the wrong url as below
http://domainname/components/Webui/Home/app/layout/shell/Shell.html
In the above url why Home is coming ?
My folder structure is
app
layout
shell
Shell.html
Controllers
Views
Home
Index.cshtml
Instructions.cshtm
Please correct me if anything is wrong.
You are using a relative path from index. Append forward slash to path to traverse from project root:
ng-include src="/app/Layout/shell/Shell.html

Angularjs $location.url or path do not update the view in ng-view

I am trying to use $location.url or path to update a view in ng-view, but unsuccessfully.
The controller is:
eventsApp.controller('MenuController',
function($scope, $location) {
$scope.createEvent = function(){
$location.url('/newEvent');
};
}
);
The function is simply called in a ngClick event located within the controller:
<li>Create Event</li>
And the routing is:
angular.module('eventsApp', ['ngResource', 'ngRoute'])
.config(function($routeProvider) {
$routeProvider.when('/newEvent',
{
templateUrl:'templates/NewEvent.html',
controller: 'EditEventController'
});
});
Of course, if i use the href of the anchor tag, it works fine; but if I want to do something more complex in the called function, I can't.
I looked in the Network section of the browser tools and I could see that the template has been fetched. But the neither the url in the address bar is updated, or the view is updated (it actually becomes blank).
I am using Apache as a web server, if this thing could be useful in understanding the cause of this issue.
I suspect it is reloading the page due to the href="#". If you need to stick with an anchor tag even though you're not using href, try removing the # like so:
<li>Create Event</li>
However, there could be some fallback if you need to support IE users. If that is the case, you could switch out the anchor tag for a button.
<li>
<button type="button" ng-click="createEvent()">Create Event</button>
</li>

How to navigate in multi pages Web App with AngularJs

I've a simple Web App with 2 html pages, see below :
index.html
<div ng-controller="ThirdCtrl">
<h2> {{titre}}</h2>
<form>
<input type="text" ng-model="test"><br>
<button ng-click="toSend()">OK</button>
</form>
</div>
indexv2.html
<div ng-controller="ThirdCtrl">
<h2>{{titre}}</h2>
{{test}}
</div>
app.js
var app = angular.module('myApp', []);
app.controller('ThirdCtrl', function($scope, $window){
$scope.titre = "ThridCtrl";
$scope.test = "";
$scope.toSend= function()
{
$window.location.href = "http://localhost:10080/ClientAngular/indexv2.html";
}
});
I'm trying to send data when button is clicked to an other page, redirection is working to indexv2.html but I can't to get my data "test". The data "titre" is weel displayed but not "test".
Do you know how can I to get a data after ng-click function ?
Thanks in advance.
I think he wants to move data between pages - I had a similar situation which I'm still working on - just that he is not showing us enough code. Say you get a visitor's input data on a web form and need to display that data to another web page where you then send to a back-end server from the subsequent page, but all this using angularjs, how do you get that done?
Further you say get the value for 'titre' right on the subsequent page but not for 'test'. I think it is because 'test' has not been set - except otherwise you are not showing enough code.

What's the correct way to update model and URL?

Suppose I've got following elements on the same page:
Filters panel (something similar to
http://www.imdb.com/search/name)
Items based on filter options
I want to implement following logic:
URL should contain applied filter data in path (/appliedOptionA/appliedOptionB)
When user opens site app get filter data from URL, updates filter
panel and items panel
When user changes filters app updates URL and refreshes items
First idea: configure ng-router, get filter data as param, convert data to model, construct filters panel, load items, construct items panel. Main problem: how should it work when user changes filter? If I'll update URL it will trigger same controller with different param and repeat the process -> get filter data as param, convert data to model, construct filters panel, items and so on. I don't need it - my model and UI is already up to date.
My question:
what is the correct way to keep model and URL synchronized? Should URL manage model (ng-route) or model changes manage URL (how?) ?
My recommendation would be to use angular-ui-router and use that instead of ng-router since angular-ui-router gives you much more flexibility and is, in my opinion, easier to use.
Given your "Main Problem", it sounds like you want to have an single application controller (let's refer to this as the "appCtrl") handle all changes and make the necessary changes to its model. The main drawback with this approach is it forces you to the maintain the "filteredItems" model within the appCtrl, which is not fun and could lead to unnecessary headaches. An easier way to approach it would be to have another controller deal with those changes to the url, rather than the appCtrl itself. That being said, let me show you how you could accomplish this.
*WARNING: The post is very long! (but hopefully helpful) *
Because of this, I have also created a demo of everything I am about to discuss in case you "just want teh codez":
http://plnkr.co/edit/IMZZTE?p=preview
http://run.plnkr.co/fUddH83P2orHUsWj/#/
Creating the application
Since we are creating a brand new application, let's start with creating and configuring the application.
NOTE: Remember to load in the angular-ui-router script into your application and add the dependency.
app.js
var myApp = angular.module('myApp', ["ui.router"]);
Now that we have the app, let's add it to the index.html using the ngApp directive.
index.html
<body ng-app="myApp"></body>
With the application in place, let's look at the issues you addressed in your question:
URL should contain applied filter data in path (/appliedOptionA/appliedOptionB)
When user opens site app get filter data from URL, updates filter panel and items panel
When user changes filters app updates URL and refreshes items
What you want to do is create a state. Essentially, a state is activated/deactivated based on the url pattern. Given a particular pattern, you can tell the application how to act (using a controller) and what to look like (using a template). Before continuing, make sure to reference url routing, which will help with url patterns.
We need to:
give the state a name ("filteredItems")
provide a url pattern
provide a controller
provide a templateUrl
Let's configure the application, as well as:
provide a default url route in the event the url does not exist
add a default home state
add the filteredItems state
app.js
myApp.config(function($stateProvider, $urlRouterProvider){
// default url - used if url pattern is not recognized
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', { // default state
url: '/'
})
.state('filteredItems', { // give the state a name
url: "/:appliedOptionA/:appliedOptionB", // provide the url pattern
controller: 'FilteredItemsCtrl', // controller to use for this state
templateUrl: "filteredItems.html" // url for the template
});
});
We have now configured the state's url to take parameters (appliedOptionA and appliedOptionB). Using $stateParams service, we can create a filteredItemsCtrl and have that be responsible for filtering out the data.
But before we do that, we should create a service. When using Angular, AVOID using controllers to maintain data since they can be created/destroyed. Best practice is to use services instead. That being said, let's create an amazing itemService:
itemService.js
angular.module('myApp').factory('itemService', itemService);
function itemService (){
var items = {
appliedOptionA: [ 'Item 1', 'Item 2', 'Item 3' ],
appliedOptionB: [ 'Item 4', 'Item 5', 'Item 6' ]
};
function getItems (){
return items;
}
return {
getItems: getItems
}
}
Gorgeous right? I am glad you think so too!
Now that we have a service, we can create the FilteredItemsCtrl. Let's also inject our newly created itemService into the FilteredItemsCtrl, so its able to access the data.
NOTE: Remember to inject the $stateParams service as a dependency!
filteredItemsCtrl.js
angular.module('myApp').controller('FilteredItemsCtrl', filteredItemsCtrl);
filteredItemsCtrl.$inject = ['$stateParams', '$scope', 'itemService'];
function filteredItemsCtrl ($stateParams, $scope, itemService){
var items = itemService.getItems(); // get items from service
// filter items
$scope.appliedOptionA = items.appliedOptionA.filter(function (item){
if ($stateParams.appliedOptionA){
return item.toLowerCase().indexOf($stateParams.appliedOptionA) > -1 ||
item.indexOf($stateParams.appliedOptionA) > -1
}
});
$scope.appliedOptionB = items.appliedOptionB.filter(function (item){
if ($stateParams.appliedOptionB){
return item.toLowerCase().indexOf($stateParams.appliedOptionB) > -1 ||
item.indexOf($stateParams.appliedOptionB) > -1
}
});
}
When we defined our state's url, we set it up like: url: "/:appliedOptionA/:appliedOptionB". This means the $stateParams will be populated with appliedOptionA, appliedOptionB properties. Also this means every time the URL changes, a new filteredItemsCtrl is created, thus, you do not have to maintain the application's model!
Before we forget, we also need to create a template for this state.
filteredItems.html
<div>
<div>
<span>Filtered Option A Items:</span>
<ul>
<li ng-repeat="optionItemA in appliedOptionA">
{{ optionItemA }}
</li>
</ul>
</div>
<div>
<span>Filtered Option B Items:</span>
<ul>
<li ng-repeat="optionItemB in appliedOptionB">
{{ optionItemB }}
</li>
</ul>
</div>
</div>
With the state, filteredItemCtrl, and view created, all that is left is to include the ui-view directive, in order for the state's template to properly appear:
index.html
<body ng-app="myApp"
ng-controller="MyAppCtrl">
<div>
<div>
Applied Option A: <input ng-model="appliedOptionA" />
</div>
<div>
Applied Option B: <input ng-model="appliedOptionB" />
</div>
<button type="button"
ng-click="filter()">Search</button>
</div>
<!-- where the state template goes -->
<div ui-view></div>
</body>
That is all there is to it! Sorry about the long post, but hopefully you found this informative! Please let me know if you have any issues!
When you want the search keywords to be reflected in the URL (so they can be bookmarked and addressed directly), I would recommend not to force every change of the model (which would be after every keystroke), but wait for a submit of the form. Then you rewrite the URL (via $location.path and/or $location.search) and the ngRoute will kick in.
NgRoute will (re)load/refresh the page as if it were the first time, and that matches the idea of a directly addressable page that includes search keywords. I would not recommend getting the data as soon as you get the submit, because as you stated, ngRoute will refresh the page.

Meteor using backbone for routing always loading one route

I'd appreciate any insight into whether this is a "correct" way of doing things and also what's causing the error I'm seeing.
I have added backbone to my base meteor install meteor add backbone
Then I set up a router.js file as follows (just showing 2 pages as example);
var Router = Backbone.Router.extend({
routes: {
"": "index",
"help": "help",
...
},
index: function() {
Session.set('currentPage', 'homePage');
},
login: function() {
Session.set('currentPage', 'loginPage');
},
...
Then for the pages I have html files with templates looking something like this...
<template name="homepage">
{{#if route}}
You're at the Home Page!
{{/if}}
</template>
Then for the main page I have an html file that contains the following;
<body>
...
{{> homepage}}
{{> loginpage}}
{{> helppage}}
...
</body>
This works for all of the pages except the one designated 'homepage', this template is always rendered regardless of where I am on the site. e.g. myapp/ as the root page just displays the homepage template, but myapp/loginpage displays the loginpage template and the homepage template. So every single page displays the contest of the homepage template.
Any insight? (or better ways to structure).
Thank you
Finally a question that I can answer because it's what I've been doing 60 hours a week at work for the last few months :-P
You're doing a few things wrong here, and they're extremely simple fixes that will get you fired up in no time.
First thing
You need to instantiate all of your routers and then initialize Backbone.pushState().
// This is just how I have been doing it, there are many correct ways.
var LoadedRouters = {
Module1 : new Module1Router(),
Module2 : new Module2Router(),
...
};
// After all routes instantiated, we tell Backbone to start listening.
Backbone.history.start({ pushState : true, root : "/" });
It's very important that you set the root property correctly, or else weird things start happening.
Second thing
You HAVE TO list your routers from the most-specific to the least-specific, top-down. The URL structure you outlined above will ALWAYS match the first route and trigger it.
routes : {
"help/phones/newservice" : HandleNewServiceHelp(),
"help/phones/oldservice" : HandleOldServiceHelp(),
"help/phones(/:any)" : HandleAllPhoneHelp(),
"help(/:any)" : HandleAllHelp(),
"" : HandleAllUnmatchedRoutes()
};
Backbone.router can be a tricky thing to learn.

Resources