Magento 2 Knockout XML->PHTML->JS->HTML - javascript

I want to display static block in html login popup during checkout, but there is a problem.
This is the html template which is called from js, this js is called from phtml, and this phtml template called from xml
layout.
( xml -> phtml -> js -> html)
So the question is how to send custom content block from the phtml or xml throught js to html template
vendor/magento/module-catalog/view/frontend/layout/default.xml
This file is calling pthml template with
<block class="Magento\Customer\Block\Account\AuthenticationPopup" name="authentication-popup" as="authentication-popup" template="Magento_Customer::account/authentication-popup.phtml">
vendor/magento/module-customer/view/frontend/templates/account/authentication-popup.phtml
This file is calling js layout with code:
<script type="text/x-magento-init">
{
"#authenticationPopup": {
"Magento_Ui/js/core/app": <?= /* #noEscape */ $block->getJsLayout() ?>
}
}
</script>
vendor/magento/module-customer/view/frontend/web/js/view/authentication-popup.js
this file is called the last html template where should be a static block from admin panel, with code:
define([
'jquery',
'ko',
// ......... //
], function ($, ko, /* ... ... ... .... ... */) {
'use strict';
return Component.extend({
registerUrl: window.authenticationPopup.customerRegisterUrl,
forgotPasswordUrl: window.authenticationPopup.customerForgotPasswordUrl,
autocomplete: window.authenticationPopup.autocomplete,
modalWindow: null,
isLoading: ko.observable(false),
defaults: {
template: 'Magento_Customer/authentication-popup'
},
});
});
this is how i get this block in php
<?php echo $this->getLayout()->createBlock('Magento\Cms\Block\Block')->setBlockId('reset_password_notice')->toHtml(); ?>
I tried to paste it to phtml, it doesn't work !!!

The problem is solved by myself.
So for the first step i started looking for data provider which helps to send data from pthml throught js to html in vendor/module-customer/
There i found file vendor/module-customer/Model/Checkout/ConfigProvider.php. That was exectly what i need.
Following this link i create:
1) app/code/Theme/Customer/etc/frontend/di.xml with code:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Customer\Controller\Account\CreatePost"
type="Theme_Name\Customer\Controller\Account\CreatePost" />
<type name="Magento\Checkout\Model\CompositeConfigProvider">
<arguments>
<argument name="configProviders" xsi:type="array">
<item name="cms_block_config_provider" xsi:type="object">Theme_Name\Customer\Model\Checkout\ConfigProvider</item>
</argument>
</arguments>
</type>
</config>
2) The next step was to create a class which is called in item tag: Theme_Name/Customer/Model/Checkout/ConfigProvider.php
with code that extends
vendor/module-customer/Model/Checkout/ConfigProvider.php
Note! They both implement the same ConfigProviderInterface. So in new ConifgProvider.php we use the same interface to extend data-provider correctly
<?php
namespace Theme_Name\Customer\Model\Checkout;
use Magento\Checkout\Model\ConfigProviderInterface;
use Magento\Framework\View\LayoutInterface;
class ConfigProvider implements ConfigProviderInterface
{
/** #var LayoutInterface */
protected $_layout;
public function __construct(LayoutInterface $layout)
{
$this->_layout = $layout;
}
public function getConfig()
{
$cmsBlockId = 'block_ID'; // id of cms block to use
return [
'cms_block_message' => $this->_layout->createBlock('Magento\Cms\Block\Block')->setBlockId($cmsBlockId)->toHtml()
];
}
}
Good. Provider is configured.
3)The last one was need to override frontend html KO template:
app/design/frontend/theme_name/Magento_Customer/web/template/authentication-popup.html
Write the next:
<div data-bind="html: window.checkoutConfig.cms_block_message"></div>

You need to put this code into your phtml file.
<?php echo $this->getLayout()->createBlock('Magento\Cms\Block\Block')->setBlockId('reset_password_notice')->toHtml(); ?>
And now it show what you write into this block.

Related

Is it possible to use laravel's core methods from a Vue.js component?

