BackboneJS + Codeigniter pushState true not working - codeigniter

So I have this Backbone App where I use Codeigniter for the Backend. For some reason, pushState:true does not work.
So, my main.js of my backbone app has this:
Backbone.history.start({ pushState: true, root: App.ROOT });
My app.js has this:
var App = {
ROOT: '/projects/mdk/'
};
and my navigation module, which renders the menulinks, each item has this:
this.insertView(new ItemView({
model: new Navigation.ItemModel({
href: App.ROOT + 'home',
class: 'home',
triggers: 'home',
route: this.route
})
}));
and the model for it:
Navigation.ItemModel = Backbone.Model.extend({
defaults: {
href: '',
text: '',
triggers: [],
route: ''
}
});
All I get from this is "Page not found"...
Add: When I in the view change it to href:'#news' - it works, but it dont really makes sense...
Anyone who knows the issue here?

From the documentation (http://backbonejs.org/#History):
Note that using real URLs requires your web server to be able to
correctly render those pages, so back-end changes are required as
well. For example, if you have a route of /documents/100, your web
server must be able to serve that page, if the browser visits that URL
directly.
The problem is that your server isn't responding to whatever URL your app is on. For every URL that your Backbone app can reach, your server MUST return a valid HTML page (contianing your Backbone app).

ok I found a solution by myself:
I made this hack:
$(document).on('click', 'a:not([data-bypass])', function (evt) {
var href = $(this).attr('href');
if (href && href.indexOf('#') === 0) {
evt.preventDefault();
Backbone.history.navigate(href, true);
}
});
and then I made:
href: '#home',
That solved the problem, now evereythings runs fluently..

Related

Ideal way to ajax load a view in Drupal 8 with contextual filters

I have a taxonomy called category.
I have a menu with links to each of these taxonomy items.
The Taxonomy page for each of these items has that menu and also contains a view which uses a contextual filter from the URL to filter the content of the view to content with that taxonomy term.
I wanted to Ajax load the view content when one of these menu items is clicked.
I've been able to achieve the desired result by enabling ajax on the view and using the following JavaScript.
(function ($) {
Drupal.behaviors.course_browser = {
attach: function (context, settings) {
// should only be one menu, but guard against 0, and avoid the if statement.
context.querySelectorAll(".menu--categories").forEach((menu) => {
// for each link in the menu
menu.querySelectorAll(".nav-link").forEach((link) => {
// on click
link.addEventListener("click", (event) => {
event.preventDefault();
// fetch the taxonomy term id from menu link
let tid = event.target.dataset.drupalLinkSystemPath.replace(
"taxonomy/term/",
""
);
// make the ajax call
$.ajax({
url: "/views/ajax",
type: "post",
data: {
view_name: "course_browser",
view_display_id: "block_1",
view_args: tid,
},
success: (response) => {
response.forEach((action) => {
// the response contains a number of commands; I'm not sure
if (
action.command === "insert" &&
action.method === "replaceWith"
) {
let viewElement = document.querySelector(VIEW_SELECTOR);
// update the html of the course browser
viewElement.innerHTML = action.data;
// update the url in the browser
window.history.pushState("", "", event.target.href);
// seperate function to adjust my page title
updatePageTitle(event.target.textContent);
// call drupal behaviours passing context to ensure all the other js code gets a chance to manipulate the new content
Drupal.attachBehaviors(viewElement);
}
});
},
error: function (data) {
console("An error occured fetching the course browser");
},
});
});
});
});
},
};
})(jQuery);
I'm looking for feedback on my approach here; my main concern at the moment is the way I handle the response. When I look at the response I receive something like that shown below:
0: {command: "settings", settings: {…}, merge: true}
1: {command: "add_css", data: "<link rel="stylesheet" media="all" href="/core/modules/views/css/views.module.css?qcog4i" />↵"}
2: {command: "insert", method: "append", selector: "body", data: "<script src="/core/assets/vendor/jquery/jquery.min…/js/modules/views/ajax_view.js?qcog4i"></script>↵", settings: null}
3: {command: "insert", method: "replaceWith", selector: ".js-view-dom-id-", data: "The HTML"}
As you can see, I'm manually handling the response by cherry picking the part I want and replacing the HTML of view. Based on what I've seen around Drupal, I think there should be something I can pass this response to that handles it automatically. When I look at the window object of the browser, I can see Drupal.AjaxCommands which looks like it was designed to handle this, but I'm not sure how I should be using this.
I also note that in the case I can simply pass this response to something to have those AjaxCommands executed, the selector ".js-view-dom-id-" isn't right. So I could tweak the response before I pass it, or if someone knows a way to adjust the ajax request to perhaps get the right selector, that would be ideal.
Sorry if this info is readily available somewhere...there are quiet a few resources around related to Drupal and Ajax but I haven't been able to find examples of exactly what I'm doing here, the circumstances always seem to differ enough that I can't use them.
Thanks for any help.

implementing angular-ui-router 'otherwise' state

If a user types myURL/ or myURL/#/ or even myURL/#/foo they get to my index page.
But if they type myURL/foo, they get a 404. This is terrible. They should instead be redirected to /.
I am trying to implement this and am not having a lot of luck.
(function() {
'use strict';
angular
.module('myApp')
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('index', {
name: 'index',
url: '/',
templateUrl: 'js/views/page1.html',
controllerAs: 'page1Controller',
data: { pageTitle: 'Main' }
})
.state('page2', {
name:'page2',
url: '/page2/:id',
templateUrl: 'js/views/page2.html',
controllerAs: 'page2Controller',
data: { pageTitle: 'page2' }
})
$urlRouterProvider.otherwise('/');
}]);
})();
I have looked at dozens of articles, and nowhere do I seem to be able find this simple case handled.
On the official docs it is mentioned that you can pass $injector and $location to the function otherwise.
Their example looks like this:
app.config(function($urlRouterProvider){
// if the path doesn't match any of the urls you configured
// otherwise will take care of routing the user to the specified url
$urlRouterProvider.otherwise('/index');
// Example of using function rule as param
$urlRouterProvider.otherwise(function($injector, $location){
... some advanced code...
});
})
What you can do to achieve your goal is to create a state, and whenever something it's not matched and enters otherwise fct, send it to that state.
$urlRouterProvider.otherwise(function($injector, $location){
$injector.get('$state').go('404');
});
I have not tested this but should work.