I'm relatively comfortable with Laravel 5 but a rather green when it comes to Vue.js and I was wandering if it is possible to call Laravel's methods which are available from blade in a Vue.js component.
For example I want to make a Vue.js component for my navbar like so:
<template>
<nav class="navbar navbar-expand-sm navbar-dark bg-info mb-2">
<div class="container">
Go Home
</div>
</nav>
</template>
<script>
export default {
name: "NavBar"
}
</script>
home is a named route in my wep.php file
Route::get('/', 'SiteController#index')
->name('home');
and I added my Vue component like so:
Vue.component('navbar', require('./components/NavBar'));
in my app.js file
How can I get something like this done? In this example I only use the route() method to insert a src tag but I am also interested in doing something analogous to this from a Vue.js component
#if (Route::has('login'))
<div class="top-right links">
#auth
Home
#else
Login
Register
#endauth
</div>
#endif
This is how I would do it from my *.blade.php file but how can I do this type of logic using Vue components? I'm using Laravel 5.7 by the way.
I have been building Vue Apps with Laravel without any blade templates other than an index to present the base HTML meta data required for the SPA to run.
Beyond that, My Apps typically require an authenticated user throughout and so on user authenticated login request I return with the user success response all the settings and permissions relevant to that user.
To get started and populate Vue with all available web routes, add the following to routes/web.php
Route::get('/js/routes.js', function() {
// White list namespaces that can be exposed to Vue.
$allow = ['App\Http\Controllers'];
// uncomment below to enable cache
// $routes = \Illuminate\Support\Facades\Cache::rememberForever('routes.js', function () {
$routes = [];
foreach(Route::getRoutes()->getIterator() AS $route)
{
if(!array_key_exists('namespace',$route->action))
continue;
if(!is_array($route->action['middleware']))
continue;
if(in_array($route->action['namespace'],$allow,true))
continue;
$name = $route->getName();
if(empty($name))
continue;
$routes[$name] = $route->uri;
};
return $routes;
// });
header('Content-Type: text/javascript');
echo 'let routes = ' . json_encode($routes) . ';';
echo 'window.route = function() { let args = Array.prototype.slice.call(arguments); let name = args.shift();if(routes[name] === undefined) { console.error(\'Unknown route \', name); } else { return \'/\' + routes[name].split(\'/\').map(s => s[0] == \'{\' ? args.shift() : s).join(\'/\');}};';
exit();
})->name('assets.routes');
Add to your blade layout file:
<link rel="preload" href="/js/routes.js" as="script">
To then make those routes available in Vue components you can bind to
Vue using the following example after import Vue from 'Vue':
Vue.prototype.$getRoute = window.route;
Vue['getRoute'] = window.route;
You can then in Vue methods use this.getRoute('laravels.route.name', [ id: user.id ]), this works much like the route function in Laravel with url variables passed as the second parameter. In a router link component or click event it would work like so:
<router-link :to="$getRoute('logout')">Logout</router-link>
<button #click="$getRoute('logout')">Logout</button>
The value passed in getRoute is the name of the route in Laravel when you use Route::get(...)->name('my.custom.route.name') in web.php
Use command php artisan route:list to list all route names of Resources you have created that you can use for $getRoute() in Vue.
On a side note as an extra added bonus, you can return custom JSON data for a newly authenticated user by editing Auth/LoginController.php and adding the following method:
public function authenticated(Request $request, $user)
{
return response()->json([
'redirect' => session()->pull('url.intended')
]);
}
You can expand on this and return many addition credentials.
For starters, I am passing back an intended redirect path that actually belongs to Vue router. The Vue application has already pushed the user to the login screen but Laravel also has a snapshot in session of where the user was going before this.$router.push('login') occurred.
Vue.prototype.$Laravel = window.Laravel;
Vue['Laravel'] = window.Laravel;
logMeIn() {
this.$root.isAuthenticating = true;
axios.post(this.$getRoute('login.send'),this.input).then(response => {
this.Laravel.myArray = response.data.myArray;
this.$router.push(response.data.redirect);
}).catch(error => {
this.$root.isAuthenticating = false;
ErrorHandler(this,error);
});
},

How to add js-file to wordpress widget?

I try to add test.js file to my widget in front-end part. But it's nothing happens. The file contain alert. What's wrong with my code?
class PPNDR_new_widget extends WP_Widget{
function __construct(){
$args = array(
'name' => 'new widget',
'description' => 'this is my first widget',
'classname' => 'ppndr-new-widget'
);
parent::__construct('my_first', '', $args);
}
//front-end display of widget
public function widget($args, $instance) {
add_action('wp_enqueue_scripts', 'CounDounJS_enqueue_js');
function CounDounJS_enqueue_js() {
wp_enqueue_script('resize', plugins_url('/assets/js/test.js', __FILE__));
}
return;
}
}
add_action( 'widgets_init', function() {
register_widget('PPNDR_new_widget');
} );
I had a similar problem and google led me here -- What ended up working for me was eliminating the add_action('wp_enqueue_scripts.. and simply enqueuing my scripts and styles in the widget function.
for example:
//front-end display of widget
public function widget($args, $instance) {
wp_enqueue_script('resize', plugins_url('/assets/js/test.js', __FILE__));
}
There is the problem of not being able to dequeue the script, but I suspect the need for this would be slim.
Hope this helps.

MVC4 AngularJS Removing # From Urls In Areas Applications

Before Posting This Question I Searched And Tried Out Many Examples As Well.
For My Problem There Was A Similar POST But It is Not Working Too.
Here Is The Structure Of MY Whole APPLICATION:
Here Is The Structure Of ADMIN AREA :
Project Has Two Applications Now One For Client And Another For Admin :
Admin Is Managed Through Areas
I Have Used Angular 1.3.5 And It is Used Inside The Areas.
The Landing Page Is Index Page of Home Controller Path :- Areas/Admin/Views/Home/Index.cshtml
Layout is _Layout Path : Areas/Admin/Views/Shared/_Layout.cshtml
RouteConfig.cs File Of Inside App_Start Folder :
namespace StockManagment
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces : new []{"StockManagment.Controllers"}
);
}
}
}
AdminAreaRegistration.cs
namespace StockManagment.Areas.Admin
{
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new [] {"StockManagment.Areas.Admin.Controllers"}
);
}
}
}
HTML5MODE For Removing # Form URL
myApp.config(["$locationProvider", function ($locationProvider) {
$locationProvider.html5Mode(true);
}]);
Base Tag In _Layout Page:
<base href="/">
Web.config Code Of Admin Areas
<rewrite>
<rules>
<rule name="Main Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
When I Try To Access Admin Side Using This URL:
http://localhost:8917/Admin/Home/Index
It Loads The Website At : localhost:8917/
And If I Refresh The Page It Loads The Original Content i.e The Client Side Home Page.
Where Am I Going Wrong In This ?
Thanks In Advance...
Edit 1 :
i removed the rewrite code in web.config i used it because i saw it in another post
before using
myApp.config(["$locationProvider", function ($locationProvider) {
$locationProvider.html5Mode(true);
}]);
and
<base href="#Request.Url.AbsolutePath" />
to access admin side home page the url was :- http://localhost:8917/Admin/Home/Index#/
after adding the above code it is :- http://localhost:8917/Admin/Home/
accessing categories page
categories before code :- http://localhost:8917/Admin/Home/Index#/Categories
categories after code :- http://localhost:8917/Admin/Home/Categories
when i refresh it gives error
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Admin/Home/Categories
I'm not sure why you're using the rewrite rule, I can imagine this would interfere with the routing you are trying to do with MVC. I would suggest removing the url rewrite rule from the web.config.
If you want a collection of routes to map to the Index action in the admin area (so that various urls go to your angular SPA), use a wildcard MVC route.
e.g.
// Route override to work with Angularjs and HTML5 routing
context.MapRoute(
name: "Application1Override",
url: "Application1/{*.}",
defaults: new { controller = "Application1", action = "Index" }
);
In your initial problem you had
<base href="/" />
which tells angular that the basic routing is "/", so it will change the href to the base ("/") when you reload the page, MVC will take you to "/" which is your root action.
When you are changing to -
<base href="#Request.Url.AbsolutePath" />
angular will know the base of this page is your current {action}/{controller}
Now you have another problem with the routing:
namespace StockManagment.Areas.Admin
{
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}", // your current url= /Admin/Home/Categories -- which means id = Categories
new { action = "Index", id = UrlParameter.Optional },
new [] {"StockManagment.Areas.Admin.Controllers"}
);
}
}}
If you don't need ID you can use this code:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{*angular}",
new { action = "Index",
new [] {"StockManagment.Areas.Admin.Controllers"}
);
}