Page error on page refresh Ember app

I have an ember app that I am developing and everything is working fine except when I refresh the page whilst on a nested route eg. /projects/1 I receive the following error:
Assertion Failed: You may not passundefinedas id to the store's find method
I am fairly sure this is to do with the way I have setup my routing but can't seem to reorganise them to fix it.
They look like so:
App.Router.reopen({
location: 'auto',
rootURL: '/'
});
App.Router.map(function() {
this.route('projects', { path: '/'});
this.route('project', { path: '/projects/:id' });
});
Any help would be awesome! Thanks.
My first suggestion for you would be to change :id segment do :project_id - it's how they define it in documentation. So your router code looks like:
App.Router.map(function() {
this.route('projects', { path: '/'});
this.route('project', { path: '/projects/:project_id' });
});
If this doesn't help, create:
App.ProjectRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('project', params.project_id);
},
});
If you still get error with passing undefined to store.find try to console.log(params) and see if project_id is defined there and matches value from URL.

Running into Error while waiting for Protractor to sync with the page with basic protractor test

describe('my homepage', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
// ptor.ignoreSynchronization = true;
ptor.get('http://localhost/myApp/home.html');
// ptor.sleep(5000);
})
describe('login', function(){
var email = element.all(protractor.By.id('email'))
, pass = ptor.findElement(protractor.By.id('password'))
, loginBtn = ptor.findElement(protractor.By.css('#login button'))
;
it('should input and login', function(){
// email.then(function(obj){
// console.log('email', obj)
// })
email.sendKeys('josephine#hotmail.com');
pass.sendKeys('shakalakabam');
loginBtn.click();
})
})
});
the above code returns
Error: Error while waiting for Protractor to sync with the page: {}
and I have no idea why this is, ptor load the page correctly, it seem to be the selection of the elements that fails.
TO SSHMSH:
Thanks, your almost right, and gave me the right philosophy, so the key is to ptor.sleep(3000) to have each page wait til ptor is in sync with the project.
I got the same error message (Angular 1.2.13). My tests were kicked off too early and Protractor didn't seem to wait for Angular to load.
It appeared that I had misconfigured the protractor config file. When the ng-app directive is not defined on the BODY-element, but on a descendant, you have to adjust the rootElement property in your protractor config file to the selector that defines your angular root element, for example:
// protractor-conf.js
rootElement: '.my-app',
when your HTML is:
<div ng-app="myApp" class="my-app">
I'm using ChromeDriver and the above error usually occurs for the first test. I've managed to get around it like this:
ptor.ignoreSynchronization = true;
ptor.get(targetUrl);
ptor.wait(
function() {
return ptor.driver.getCurrentUrl().then(
function(url) {
return targetUrl == url;
});
}, 2000, 'It\'s taking too long to load ' + targetUrl + '!'
);
Essentially you are waiting for the current URL of the browser to become what you've asked for and allow 2s for this to happen.
You probably want to switch the ignoreSynchronization = false afterwards, possibly wrapping it in a ptor.wait(...). Just wondering, would uncommenting the ptor.sleep(5000); not help?
EDIT:
After some experience with Promise/Deferred I've realised the correct way of doing this would be:
loginBtn.click().then(function () {
ptor.getCurrentUrl(targetUrl).then(function (newURL){
expect(newURL).toBe(whatItShouldBe);
});
});
Please note that if you are changing the URL (that is, moving away from the current AngularJS activated page to another, implying the AngularJS library needs to reload and init) than, at least in my experience, there's no way of avoiding the ptor.sleep(...) call. The above will only work if you are staying on the same Angular page, but changing the part of URL after the hashtag.
In my case, I encountered the error with the following code:
describe("application", function() {
it("should set the title", function() {
browser.getTitle().then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
Fixed it by doing this:
describe("application", function() {
it("should set the title", function() {
browser.get("#/home").then(function() {
return browser.getTitle();
}).then(function(title) {
expect(title).toEqual("Welcome");
});
});
});
In other words, I was forgetting to navigate to the page I wanted to test, so Protractor was having trouble finding Angular. D'oh!
The rootElement param of the exports.config object defined in your protractor configuration file must match the element containing your ng-app directive. This doesn't have to be uniquely identifying the element -- 'div' suffices if the directive is in a div, as in my case.
From referenceConf.js:
// Selector for the element housing the angular app - this defaults to
// body, but is necessary if ng-app is on a descendant of <body>
rootElement: 'div',
I got started with Protractor by watching the otherwise excellent egghead.io lecture, where he uses a condensed exports.config. Since rootElement defaults to body, there is no hint as to what is wrong with your configuration if you don't start with a copy of the provided reference configuration, and even then the
Error while waiting for Protractor to sync with the page: {}
message doesn't give much of a clue.
I had to switch from doing this:
describe('navigation', function(){
browser.get('');
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
to doing this:
describe('navigation', function(){
beforeEach(function(){
browser.get('');
});
var navbar = element(by.css('#nav'));
it('should have a link to home in the navbar', function(){
//validate
});
it('should have a link to search in the navbar', function(){
//validate
});
});
the key diff being:
beforeEach(function(){
browser.get('');
});
hope this may help someone.
I was getting this error:
Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
The solution was to call page.navigateTo() before page.getTitle().
Before:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
After:
import { AppPage } from './app.po';
describe('App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
page.navigateTo();
});
it('should have the correct title', () => {
expect(page.getTitle()).toEqual('...');
})
});
If you are using
browser.restart()
in your spec some times, it throws the same error.
Try to use
await browser.restart()

Why doesn't javascript execute in .php file loaded with Ext.Ajax.Request?

I want to load .php files via ajax which execute ExtJS script as they load, which in turn modifies the existing ExtJS objects already present in the DOM.
However, I can't even get Javascript to execute from a page that is being loaded via Ext.Ajax.request. And no errors are showing up in the Firebug Net panel. The PHP code gets executed, but not the Javascript. When I call the page being loaded by itself in the browser, it executes the Javascript fine.
How can I get Javascript to execute in pages loaded with Ext.Ajax.request?
Ext.onReady(function(){
var menuItemStart = new Ext.Panel({
id: 'panelStart',
title: 'Start',
html: 'This is the start menu item.',
cls:'menuItem'
});
var menuItemApplication = new Ext.Panel({
id: 'panelApplication',
title: 'Application',
html: 'this is the application page',
cls:'menuItem'
});
var regionMenu = new Ext.Panel({
region:'west',
split:true,
width: 210,
layout:'accordion',
layoutConfig:{
animate:true
},
items: [ menuItemStart, menuItemApplication ]
});
var regionContent = new Ext.Panel({
id: 'contentArea',
region: 'center',
padding:'10',
autoScroll: true,
html: 'this is the content'
});
new Ext.Viewport({
layout: 'border',
items: [ regionMenu, regionContent ]
});
menuItemStart.header.on('click', function() {
Ext.Ajax.request({
url: 'content/view_start.php',
success: function(objServerResponse) {
regionContent.update(objServerResponse.responseText);
}
});
});
menuItemApplication.header.on('click', function() {
Ext.Ajax.request({
url: 'content/view_application.php',
success: function(objServerResponse) {
regionContent.update(objServerResponse.responseText);
}
});
});
});
the file that is being loaded via Ajax:
<script type="text/javascript">
window.onload=function() {
alert('from application view'); //is not executed
}
//Ext.onReady(function(){
// alert('from application view extjs'); //is not executed
//}
</script>
<?php
echo 'this is the application view at ' . date('Y-m-d h:i:s');
?>
When you get the ajax response the onload event on the window has been already fired so the function won't be executed because the onload event won't be fired again. Try only with the alert:
<script type="text/javascript">
alert('from application view');
</script>
<?php
echo 'this is the application view at ' . date('Y-m-d h:i:s');
?>
UPDATE
Browsers don't execute injected scripts in that way so you can try with something like:
var scripts, scriptsFinder=/<script[^>]*>([\s\S]+)<\/script>/gi;
while(scripts=scriptsFinder.exec(responseText))
{
eval(scripts[1]);
}
Have you tried passing true for the second param to Panel.load (which happens to be the loadScripts option)?
regionContent.update(objServerResponse.responseText, true);
Normally, when you call update with ext you just do
update(string,true) and it will execute scripts contained within the string. However, ext core seems to lack this functionality, but there is no documentation for the update method (I had to search the actual code to confirm this.)
If you are using some regular EXT (like ext-all) you can simply add
regionContent.update(objServerResponse.responseText,true);
like this and it should eval the scripts. No dice for me, though - ext-all is too slow but I need eval functionality. I may have to hack EXT.

Resources