How to use JSX file in Yii2 Asset Bundle

I have Yii2 project setup and I've decided to use Facebook's JavaScript framework React.js which provided a convenient way to declare HTML templates inside JavaScript code, called JSX.
My JavaScript looks as following:
(function () {
'use strict';
MyBlock = React.createClass({
getInitialState: function () {
return {data: []};
},
componentDidMount: function () {
$.ajax({
url: '/api/',
dataType: 'json',
success: function (data) {
this.setState({ data: data });
}.bind(this)
});
},
render: function () {
<div class="block">
{this.props.variable}
</div>
}
});
React.render(
<ProfileQuestion />,
document.getElementById('profile_question_wrapper')
);
}());
AssetBundle helps me to include required libraries in my view, so I added React.JS and JSX file from CDN into my AssetBundle:
class MyAsset extends AssetBundle
{
public $basePath = '#webroot';
public $baseUrl = '#web';
public $css = [
];
public $js = [
'//fb.me/react-0.13.1.min.js',
'//fb.me/JSXTransformer-0.13.1.js',
'js/app.jsx',
];
public $depends = [
'app\assets\AppAsset'
];
}
However, when it adds the script in the bottom of my page it shows it as
<script src="/js/app.jsx"></script>
and doesn't mark the type of the script as text/jsx therefore JSX Transform library doesn't recognize JSX syntax and the .jsx file interprets as simple JavaScript, throwing syntax error message on JSX style.
SyntaxError: Unexpected token '<'
Is there any way to specify in AssetBundle that .jsx file should have type of text/jsx in script element, when rendered?
You will need to use two assets for doing this
You can pass attributes to the script tag for js files. However it will be passed to all the files in the JS array, therefore you will need to have two asset classes. Like this
class MyAsset extends AssetBundle
{
public $basePath = '#webroot';
public $baseUrl = '#web';
public $css = [
];
public $js = [
'//fb.me/react-0.13.1.min.js',
'//fb.me/JSXTransformer-0.13.1.js',
];
public $depends = [
'app\assets\AppAsset'
];
}
second class
class JSXAsset extends AssetBundle
{
public $basePath = '#webroot';
public $baseUrl = '#web';
public $js = [
'js/app.jsx',
];
public jsOptions = ['type'=>'text/jsx'];
public $depends = [
'app\assets\MyAsset'
];
}
And include the second class in your Views. The jsOptions calls the View::registerjsFile function which in turn calls the jsFile helper function in Html class Here. The options if not specially defined will be passed as the script tag attribute

Use handlebars template helpers in hapijs

I'm using jquery's typeahead in hapijs using a handlebar view. It works with static source data. I want to use data from a postgresql-database but I don't know how to include the pg-bluebird npm module.
I've done a similar jquery typeahead in meteorjs where I get the source from a mongodb collection using the template name and then call a template event from javascript like:
html:
<template name="participantItem">
... html form input etc. ...
</template>
javascript:
Template.participantItem.events({
'click .edit': function (e) {
e.preventDefault();
$("#club").autocomplete({
/* query remote source */
});
}
});
When I try to add <template name="foo"> to my html file in hapijs it loads the content but the page is not rendered except for the header and footer.
I like the way meteorjs is using helper templates and events and would like to do it the same way in hapijs.
I ended up including javascript to test typeahead with static data in the html file but it does not appear to be very elegant.
<div class="container">
<div class="row">
... content using some json data ...
</div>
</div>
<script type="text/javascript">
$(function () {
$('#states').autocomplete({
delay: 500,
minLength: 2,
source: states
});
}
</script>
I call the view like this from index.js:
var Pgb = require('pg-bluebird');
var pg = new Pgb();
var db = "postgresq://claus#localhost/karusellrenn";
exports.register = function (plugin, options, next) {
plugin.route([
{
method: 'GET',
path: '/participants',
handler: function (request, reply) {
.then(function (result) {
cnn.done();
reply.view('participants', { p: result.rows});
.catch(function (error) {
console.log(error);
});
}
}
]);
next();
};
Can I use handlebars in hapijs the same way I use handlebars in meteorjs like <template name="foo"></template> and then make use of a javascript helper like Template.foo.events({});?
And if not, can I use pg-bluebird included in index.js in my handlebars view inside in the handlebars view?

Categories

Resources